@probelabs/probe 0.6.0-rc104 → 0.6.0-rc105
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/build/agent/index.js +63 -23
- package/build/agent/mcp/xmlBridge.js +34 -6
- package/build/agent/tools.js +9 -75
- package/build/agent/xmlParsingUtils.js +118 -0
- package/cjs/agent/ProbeAgent.cjs +62 -22
- package/cjs/index.cjs +62 -22
- package/package.json +1 -1
- package/src/agent/mcp/xmlBridge.js +34 -6
- package/src/agent/tools.js +9 -75
- package/src/agent/xmlParsingUtils.js +118 -0
package/build/agent/index.js
CHANGED
|
@@ -30083,20 +30083,15 @@ var init_index = __esm({
|
|
|
30083
30083
|
}
|
|
30084
30084
|
});
|
|
30085
30085
|
|
|
30086
|
-
// src/agent/
|
|
30087
|
-
|
|
30088
|
-
|
|
30089
|
-
return {
|
|
30090
|
-
searchTool: searchTool(configOptions),
|
|
30091
|
-
queryTool: queryTool(configOptions),
|
|
30092
|
-
extractTool: extractTool(configOptions),
|
|
30093
|
-
delegateTool: delegateTool(configOptions)
|
|
30094
|
-
};
|
|
30086
|
+
// src/agent/xmlParsingUtils.js
|
|
30087
|
+
function removeThinkingTags(xmlString) {
|
|
30088
|
+
return xmlString.replace(/<thinking>[\s\S]*?<\/thinking>/g, "").trim();
|
|
30095
30089
|
}
|
|
30096
|
-
function
|
|
30090
|
+
function extractThinkingContent(xmlString) {
|
|
30097
30091
|
const thinkingMatch = xmlString.match(/<thinking>([\s\S]*?)<\/thinking>/);
|
|
30098
|
-
|
|
30099
|
-
|
|
30092
|
+
return thinkingMatch ? thinkingMatch[1].trim() : null;
|
|
30093
|
+
}
|
|
30094
|
+
function checkAttemptCompleteRecovery(cleanedXmlString, validTools = []) {
|
|
30100
30095
|
const attemptCompletePatterns = [
|
|
30101
30096
|
// Standard shorthand with optional whitespace
|
|
30102
30097
|
/^<attempt_complete>\s*$/,
|
|
@@ -30126,12 +30121,7 @@ function parseXmlToolCallWithThinking(xmlString, validTools) {
|
|
|
30126
30121
|
params: { result: "__PREVIOUS_RESPONSE__" }
|
|
30127
30122
|
};
|
|
30128
30123
|
}
|
|
30129
|
-
|
|
30130
|
-
if (process.env.DEBUG === "1" && thinkingContent) {
|
|
30131
|
-
console.log(`[DEBUG] AI Thinking Process:
|
|
30132
|
-
${thinkingContent}`);
|
|
30133
|
-
}
|
|
30134
|
-
return parsedTool;
|
|
30124
|
+
return null;
|
|
30135
30125
|
}
|
|
30136
30126
|
function hasOtherToolTags(xmlString, validTools = []) {
|
|
30137
30127
|
const defaultTools = ["search", "query", "extract", "listFiles", "searchFiles", "implement", "attempt_completion"];
|
|
@@ -30143,11 +30133,49 @@ function hasOtherToolTags(xmlString, validTools = []) {
|
|
|
30143
30133
|
}
|
|
30144
30134
|
return false;
|
|
30145
30135
|
}
|
|
30136
|
+
function processXmlWithThinkingAndRecovery(xmlString, validTools = []) {
|
|
30137
|
+
const thinkingContent = extractThinkingContent(xmlString);
|
|
30138
|
+
const cleanedXmlString = removeThinkingTags(xmlString);
|
|
30139
|
+
const recoveryResult = checkAttemptCompleteRecovery(cleanedXmlString, validTools);
|
|
30140
|
+
if (process.env.DEBUG === "1" && thinkingContent) {
|
|
30141
|
+
console.log(`[DEBUG] AI Thinking Process:
|
|
30142
|
+
${thinkingContent}`);
|
|
30143
|
+
}
|
|
30144
|
+
return {
|
|
30145
|
+
cleanedXmlString,
|
|
30146
|
+
thinkingContent,
|
|
30147
|
+
recoveryResult
|
|
30148
|
+
};
|
|
30149
|
+
}
|
|
30150
|
+
var init_xmlParsingUtils = __esm({
|
|
30151
|
+
"src/agent/xmlParsingUtils.js"() {
|
|
30152
|
+
"use strict";
|
|
30153
|
+
}
|
|
30154
|
+
});
|
|
30155
|
+
|
|
30156
|
+
// src/agent/tools.js
|
|
30157
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
30158
|
+
function createTools(configOptions) {
|
|
30159
|
+
return {
|
|
30160
|
+
searchTool: searchTool(configOptions),
|
|
30161
|
+
queryTool: queryTool(configOptions),
|
|
30162
|
+
extractTool: extractTool(configOptions),
|
|
30163
|
+
delegateTool: delegateTool(configOptions)
|
|
30164
|
+
};
|
|
30165
|
+
}
|
|
30166
|
+
function parseXmlToolCallWithThinking(xmlString, validTools) {
|
|
30167
|
+
const { cleanedXmlString, recoveryResult } = processXmlWithThinkingAndRecovery(xmlString, validTools);
|
|
30168
|
+
if (recoveryResult) {
|
|
30169
|
+
return recoveryResult;
|
|
30170
|
+
}
|
|
30171
|
+
return parseXmlToolCall(cleanedXmlString, validTools);
|
|
30172
|
+
}
|
|
30146
30173
|
var implementToolDefinition, listFilesToolDefinition, searchFilesToolDefinition;
|
|
30147
30174
|
var init_tools2 = __esm({
|
|
30148
30175
|
"src/agent/tools.js"() {
|
|
30149
30176
|
"use strict";
|
|
30150
30177
|
init_index();
|
|
30178
|
+
init_xmlParsingUtils();
|
|
30151
30179
|
implementToolDefinition = `
|
|
30152
30180
|
## implement
|
|
30153
30181
|
Description: Implement a given task. Can modify files. Can be used ONLY if task explicitly stated that something requires modification or implementation.
|
|
@@ -32151,11 +32179,9 @@ function parseXmlMcpToolCall(xmlString, mcpToolNames = []) {
|
|
|
32151
32179
|
return null;
|
|
32152
32180
|
}
|
|
32153
32181
|
function parseHybridXmlToolCall(xmlString, nativeTools = [], mcpBridge = null) {
|
|
32154
|
-
|
|
32155
|
-
|
|
32156
|
-
|
|
32157
|
-
return { ...nativeResult, type: "native" };
|
|
32158
|
-
}
|
|
32182
|
+
const nativeResult = parseNativeXmlToolWithThinking(xmlString, nativeTools);
|
|
32183
|
+
if (nativeResult) {
|
|
32184
|
+
return { ...nativeResult, type: "native" };
|
|
32159
32185
|
}
|
|
32160
32186
|
if (mcpBridge) {
|
|
32161
32187
|
const mcpResult = parseXmlMcpToolCall(xmlString, mcpBridge.getToolNames());
|
|
@@ -32165,6 +32191,19 @@ function parseHybridXmlToolCall(xmlString, nativeTools = [], mcpBridge = null) {
|
|
|
32165
32191
|
}
|
|
32166
32192
|
return null;
|
|
32167
32193
|
}
|
|
32194
|
+
function parseNativeXmlToolWithThinking(xmlString, validTools) {
|
|
32195
|
+
const { cleanedXmlString, recoveryResult } = processXmlWithThinkingAndRecovery(xmlString, validTools);
|
|
32196
|
+
if (recoveryResult) {
|
|
32197
|
+
return recoveryResult;
|
|
32198
|
+
}
|
|
32199
|
+
for (const toolName of validTools) {
|
|
32200
|
+
const result = parseNativeXmlTool(cleanedXmlString, toolName);
|
|
32201
|
+
if (result) {
|
|
32202
|
+
return result;
|
|
32203
|
+
}
|
|
32204
|
+
}
|
|
32205
|
+
return null;
|
|
32206
|
+
}
|
|
32168
32207
|
function parseNativeXmlTool(xmlString, toolName) {
|
|
32169
32208
|
const openTag = `<${toolName}>`;
|
|
32170
32209
|
const closeTag = `</${toolName}>`;
|
|
@@ -32194,6 +32233,7 @@ var init_xmlBridge = __esm({
|
|
|
32194
32233
|
"use strict";
|
|
32195
32234
|
init_client2();
|
|
32196
32235
|
init_config();
|
|
32236
|
+
init_xmlParsingUtils();
|
|
32197
32237
|
MCPXmlBridge = class {
|
|
32198
32238
|
constructor(options = {}) {
|
|
32199
32239
|
this.debug = options.debug || false;
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import { MCPClientManager } from './client.js';
|
|
7
7
|
import { loadMCPConfiguration } from './config.js';
|
|
8
|
+
import { processXmlWithThinkingAndRecovery } from '../xmlParsingUtils.js';
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* Convert MCP tool to XML definition format
|
|
@@ -254,18 +255,18 @@ export class MCPXmlBridge {
|
|
|
254
255
|
|
|
255
256
|
/**
|
|
256
257
|
* Enhanced XML parser that handles both native and MCP tools
|
|
258
|
+
* Uses the exact same logic as CLI/SDK mode to ensure consistency
|
|
257
259
|
* @param {string} xmlString - XML string to parse
|
|
258
260
|
* @param {Array<string>} nativeTools - List of native tool names
|
|
259
261
|
* @param {MCPXmlBridge} mcpBridge - MCP bridge instance
|
|
260
262
|
* @returns {Object|null} Parsed tool call
|
|
261
263
|
*/
|
|
262
264
|
export function parseHybridXmlToolCall(xmlString, nativeTools = [], mcpBridge = null) {
|
|
263
|
-
// First try native tools with
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
}
|
|
265
|
+
// First try native tools with the same logic as CLI/SDK mode
|
|
266
|
+
// This includes thinking tag removal and attempt_complete recovery logic
|
|
267
|
+
const nativeResult = parseNativeXmlToolWithThinking(xmlString, nativeTools);
|
|
268
|
+
if (nativeResult) {
|
|
269
|
+
return { ...nativeResult, type: 'native' };
|
|
269
270
|
}
|
|
270
271
|
|
|
271
272
|
// Then try MCP tools if bridge is available
|
|
@@ -279,6 +280,33 @@ export function parseHybridXmlToolCall(xmlString, nativeTools = [], mcpBridge =
|
|
|
279
280
|
return null;
|
|
280
281
|
}
|
|
281
282
|
|
|
283
|
+
/**
|
|
284
|
+
* Parse native XML tools using the same logic as CLI/SDK mode
|
|
285
|
+
* Now uses shared utilities instead of duplicating code
|
|
286
|
+
* @param {string} xmlString - XML string to parse
|
|
287
|
+
* @param {Array<string>} validTools - List of valid tool names
|
|
288
|
+
* @returns {Object|null} Parsed tool call
|
|
289
|
+
*/
|
|
290
|
+
function parseNativeXmlToolWithThinking(xmlString, validTools) {
|
|
291
|
+
// Use the shared processing logic
|
|
292
|
+
const { cleanedXmlString, recoveryResult } = processXmlWithThinkingAndRecovery(xmlString, validTools);
|
|
293
|
+
|
|
294
|
+
// If recovery found an attempt_complete pattern, return it
|
|
295
|
+
if (recoveryResult) {
|
|
296
|
+
return recoveryResult;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// Use the original parseNativeXmlTool function to parse the cleaned XML string
|
|
300
|
+
for (const toolName of validTools) {
|
|
301
|
+
const result = parseNativeXmlTool(cleanedXmlString, toolName);
|
|
302
|
+
if (result) {
|
|
303
|
+
return result;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
return null;
|
|
308
|
+
}
|
|
309
|
+
|
|
282
310
|
/**
|
|
283
311
|
* Parse native XML tool (existing format)
|
|
284
312
|
* @param {string} xmlString - XML string
|
package/build/agent/tools.js
CHANGED
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
parseXmlToolCall
|
|
19
19
|
} from '../index.js';
|
|
20
20
|
import { randomUUID } from 'crypto';
|
|
21
|
+
import { processXmlWithThinkingAndRecovery } from './xmlParsingUtils.js';
|
|
21
22
|
|
|
22
23
|
// Create configured tool instances
|
|
23
24
|
export function createTools(configOptions) {
|
|
@@ -134,82 +135,15 @@ User: Find all markdown files in the docs directory, but only at the top level.
|
|
|
134
135
|
* @returns {Object|null} - The parsed tool call or null if no valid tool call found
|
|
135
136
|
*/
|
|
136
137
|
export function parseXmlToolCallWithThinking(xmlString, validTools) {
|
|
137
|
-
//
|
|
138
|
-
const
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
// Enhanced recovery logic for attempt_complete shorthand
|
|
145
|
-
// Check for various forms of attempt_complete tags:
|
|
146
|
-
// 1. Perfect shorthand: <attempt_complete>
|
|
147
|
-
// 2. Self-closing: <attempt_complete/>
|
|
148
|
-
// 3. Empty with closing tag: <attempt_complete></attempt_complete>
|
|
149
|
-
// 4. Incomplete: <attempt_complete (missing closing bracket)
|
|
150
|
-
// 5. With trailing content that should be ignored
|
|
151
|
-
const attemptCompletePatterns = [
|
|
152
|
-
// Standard shorthand with optional whitespace
|
|
153
|
-
/^<attempt_complete>\s*$/,
|
|
154
|
-
// Empty with proper closing tag (common case from the logs)
|
|
155
|
-
/^<attempt_complete>\s*<\/attempt_complete>\s*$/,
|
|
156
|
-
// Self-closing variant
|
|
157
|
-
/^<attempt_complete\s*\/>\s*$/,
|
|
158
|
-
// Incomplete opening tag (missing closing bracket)
|
|
159
|
-
/^<attempt_complete\s*$/,
|
|
160
|
-
// With trailing content (extract just the tag part) - must come after empty tag pattern
|
|
161
|
-
/^<attempt_complete>(.*)$/s,
|
|
162
|
-
// Self-closing with trailing content
|
|
163
|
-
/^<attempt_complete\s*\/>(.*)$/s
|
|
164
|
-
];
|
|
165
|
-
|
|
166
|
-
for (const pattern of attemptCompletePatterns) {
|
|
167
|
-
const match = cleanedXmlString.match(pattern);
|
|
168
|
-
if (match) {
|
|
169
|
-
// Convert any form of attempt_complete to the standard format
|
|
170
|
-
return {
|
|
171
|
-
toolName: 'attempt_completion',
|
|
172
|
-
params: { result: '__PREVIOUS_RESPONSE__' }
|
|
173
|
-
};
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
// Additional recovery: check if the string contains attempt_complete anywhere
|
|
178
|
-
// and treat the entire response as a completion signal if no other tool tags are found
|
|
179
|
-
if (cleanedXmlString.includes('<attempt_complete') && !hasOtherToolTags(cleanedXmlString, validTools)) {
|
|
180
|
-
// This handles malformed cases where attempt_complete appears but is broken
|
|
181
|
-
return {
|
|
182
|
-
toolName: 'attempt_completion',
|
|
183
|
-
params: { result: '__PREVIOUS_RESPONSE__' }
|
|
184
|
-
};
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
// Use the original parseXmlToolCall function to parse the cleaned XML string
|
|
188
|
-
const parsedTool = parseXmlToolCall(cleanedXmlString, validTools);
|
|
189
|
-
|
|
190
|
-
// If debugging is enabled, log the thinking content
|
|
191
|
-
if (process.env.DEBUG === '1' && thinkingContent) {
|
|
192
|
-
console.log(`[DEBUG] AI Thinking Process:\n${thinkingContent}`);
|
|
138
|
+
// Use the shared processing logic
|
|
139
|
+
const { cleanedXmlString, recoveryResult } = processXmlWithThinkingAndRecovery(xmlString, validTools);
|
|
140
|
+
|
|
141
|
+
// If recovery found an attempt_complete pattern, return it
|
|
142
|
+
if (recoveryResult) {
|
|
143
|
+
return recoveryResult;
|
|
193
144
|
}
|
|
194
145
|
|
|
195
|
-
|
|
146
|
+
// Otherwise, use the original parseXmlToolCall function to parse the cleaned XML string
|
|
147
|
+
return parseXmlToolCall(cleanedXmlString, validTools);
|
|
196
148
|
}
|
|
197
149
|
|
|
198
|
-
/**
|
|
199
|
-
* Helper function to check if the XML string contains other tool tags
|
|
200
|
-
* @param {string} xmlString - The XML string to check
|
|
201
|
-
* @param {string[]} validTools - List of valid tool names
|
|
202
|
-
* @returns {boolean} - True if other tool tags are found
|
|
203
|
-
*/
|
|
204
|
-
function hasOtherToolTags(xmlString, validTools = []) {
|
|
205
|
-
const defaultTools = ['search', 'query', 'extract', 'listFiles', 'searchFiles', 'implement', 'attempt_completion'];
|
|
206
|
-
const toolsToCheck = validTools.length > 0 ? validTools : defaultTools;
|
|
207
|
-
|
|
208
|
-
// Check for any tool tags other than attempt_complete variants
|
|
209
|
-
for (const tool of toolsToCheck) {
|
|
210
|
-
if (tool !== 'attempt_completion' && xmlString.includes(`<${tool}`)) {
|
|
211
|
-
return true;
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
return false;
|
|
215
|
-
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared XML parsing utilities used by both CLI/SDK and MCP modes
|
|
3
|
+
* This module contains the core logic for thinking tag removal and attempt_complete recovery
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Remove thinking tags and their content from XML string
|
|
8
|
+
* @param {string} xmlString - The XML string to clean
|
|
9
|
+
* @returns {string} - Cleaned XML string without thinking tags
|
|
10
|
+
*/
|
|
11
|
+
export function removeThinkingTags(xmlString) {
|
|
12
|
+
return xmlString.replace(/<thinking>[\s\S]*?<\/thinking>/g, '').trim();
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Extract thinking content for potential logging
|
|
17
|
+
* @param {string} xmlString - The XML string to extract from
|
|
18
|
+
* @returns {string|null} - Thinking content or null if not found
|
|
19
|
+
*/
|
|
20
|
+
export function extractThinkingContent(xmlString) {
|
|
21
|
+
const thinkingMatch = xmlString.match(/<thinking>([\s\S]*?)<\/thinking>/);
|
|
22
|
+
return thinkingMatch ? thinkingMatch[1].trim() : null;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Check for attempt_complete recovery patterns and return standardized result
|
|
27
|
+
* @param {string} cleanedXmlString - XML string with thinking tags already removed
|
|
28
|
+
* @param {Array<string>} validTools - List of valid tool names
|
|
29
|
+
* @returns {Object|null} - Standardized attempt_completion result or null
|
|
30
|
+
*/
|
|
31
|
+
export function checkAttemptCompleteRecovery(cleanedXmlString, validTools = []) {
|
|
32
|
+
// Enhanced recovery logic for attempt_complete shorthand
|
|
33
|
+
const attemptCompletePatterns = [
|
|
34
|
+
// Standard shorthand with optional whitespace
|
|
35
|
+
/^<attempt_complete>\s*$/,
|
|
36
|
+
// Empty with proper closing tag (common case from the logs)
|
|
37
|
+
/^<attempt_complete>\s*<\/attempt_complete>\s*$/,
|
|
38
|
+
// Self-closing variant
|
|
39
|
+
/^<attempt_complete\s*\/>\s*$/,
|
|
40
|
+
// Incomplete opening tag (missing closing bracket)
|
|
41
|
+
/^<attempt_complete\s*$/,
|
|
42
|
+
// With trailing content (extract just the tag part) - must come after empty tag pattern
|
|
43
|
+
/^<attempt_complete>(.*)$/s,
|
|
44
|
+
// Self-closing with trailing content
|
|
45
|
+
/^<attempt_complete\s*\/>(.*)$/s
|
|
46
|
+
];
|
|
47
|
+
|
|
48
|
+
for (const pattern of attemptCompletePatterns) {
|
|
49
|
+
const match = cleanedXmlString.match(pattern);
|
|
50
|
+
if (match) {
|
|
51
|
+
// Convert any form of attempt_complete to the standard format
|
|
52
|
+
return {
|
|
53
|
+
toolName: 'attempt_completion',
|
|
54
|
+
params: { result: '__PREVIOUS_RESPONSE__' }
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Additional recovery: check if the string contains attempt_complete anywhere
|
|
60
|
+
// and treat the entire response as a completion signal if no other tool tags are found
|
|
61
|
+
if (cleanedXmlString.includes('<attempt_complete') && !hasOtherToolTags(cleanedXmlString, validTools)) {
|
|
62
|
+
// This handles malformed cases where attempt_complete appears but is broken
|
|
63
|
+
return {
|
|
64
|
+
toolName: 'attempt_completion',
|
|
65
|
+
params: { result: '__PREVIOUS_RESPONSE__' }
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Helper function to check if the XML string contains other tool tags
|
|
74
|
+
* @param {string} xmlString - The XML string to check
|
|
75
|
+
* @param {string[]} validTools - List of valid tool names
|
|
76
|
+
* @returns {boolean} - True if other tool tags are found
|
|
77
|
+
*/
|
|
78
|
+
function hasOtherToolTags(xmlString, validTools = []) {
|
|
79
|
+
const defaultTools = ['search', 'query', 'extract', 'listFiles', 'searchFiles', 'implement', 'attempt_completion'];
|
|
80
|
+
const toolsToCheck = validTools.length > 0 ? validTools : defaultTools;
|
|
81
|
+
|
|
82
|
+
// Check for any tool tags other than attempt_complete variants
|
|
83
|
+
for (const tool of toolsToCheck) {
|
|
84
|
+
if (tool !== 'attempt_completion' && xmlString.includes(`<${tool}`)) {
|
|
85
|
+
return true;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Apply the full thinking tag removal and attempt_complete recovery logic
|
|
93
|
+
* This replicates the core logic from parseXmlToolCallWithThinking
|
|
94
|
+
* @param {string} xmlString - The XML string to process
|
|
95
|
+
* @param {Array<string>} validTools - List of valid tool names
|
|
96
|
+
* @returns {Object} - Processing result with cleanedXml and potentialRecovery
|
|
97
|
+
*/
|
|
98
|
+
export function processXmlWithThinkingAndRecovery(xmlString, validTools = []) {
|
|
99
|
+
// Extract thinking content if present (for potential logging or analysis)
|
|
100
|
+
const thinkingContent = extractThinkingContent(xmlString);
|
|
101
|
+
|
|
102
|
+
// Remove thinking tags and their content from the XML string
|
|
103
|
+
const cleanedXmlString = removeThinkingTags(xmlString);
|
|
104
|
+
|
|
105
|
+
// Check for attempt_complete recovery patterns
|
|
106
|
+
const recoveryResult = checkAttemptCompleteRecovery(cleanedXmlString, validTools);
|
|
107
|
+
|
|
108
|
+
// If debugging is enabled, log the thinking content
|
|
109
|
+
if (process.env.DEBUG === '1' && thinkingContent) {
|
|
110
|
+
console.log(`[DEBUG] AI Thinking Process:\n${thinkingContent}`);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return {
|
|
114
|
+
cleanedXmlString,
|
|
115
|
+
thinkingContent,
|
|
116
|
+
recoveryResult
|
|
117
|
+
};
|
|
118
|
+
}
|
package/cjs/agent/ProbeAgent.cjs
CHANGED
|
@@ -29863,19 +29863,15 @@ var init_index = __esm({
|
|
|
29863
29863
|
}
|
|
29864
29864
|
});
|
|
29865
29865
|
|
|
29866
|
-
// src/agent/
|
|
29867
|
-
function
|
|
29868
|
-
return
|
|
29869
|
-
searchTool: searchTool(configOptions),
|
|
29870
|
-
queryTool: queryTool(configOptions),
|
|
29871
|
-
extractTool: extractTool(configOptions),
|
|
29872
|
-
delegateTool: delegateTool(configOptions)
|
|
29873
|
-
};
|
|
29866
|
+
// src/agent/xmlParsingUtils.js
|
|
29867
|
+
function removeThinkingTags(xmlString) {
|
|
29868
|
+
return xmlString.replace(/<thinking>[\s\S]*?<\/thinking>/g, "").trim();
|
|
29874
29869
|
}
|
|
29875
|
-
function
|
|
29870
|
+
function extractThinkingContent(xmlString) {
|
|
29876
29871
|
const thinkingMatch = xmlString.match(/<thinking>([\s\S]*?)<\/thinking>/);
|
|
29877
|
-
|
|
29878
|
-
|
|
29872
|
+
return thinkingMatch ? thinkingMatch[1].trim() : null;
|
|
29873
|
+
}
|
|
29874
|
+
function checkAttemptCompleteRecovery(cleanedXmlString, validTools = []) {
|
|
29879
29875
|
const attemptCompletePatterns = [
|
|
29880
29876
|
// Standard shorthand with optional whitespace
|
|
29881
29877
|
/^<attempt_complete>\s*$/,
|
|
@@ -29905,12 +29901,7 @@ function parseXmlToolCallWithThinking(xmlString, validTools) {
|
|
|
29905
29901
|
params: { result: "__PREVIOUS_RESPONSE__" }
|
|
29906
29902
|
};
|
|
29907
29903
|
}
|
|
29908
|
-
|
|
29909
|
-
if (process.env.DEBUG === "1" && thinkingContent) {
|
|
29910
|
-
console.log(`[DEBUG] AI Thinking Process:
|
|
29911
|
-
${thinkingContent}`);
|
|
29912
|
-
}
|
|
29913
|
-
return parsedTool;
|
|
29904
|
+
return null;
|
|
29914
29905
|
}
|
|
29915
29906
|
function hasOtherToolTags(xmlString, validTools = []) {
|
|
29916
29907
|
const defaultTools = ["search", "query", "extract", "listFiles", "searchFiles", "implement", "attempt_completion"];
|
|
@@ -29922,12 +29913,49 @@ function hasOtherToolTags(xmlString, validTools = []) {
|
|
|
29922
29913
|
}
|
|
29923
29914
|
return false;
|
|
29924
29915
|
}
|
|
29916
|
+
function processXmlWithThinkingAndRecovery(xmlString, validTools = []) {
|
|
29917
|
+
const thinkingContent = extractThinkingContent(xmlString);
|
|
29918
|
+
const cleanedXmlString = removeThinkingTags(xmlString);
|
|
29919
|
+
const recoveryResult = checkAttemptCompleteRecovery(cleanedXmlString, validTools);
|
|
29920
|
+
if (process.env.DEBUG === "1" && thinkingContent) {
|
|
29921
|
+
console.log(`[DEBUG] AI Thinking Process:
|
|
29922
|
+
${thinkingContent}`);
|
|
29923
|
+
}
|
|
29924
|
+
return {
|
|
29925
|
+
cleanedXmlString,
|
|
29926
|
+
thinkingContent,
|
|
29927
|
+
recoveryResult
|
|
29928
|
+
};
|
|
29929
|
+
}
|
|
29930
|
+
var init_xmlParsingUtils = __esm({
|
|
29931
|
+
"src/agent/xmlParsingUtils.js"() {
|
|
29932
|
+
"use strict";
|
|
29933
|
+
}
|
|
29934
|
+
});
|
|
29935
|
+
|
|
29936
|
+
// src/agent/tools.js
|
|
29937
|
+
function createTools(configOptions) {
|
|
29938
|
+
return {
|
|
29939
|
+
searchTool: searchTool(configOptions),
|
|
29940
|
+
queryTool: queryTool(configOptions),
|
|
29941
|
+
extractTool: extractTool(configOptions),
|
|
29942
|
+
delegateTool: delegateTool(configOptions)
|
|
29943
|
+
};
|
|
29944
|
+
}
|
|
29945
|
+
function parseXmlToolCallWithThinking(xmlString, validTools) {
|
|
29946
|
+
const { cleanedXmlString, recoveryResult } = processXmlWithThinkingAndRecovery(xmlString, validTools);
|
|
29947
|
+
if (recoveryResult) {
|
|
29948
|
+
return recoveryResult;
|
|
29949
|
+
}
|
|
29950
|
+
return parseXmlToolCall(cleanedXmlString, validTools);
|
|
29951
|
+
}
|
|
29925
29952
|
var import_crypto3, implementToolDefinition, listFilesToolDefinition, searchFilesToolDefinition;
|
|
29926
29953
|
var init_tools2 = __esm({
|
|
29927
29954
|
"src/agent/tools.js"() {
|
|
29928
29955
|
"use strict";
|
|
29929
29956
|
init_index();
|
|
29930
29957
|
import_crypto3 = require("crypto");
|
|
29958
|
+
init_xmlParsingUtils();
|
|
29931
29959
|
implementToolDefinition = `
|
|
29932
29960
|
## implement
|
|
29933
29961
|
Description: Implement a given task. Can modify files. Can be used ONLY if task explicitly stated that something requires modification or implementation.
|
|
@@ -31872,11 +31900,9 @@ function parseXmlMcpToolCall(xmlString, mcpToolNames = []) {
|
|
|
31872
31900
|
return null;
|
|
31873
31901
|
}
|
|
31874
31902
|
function parseHybridXmlToolCall(xmlString, nativeTools = [], mcpBridge = null) {
|
|
31875
|
-
|
|
31876
|
-
|
|
31877
|
-
|
|
31878
|
-
return { ...nativeResult, type: "native" };
|
|
31879
|
-
}
|
|
31903
|
+
const nativeResult = parseNativeXmlToolWithThinking(xmlString, nativeTools);
|
|
31904
|
+
if (nativeResult) {
|
|
31905
|
+
return { ...nativeResult, type: "native" };
|
|
31880
31906
|
}
|
|
31881
31907
|
if (mcpBridge) {
|
|
31882
31908
|
const mcpResult = parseXmlMcpToolCall(xmlString, mcpBridge.getToolNames());
|
|
@@ -31886,6 +31912,19 @@ function parseHybridXmlToolCall(xmlString, nativeTools = [], mcpBridge = null) {
|
|
|
31886
31912
|
}
|
|
31887
31913
|
return null;
|
|
31888
31914
|
}
|
|
31915
|
+
function parseNativeXmlToolWithThinking(xmlString, validTools) {
|
|
31916
|
+
const { cleanedXmlString, recoveryResult } = processXmlWithThinkingAndRecovery(xmlString, validTools);
|
|
31917
|
+
if (recoveryResult) {
|
|
31918
|
+
return recoveryResult;
|
|
31919
|
+
}
|
|
31920
|
+
for (const toolName of validTools) {
|
|
31921
|
+
const result = parseNativeXmlTool(cleanedXmlString, toolName);
|
|
31922
|
+
if (result) {
|
|
31923
|
+
return result;
|
|
31924
|
+
}
|
|
31925
|
+
}
|
|
31926
|
+
return null;
|
|
31927
|
+
}
|
|
31889
31928
|
function parseNativeXmlTool(xmlString, toolName) {
|
|
31890
31929
|
const openTag = `<${toolName}>`;
|
|
31891
31930
|
const closeTag = `</${toolName}>`;
|
|
@@ -31915,6 +31954,7 @@ var init_xmlBridge = __esm({
|
|
|
31915
31954
|
"use strict";
|
|
31916
31955
|
init_client2();
|
|
31917
31956
|
init_config();
|
|
31957
|
+
init_xmlParsingUtils();
|
|
31918
31958
|
MCPXmlBridge = class {
|
|
31919
31959
|
constructor(options = {}) {
|
|
31920
31960
|
this.debug = options.debug || false;
|
package/cjs/index.cjs
CHANGED
|
@@ -29949,19 +29949,15 @@ var init_tokenCounter = __esm({
|
|
|
29949
29949
|
}
|
|
29950
29950
|
});
|
|
29951
29951
|
|
|
29952
|
-
// src/agent/
|
|
29953
|
-
function
|
|
29954
|
-
return
|
|
29955
|
-
searchTool: searchTool(configOptions),
|
|
29956
|
-
queryTool: queryTool(configOptions),
|
|
29957
|
-
extractTool: extractTool(configOptions),
|
|
29958
|
-
delegateTool: delegateTool(configOptions)
|
|
29959
|
-
};
|
|
29952
|
+
// src/agent/xmlParsingUtils.js
|
|
29953
|
+
function removeThinkingTags(xmlString) {
|
|
29954
|
+
return xmlString.replace(/<thinking>[\s\S]*?<\/thinking>/g, "").trim();
|
|
29960
29955
|
}
|
|
29961
|
-
function
|
|
29956
|
+
function extractThinkingContent(xmlString) {
|
|
29962
29957
|
const thinkingMatch = xmlString.match(/<thinking>([\s\S]*?)<\/thinking>/);
|
|
29963
|
-
|
|
29964
|
-
|
|
29958
|
+
return thinkingMatch ? thinkingMatch[1].trim() : null;
|
|
29959
|
+
}
|
|
29960
|
+
function checkAttemptCompleteRecovery(cleanedXmlString, validTools = []) {
|
|
29965
29961
|
const attemptCompletePatterns = [
|
|
29966
29962
|
// Standard shorthand with optional whitespace
|
|
29967
29963
|
/^<attempt_complete>\s*$/,
|
|
@@ -29991,12 +29987,7 @@ function parseXmlToolCallWithThinking(xmlString, validTools) {
|
|
|
29991
29987
|
params: { result: "__PREVIOUS_RESPONSE__" }
|
|
29992
29988
|
};
|
|
29993
29989
|
}
|
|
29994
|
-
|
|
29995
|
-
if (process.env.DEBUG === "1" && thinkingContent) {
|
|
29996
|
-
console.log(`[DEBUG] AI Thinking Process:
|
|
29997
|
-
${thinkingContent}`);
|
|
29998
|
-
}
|
|
29999
|
-
return parsedTool;
|
|
29990
|
+
return null;
|
|
30000
29991
|
}
|
|
30001
29992
|
function hasOtherToolTags(xmlString, validTools = []) {
|
|
30002
29993
|
const defaultTools = ["search", "query", "extract", "listFiles", "searchFiles", "implement", "attempt_completion"];
|
|
@@ -30008,12 +29999,49 @@ function hasOtherToolTags(xmlString, validTools = []) {
|
|
|
30008
29999
|
}
|
|
30009
30000
|
return false;
|
|
30010
30001
|
}
|
|
30002
|
+
function processXmlWithThinkingAndRecovery(xmlString, validTools = []) {
|
|
30003
|
+
const thinkingContent = extractThinkingContent(xmlString);
|
|
30004
|
+
const cleanedXmlString = removeThinkingTags(xmlString);
|
|
30005
|
+
const recoveryResult = checkAttemptCompleteRecovery(cleanedXmlString, validTools);
|
|
30006
|
+
if (process.env.DEBUG === "1" && thinkingContent) {
|
|
30007
|
+
console.log(`[DEBUG] AI Thinking Process:
|
|
30008
|
+
${thinkingContent}`);
|
|
30009
|
+
}
|
|
30010
|
+
return {
|
|
30011
|
+
cleanedXmlString,
|
|
30012
|
+
thinkingContent,
|
|
30013
|
+
recoveryResult
|
|
30014
|
+
};
|
|
30015
|
+
}
|
|
30016
|
+
var init_xmlParsingUtils = __esm({
|
|
30017
|
+
"src/agent/xmlParsingUtils.js"() {
|
|
30018
|
+
"use strict";
|
|
30019
|
+
}
|
|
30020
|
+
});
|
|
30021
|
+
|
|
30022
|
+
// src/agent/tools.js
|
|
30023
|
+
function createTools(configOptions) {
|
|
30024
|
+
return {
|
|
30025
|
+
searchTool: searchTool(configOptions),
|
|
30026
|
+
queryTool: queryTool(configOptions),
|
|
30027
|
+
extractTool: extractTool(configOptions),
|
|
30028
|
+
delegateTool: delegateTool(configOptions)
|
|
30029
|
+
};
|
|
30030
|
+
}
|
|
30031
|
+
function parseXmlToolCallWithThinking(xmlString, validTools) {
|
|
30032
|
+
const { cleanedXmlString, recoveryResult } = processXmlWithThinkingAndRecovery(xmlString, validTools);
|
|
30033
|
+
if (recoveryResult) {
|
|
30034
|
+
return recoveryResult;
|
|
30035
|
+
}
|
|
30036
|
+
return parseXmlToolCall(cleanedXmlString, validTools);
|
|
30037
|
+
}
|
|
30011
30038
|
var import_crypto3, implementToolDefinition, listFilesToolDefinition, searchFilesToolDefinition;
|
|
30012
30039
|
var init_tools2 = __esm({
|
|
30013
30040
|
"src/agent/tools.js"() {
|
|
30014
30041
|
"use strict";
|
|
30015
30042
|
init_index();
|
|
30016
30043
|
import_crypto3 = require("crypto");
|
|
30044
|
+
init_xmlParsingUtils();
|
|
30017
30045
|
implementToolDefinition = `
|
|
30018
30046
|
## implement
|
|
30019
30047
|
Description: Implement a given task. Can modify files. Can be used ONLY if task explicitly stated that something requires modification or implementation.
|
|
@@ -31958,11 +31986,9 @@ function parseXmlMcpToolCall(xmlString, mcpToolNames = []) {
|
|
|
31958
31986
|
return null;
|
|
31959
31987
|
}
|
|
31960
31988
|
function parseHybridXmlToolCall(xmlString, nativeTools = [], mcpBridge = null) {
|
|
31961
|
-
|
|
31962
|
-
|
|
31963
|
-
|
|
31964
|
-
return { ...nativeResult, type: "native" };
|
|
31965
|
-
}
|
|
31989
|
+
const nativeResult = parseNativeXmlToolWithThinking(xmlString, nativeTools);
|
|
31990
|
+
if (nativeResult) {
|
|
31991
|
+
return { ...nativeResult, type: "native" };
|
|
31966
31992
|
}
|
|
31967
31993
|
if (mcpBridge) {
|
|
31968
31994
|
const mcpResult = parseXmlMcpToolCall(xmlString, mcpBridge.getToolNames());
|
|
@@ -31972,6 +31998,19 @@ function parseHybridXmlToolCall(xmlString, nativeTools = [], mcpBridge = null) {
|
|
|
31972
31998
|
}
|
|
31973
31999
|
return null;
|
|
31974
32000
|
}
|
|
32001
|
+
function parseNativeXmlToolWithThinking(xmlString, validTools) {
|
|
32002
|
+
const { cleanedXmlString, recoveryResult } = processXmlWithThinkingAndRecovery(xmlString, validTools);
|
|
32003
|
+
if (recoveryResult) {
|
|
32004
|
+
return recoveryResult;
|
|
32005
|
+
}
|
|
32006
|
+
for (const toolName of validTools) {
|
|
32007
|
+
const result = parseNativeXmlTool(cleanedXmlString, toolName);
|
|
32008
|
+
if (result) {
|
|
32009
|
+
return result;
|
|
32010
|
+
}
|
|
32011
|
+
}
|
|
32012
|
+
return null;
|
|
32013
|
+
}
|
|
31975
32014
|
function parseNativeXmlTool(xmlString, toolName) {
|
|
31976
32015
|
const openTag = `<${toolName}>`;
|
|
31977
32016
|
const closeTag = `</${toolName}>`;
|
|
@@ -32001,6 +32040,7 @@ var init_xmlBridge = __esm({
|
|
|
32001
32040
|
"use strict";
|
|
32002
32041
|
init_client2();
|
|
32003
32042
|
init_config();
|
|
32043
|
+
init_xmlParsingUtils();
|
|
32004
32044
|
MCPXmlBridge = class {
|
|
32005
32045
|
constructor(options = {}) {
|
|
32006
32046
|
this.debug = options.debug || false;
|
package/package.json
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import { MCPClientManager } from './client.js';
|
|
7
7
|
import { loadMCPConfiguration } from './config.js';
|
|
8
|
+
import { processXmlWithThinkingAndRecovery } from '../xmlParsingUtils.js';
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* Convert MCP tool to XML definition format
|
|
@@ -254,18 +255,18 @@ export class MCPXmlBridge {
|
|
|
254
255
|
|
|
255
256
|
/**
|
|
256
257
|
* Enhanced XML parser that handles both native and MCP tools
|
|
258
|
+
* Uses the exact same logic as CLI/SDK mode to ensure consistency
|
|
257
259
|
* @param {string} xmlString - XML string to parse
|
|
258
260
|
* @param {Array<string>} nativeTools - List of native tool names
|
|
259
261
|
* @param {MCPXmlBridge} mcpBridge - MCP bridge instance
|
|
260
262
|
* @returns {Object|null} Parsed tool call
|
|
261
263
|
*/
|
|
262
264
|
export function parseHybridXmlToolCall(xmlString, nativeTools = [], mcpBridge = null) {
|
|
263
|
-
// First try native tools with
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
}
|
|
265
|
+
// First try native tools with the same logic as CLI/SDK mode
|
|
266
|
+
// This includes thinking tag removal and attempt_complete recovery logic
|
|
267
|
+
const nativeResult = parseNativeXmlToolWithThinking(xmlString, nativeTools);
|
|
268
|
+
if (nativeResult) {
|
|
269
|
+
return { ...nativeResult, type: 'native' };
|
|
269
270
|
}
|
|
270
271
|
|
|
271
272
|
// Then try MCP tools if bridge is available
|
|
@@ -279,6 +280,33 @@ export function parseHybridXmlToolCall(xmlString, nativeTools = [], mcpBridge =
|
|
|
279
280
|
return null;
|
|
280
281
|
}
|
|
281
282
|
|
|
283
|
+
/**
|
|
284
|
+
* Parse native XML tools using the same logic as CLI/SDK mode
|
|
285
|
+
* Now uses shared utilities instead of duplicating code
|
|
286
|
+
* @param {string} xmlString - XML string to parse
|
|
287
|
+
* @param {Array<string>} validTools - List of valid tool names
|
|
288
|
+
* @returns {Object|null} Parsed tool call
|
|
289
|
+
*/
|
|
290
|
+
function parseNativeXmlToolWithThinking(xmlString, validTools) {
|
|
291
|
+
// Use the shared processing logic
|
|
292
|
+
const { cleanedXmlString, recoveryResult } = processXmlWithThinkingAndRecovery(xmlString, validTools);
|
|
293
|
+
|
|
294
|
+
// If recovery found an attempt_complete pattern, return it
|
|
295
|
+
if (recoveryResult) {
|
|
296
|
+
return recoveryResult;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// Use the original parseNativeXmlTool function to parse the cleaned XML string
|
|
300
|
+
for (const toolName of validTools) {
|
|
301
|
+
const result = parseNativeXmlTool(cleanedXmlString, toolName);
|
|
302
|
+
if (result) {
|
|
303
|
+
return result;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
return null;
|
|
308
|
+
}
|
|
309
|
+
|
|
282
310
|
/**
|
|
283
311
|
* Parse native XML tool (existing format)
|
|
284
312
|
* @param {string} xmlString - XML string
|
package/src/agent/tools.js
CHANGED
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
parseXmlToolCall
|
|
19
19
|
} from '../index.js';
|
|
20
20
|
import { randomUUID } from 'crypto';
|
|
21
|
+
import { processXmlWithThinkingAndRecovery } from './xmlParsingUtils.js';
|
|
21
22
|
|
|
22
23
|
// Create configured tool instances
|
|
23
24
|
export function createTools(configOptions) {
|
|
@@ -134,82 +135,15 @@ User: Find all markdown files in the docs directory, but only at the top level.
|
|
|
134
135
|
* @returns {Object|null} - The parsed tool call or null if no valid tool call found
|
|
135
136
|
*/
|
|
136
137
|
export function parseXmlToolCallWithThinking(xmlString, validTools) {
|
|
137
|
-
//
|
|
138
|
-
const
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
// Enhanced recovery logic for attempt_complete shorthand
|
|
145
|
-
// Check for various forms of attempt_complete tags:
|
|
146
|
-
// 1. Perfect shorthand: <attempt_complete>
|
|
147
|
-
// 2. Self-closing: <attempt_complete/>
|
|
148
|
-
// 3. Empty with closing tag: <attempt_complete></attempt_complete>
|
|
149
|
-
// 4. Incomplete: <attempt_complete (missing closing bracket)
|
|
150
|
-
// 5. With trailing content that should be ignored
|
|
151
|
-
const attemptCompletePatterns = [
|
|
152
|
-
// Standard shorthand with optional whitespace
|
|
153
|
-
/^<attempt_complete>\s*$/,
|
|
154
|
-
// Empty with proper closing tag (common case from the logs)
|
|
155
|
-
/^<attempt_complete>\s*<\/attempt_complete>\s*$/,
|
|
156
|
-
// Self-closing variant
|
|
157
|
-
/^<attempt_complete\s*\/>\s*$/,
|
|
158
|
-
// Incomplete opening tag (missing closing bracket)
|
|
159
|
-
/^<attempt_complete\s*$/,
|
|
160
|
-
// With trailing content (extract just the tag part) - must come after empty tag pattern
|
|
161
|
-
/^<attempt_complete>(.*)$/s,
|
|
162
|
-
// Self-closing with trailing content
|
|
163
|
-
/^<attempt_complete\s*\/>(.*)$/s
|
|
164
|
-
];
|
|
165
|
-
|
|
166
|
-
for (const pattern of attemptCompletePatterns) {
|
|
167
|
-
const match = cleanedXmlString.match(pattern);
|
|
168
|
-
if (match) {
|
|
169
|
-
// Convert any form of attempt_complete to the standard format
|
|
170
|
-
return {
|
|
171
|
-
toolName: 'attempt_completion',
|
|
172
|
-
params: { result: '__PREVIOUS_RESPONSE__' }
|
|
173
|
-
};
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
// Additional recovery: check if the string contains attempt_complete anywhere
|
|
178
|
-
// and treat the entire response as a completion signal if no other tool tags are found
|
|
179
|
-
if (cleanedXmlString.includes('<attempt_complete') && !hasOtherToolTags(cleanedXmlString, validTools)) {
|
|
180
|
-
// This handles malformed cases where attempt_complete appears but is broken
|
|
181
|
-
return {
|
|
182
|
-
toolName: 'attempt_completion',
|
|
183
|
-
params: { result: '__PREVIOUS_RESPONSE__' }
|
|
184
|
-
};
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
// Use the original parseXmlToolCall function to parse the cleaned XML string
|
|
188
|
-
const parsedTool = parseXmlToolCall(cleanedXmlString, validTools);
|
|
189
|
-
|
|
190
|
-
// If debugging is enabled, log the thinking content
|
|
191
|
-
if (process.env.DEBUG === '1' && thinkingContent) {
|
|
192
|
-
console.log(`[DEBUG] AI Thinking Process:\n${thinkingContent}`);
|
|
138
|
+
// Use the shared processing logic
|
|
139
|
+
const { cleanedXmlString, recoveryResult } = processXmlWithThinkingAndRecovery(xmlString, validTools);
|
|
140
|
+
|
|
141
|
+
// If recovery found an attempt_complete pattern, return it
|
|
142
|
+
if (recoveryResult) {
|
|
143
|
+
return recoveryResult;
|
|
193
144
|
}
|
|
194
145
|
|
|
195
|
-
|
|
146
|
+
// Otherwise, use the original parseXmlToolCall function to parse the cleaned XML string
|
|
147
|
+
return parseXmlToolCall(cleanedXmlString, validTools);
|
|
196
148
|
}
|
|
197
149
|
|
|
198
|
-
/**
|
|
199
|
-
* Helper function to check if the XML string contains other tool tags
|
|
200
|
-
* @param {string} xmlString - The XML string to check
|
|
201
|
-
* @param {string[]} validTools - List of valid tool names
|
|
202
|
-
* @returns {boolean} - True if other tool tags are found
|
|
203
|
-
*/
|
|
204
|
-
function hasOtherToolTags(xmlString, validTools = []) {
|
|
205
|
-
const defaultTools = ['search', 'query', 'extract', 'listFiles', 'searchFiles', 'implement', 'attempt_completion'];
|
|
206
|
-
const toolsToCheck = validTools.length > 0 ? validTools : defaultTools;
|
|
207
|
-
|
|
208
|
-
// Check for any tool tags other than attempt_complete variants
|
|
209
|
-
for (const tool of toolsToCheck) {
|
|
210
|
-
if (tool !== 'attempt_completion' && xmlString.includes(`<${tool}`)) {
|
|
211
|
-
return true;
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
return false;
|
|
215
|
-
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared XML parsing utilities used by both CLI/SDK and MCP modes
|
|
3
|
+
* This module contains the core logic for thinking tag removal and attempt_complete recovery
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Remove thinking tags and their content from XML string
|
|
8
|
+
* @param {string} xmlString - The XML string to clean
|
|
9
|
+
* @returns {string} - Cleaned XML string without thinking tags
|
|
10
|
+
*/
|
|
11
|
+
export function removeThinkingTags(xmlString) {
|
|
12
|
+
return xmlString.replace(/<thinking>[\s\S]*?<\/thinking>/g, '').trim();
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Extract thinking content for potential logging
|
|
17
|
+
* @param {string} xmlString - The XML string to extract from
|
|
18
|
+
* @returns {string|null} - Thinking content or null if not found
|
|
19
|
+
*/
|
|
20
|
+
export function extractThinkingContent(xmlString) {
|
|
21
|
+
const thinkingMatch = xmlString.match(/<thinking>([\s\S]*?)<\/thinking>/);
|
|
22
|
+
return thinkingMatch ? thinkingMatch[1].trim() : null;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Check for attempt_complete recovery patterns and return standardized result
|
|
27
|
+
* @param {string} cleanedXmlString - XML string with thinking tags already removed
|
|
28
|
+
* @param {Array<string>} validTools - List of valid tool names
|
|
29
|
+
* @returns {Object|null} - Standardized attempt_completion result or null
|
|
30
|
+
*/
|
|
31
|
+
export function checkAttemptCompleteRecovery(cleanedXmlString, validTools = []) {
|
|
32
|
+
// Enhanced recovery logic for attempt_complete shorthand
|
|
33
|
+
const attemptCompletePatterns = [
|
|
34
|
+
// Standard shorthand with optional whitespace
|
|
35
|
+
/^<attempt_complete>\s*$/,
|
|
36
|
+
// Empty with proper closing tag (common case from the logs)
|
|
37
|
+
/^<attempt_complete>\s*<\/attempt_complete>\s*$/,
|
|
38
|
+
// Self-closing variant
|
|
39
|
+
/^<attempt_complete\s*\/>\s*$/,
|
|
40
|
+
// Incomplete opening tag (missing closing bracket)
|
|
41
|
+
/^<attempt_complete\s*$/,
|
|
42
|
+
// With trailing content (extract just the tag part) - must come after empty tag pattern
|
|
43
|
+
/^<attempt_complete>(.*)$/s,
|
|
44
|
+
// Self-closing with trailing content
|
|
45
|
+
/^<attempt_complete\s*\/>(.*)$/s
|
|
46
|
+
];
|
|
47
|
+
|
|
48
|
+
for (const pattern of attemptCompletePatterns) {
|
|
49
|
+
const match = cleanedXmlString.match(pattern);
|
|
50
|
+
if (match) {
|
|
51
|
+
// Convert any form of attempt_complete to the standard format
|
|
52
|
+
return {
|
|
53
|
+
toolName: 'attempt_completion',
|
|
54
|
+
params: { result: '__PREVIOUS_RESPONSE__' }
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Additional recovery: check if the string contains attempt_complete anywhere
|
|
60
|
+
// and treat the entire response as a completion signal if no other tool tags are found
|
|
61
|
+
if (cleanedXmlString.includes('<attempt_complete') && !hasOtherToolTags(cleanedXmlString, validTools)) {
|
|
62
|
+
// This handles malformed cases where attempt_complete appears but is broken
|
|
63
|
+
return {
|
|
64
|
+
toolName: 'attempt_completion',
|
|
65
|
+
params: { result: '__PREVIOUS_RESPONSE__' }
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Helper function to check if the XML string contains other tool tags
|
|
74
|
+
* @param {string} xmlString - The XML string to check
|
|
75
|
+
* @param {string[]} validTools - List of valid tool names
|
|
76
|
+
* @returns {boolean} - True if other tool tags are found
|
|
77
|
+
*/
|
|
78
|
+
function hasOtherToolTags(xmlString, validTools = []) {
|
|
79
|
+
const defaultTools = ['search', 'query', 'extract', 'listFiles', 'searchFiles', 'implement', 'attempt_completion'];
|
|
80
|
+
const toolsToCheck = validTools.length > 0 ? validTools : defaultTools;
|
|
81
|
+
|
|
82
|
+
// Check for any tool tags other than attempt_complete variants
|
|
83
|
+
for (const tool of toolsToCheck) {
|
|
84
|
+
if (tool !== 'attempt_completion' && xmlString.includes(`<${tool}`)) {
|
|
85
|
+
return true;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Apply the full thinking tag removal and attempt_complete recovery logic
|
|
93
|
+
* This replicates the core logic from parseXmlToolCallWithThinking
|
|
94
|
+
* @param {string} xmlString - The XML string to process
|
|
95
|
+
* @param {Array<string>} validTools - List of valid tool names
|
|
96
|
+
* @returns {Object} - Processing result with cleanedXml and potentialRecovery
|
|
97
|
+
*/
|
|
98
|
+
export function processXmlWithThinkingAndRecovery(xmlString, validTools = []) {
|
|
99
|
+
// Extract thinking content if present (for potential logging or analysis)
|
|
100
|
+
const thinkingContent = extractThinkingContent(xmlString);
|
|
101
|
+
|
|
102
|
+
// Remove thinking tags and their content from the XML string
|
|
103
|
+
const cleanedXmlString = removeThinkingTags(xmlString);
|
|
104
|
+
|
|
105
|
+
// Check for attempt_complete recovery patterns
|
|
106
|
+
const recoveryResult = checkAttemptCompleteRecovery(cleanedXmlString, validTools);
|
|
107
|
+
|
|
108
|
+
// If debugging is enabled, log the thinking content
|
|
109
|
+
if (process.env.DEBUG === '1' && thinkingContent) {
|
|
110
|
+
console.log(`[DEBUG] AI Thinking Process:\n${thinkingContent}`);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return {
|
|
114
|
+
cleanedXmlString,
|
|
115
|
+
thinkingContent,
|
|
116
|
+
recoveryResult
|
|
117
|
+
};
|
|
118
|
+
}
|