@probelabs/probe 0.6.0-rc265 → 0.6.0-rc267
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-rc267-aarch64-apple-darwin.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc267-aarch64-unknown-linux-musl.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc267-x86_64-apple-darwin.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc267-x86_64-pc-windows-msvc.zip +0 -0
- package/bin/binaries/probe-v0.6.0-rc267-x86_64-unknown-linux-musl.tar.gz +0 -0
- package/build/agent/ProbeAgent.js +644 -1442
- package/build/agent/engines/enhanced-vercel.js +0 -7
- package/build/agent/index.js +3941 -5940
- package/build/agent/mcp/index.js +6 -15
- package/build/agent/mcp/xmlBridge.js +24 -324
- package/build/agent/tasks/index.js +0 -1
- package/build/agent/tools.js +11 -181
- package/build/index.js +13 -35
- package/build/tools/common.js +15 -707
- package/build/tools/executePlan.js +2 -2
- package/build/tools/index.js +8 -11
- package/cjs/agent/ProbeAgent.cjs +3734 -5831
- package/cjs/index.cjs +4797 -6869
- package/package.json +1 -1
- package/src/agent/ProbeAgent.js +644 -1442
- package/src/agent/engines/enhanced-vercel.js +0 -7
- package/src/agent/index.js +10 -2
- package/src/agent/mcp/index.js +6 -15
- package/src/agent/mcp/xmlBridge.js +24 -324
- package/src/agent/tasks/index.js +0 -1
- package/src/agent/tools.js +11 -181
- package/src/index.js +13 -35
- package/src/tools/common.js +15 -707
- package/src/tools/executePlan.js +2 -2
- package/src/tools/index.js +8 -11
- package/bin/binaries/probe-v0.6.0-rc265-aarch64-apple-darwin.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc265-aarch64-unknown-linux-musl.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc265-x86_64-apple-darwin.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc265-x86_64-pc-windows-msvc.zip +0 -0
- package/bin/binaries/probe-v0.6.0-rc265-x86_64-unknown-linux-musl.tar.gz +0 -0
- package/build/agent/xmlParsingUtils.js +0 -221
- package/src/agent/xmlParsingUtils.js +0 -221
package/src/tools/executePlan.js
CHANGED
|
@@ -393,7 +393,7 @@ export function createExecutePlanTool(options) {
|
|
|
393
393
|
description: 'Execute a JavaScript DSL program to orchestrate tool calls. ' +
|
|
394
394
|
'Use for batch processing, paginated APIs, multi-step workflows where intermediate data is large. ' +
|
|
395
395
|
'Write simple synchronous-looking code — do NOT use async/await.',
|
|
396
|
-
|
|
396
|
+
inputSchema: executePlanSchema,
|
|
397
397
|
execute: async ({ code, description }) => {
|
|
398
398
|
// Generate a unique session ID for this execute_plan invocation
|
|
399
399
|
// This ensures search pagination is isolated per execute_plan call
|
|
@@ -1051,7 +1051,7 @@ export function createCleanupExecutePlanTool(options) {
|
|
|
1051
1051
|
return tool({
|
|
1052
1052
|
description: 'Clean up output buffer and session store from previous execute_plan calls. ' +
|
|
1053
1053
|
'Use this when a previous execute_plan failed and left stale data, or before starting a fresh analysis.',
|
|
1054
|
-
|
|
1054
|
+
inputSchema: cleanupExecutePlanSchema,
|
|
1055
1055
|
execute: async ({ clearOutputBuffer = true, clearSessionStore = false }) => {
|
|
1056
1056
|
const span = tracer?.createToolSpan?.('cleanup_execute_plan', {
|
|
1057
1057
|
'cleanup.clear_output_buffer': clearOutputBuffer,
|
package/src/tools/index.js
CHANGED
|
@@ -12,7 +12,7 @@ export { editTool, createTool, multiEditTool } from './edit.js';
|
|
|
12
12
|
export { createSearchTool, createQueryTool, createExtractTool } from './langchain.js';
|
|
13
13
|
|
|
14
14
|
// Export execute_plan and cleanup_execute_plan tools
|
|
15
|
-
export { createExecutePlanTool,
|
|
15
|
+
export { createExecutePlanTool, createCleanupExecutePlanTool } from './executePlan.js';
|
|
16
16
|
|
|
17
17
|
// Export common schemas and utilities
|
|
18
18
|
export {
|
|
@@ -24,15 +24,15 @@ export {
|
|
|
24
24
|
executePlanSchema,
|
|
25
25
|
cleanupExecutePlanSchema,
|
|
26
26
|
delegateDescription,
|
|
27
|
-
delegateToolDefinition,
|
|
28
27
|
bashDescription,
|
|
29
|
-
bashToolDefinition,
|
|
30
28
|
attemptCompletionSchema,
|
|
31
|
-
attemptCompletionToolDefinition,
|
|
32
29
|
parseAndResolvePaths,
|
|
33
30
|
resolveTargetPath,
|
|
34
|
-
|
|
35
|
-
|
|
31
|
+
listFilesSchema,
|
|
32
|
+
searchFilesSchema,
|
|
33
|
+
readImageSchema,
|
|
34
|
+
listSkillsSchema,
|
|
35
|
+
useSkillSchema
|
|
36
36
|
} from './common.js';
|
|
37
37
|
|
|
38
38
|
// Export edit and create schemas
|
|
@@ -42,10 +42,7 @@ export {
|
|
|
42
42
|
multiEditSchema,
|
|
43
43
|
editDescription,
|
|
44
44
|
createDescription,
|
|
45
|
-
multiEditDescription
|
|
46
|
-
editToolDefinition,
|
|
47
|
-
createToolDefinition,
|
|
48
|
-
multiEditToolDefinition
|
|
45
|
+
multiEditDescription
|
|
49
46
|
} from './edit.js';
|
|
50
47
|
|
|
51
48
|
// Export system message
|
|
@@ -66,4 +63,4 @@ const tools = {
|
|
|
66
63
|
DEFAULT_SYSTEM_MESSAGE
|
|
67
64
|
};
|
|
68
65
|
|
|
69
|
-
export { tools };
|
|
66
|
+
export { tools };
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -1,221 +0,0 @@
|
|
|
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
|
-
import { DEFAULT_VALID_TOOLS, buildToolTagPattern } from '../tools/common.js';
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Remove thinking tags and their content from XML string
|
|
10
|
-
* Handles both closed and unclosed thinking tags
|
|
11
|
-
* @param {string} xmlString - The XML string to clean
|
|
12
|
-
* @returns {string} - Cleaned XML string without thinking tags
|
|
13
|
-
*/
|
|
14
|
-
export function removeThinkingTags(xmlString, validTools = DEFAULT_VALID_TOOLS) {
|
|
15
|
-
let result = xmlString;
|
|
16
|
-
|
|
17
|
-
// Remove all properly closed thinking tags first
|
|
18
|
-
result = result.replace(/<thinking>[\s\S]*?<\/thinking>/g, '');
|
|
19
|
-
|
|
20
|
-
// Handle unclosed thinking tags
|
|
21
|
-
// Find any remaining <thinking> tag (which means it's unclosed)
|
|
22
|
-
const thinkingIndex = result.indexOf('<thinking>');
|
|
23
|
-
if (thinkingIndex !== -1) {
|
|
24
|
-
// Check if there's a tool tag after the thinking tag
|
|
25
|
-
// We want to preserve tool tags even if they're after unclosed thinking
|
|
26
|
-
const afterThinking = result.substring(thinkingIndex + '<thinking>'.length);
|
|
27
|
-
|
|
28
|
-
// Look for any tool tags in the remaining content
|
|
29
|
-
// Use the provided valid tools list to build the pattern dynamically
|
|
30
|
-
const toolPattern = buildToolTagPattern(validTools);
|
|
31
|
-
const toolMatch = afterThinking.match(toolPattern);
|
|
32
|
-
|
|
33
|
-
if (toolMatch) {
|
|
34
|
-
// Found a tool tag - remove thinking tag and its content up to the tool tag
|
|
35
|
-
const toolStart = thinkingIndex + '<thinking>'.length + toolMatch.index;
|
|
36
|
-
result = result.substring(0, thinkingIndex) + result.substring(toolStart);
|
|
37
|
-
} else {
|
|
38
|
-
// No tool tag found - remove everything from <thinking> onwards
|
|
39
|
-
result = result.substring(0, thinkingIndex);
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
return result.trim();
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Extract thinking content for potential logging
|
|
48
|
-
* Handles nested thinking tags by recursively stripping inner tags.
|
|
49
|
-
* @param {string} xmlString - The XML string to extract from
|
|
50
|
-
* @returns {string|null} - Thinking content (cleaned of nested tags) or null if not found
|
|
51
|
-
*/
|
|
52
|
-
export function extractThinkingContent(xmlString) {
|
|
53
|
-
const thinkingMatch = xmlString.match(/<thinking>([\s\S]*?)<\/thinking>/);
|
|
54
|
-
if (!thinkingMatch) {
|
|
55
|
-
return null;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
let content = thinkingMatch[1].trim();
|
|
59
|
-
|
|
60
|
-
// Handle nested thinking tags: if the extracted content itself starts with <thinking>,
|
|
61
|
-
// recursively extract from it until we get clean content.
|
|
62
|
-
// This handles: <thinking><thinking>content</thinking></thinking>
|
|
63
|
-
// where non-greedy match captures "<thinking>content" (issue #439)
|
|
64
|
-
while (content.startsWith('<thinking>')) {
|
|
65
|
-
const innerMatch = content.match(/<thinking>([\s\S]*?)<\/thinking>/);
|
|
66
|
-
if (innerMatch) {
|
|
67
|
-
content = innerMatch[1].trim();
|
|
68
|
-
} else {
|
|
69
|
-
// Unclosed inner <thinking> tag - strip the opening tag and use remaining content
|
|
70
|
-
// e.g., "<thinking>content" becomes "content"
|
|
71
|
-
content = content.substring('<thinking>'.length).trim();
|
|
72
|
-
break;
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// Also strip any remaining thinking tags that might be embedded in the content
|
|
77
|
-
content = content.replace(/<\/?thinking>/g, '').trim();
|
|
78
|
-
|
|
79
|
-
return content || null;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* Check for attempt_complete recovery patterns and return standardized result
|
|
84
|
-
* @param {string} cleanedXmlString - XML string with thinking tags already removed
|
|
85
|
-
* @param {Array<string>} validTools - List of valid tool names
|
|
86
|
-
* @returns {Object|null} - Standardized attempt_completion result or null
|
|
87
|
-
*/
|
|
88
|
-
export function checkAttemptCompleteRecovery(cleanedXmlString, validTools = []) {
|
|
89
|
-
// Check for <attempt_completion> with content (with or without closing tag)
|
|
90
|
-
// This handles: "<attempt_completion>content" or "<attempt_completion>content</attempt_completion>"
|
|
91
|
-
|
|
92
|
-
// IMPORTANT: Use greedy match ([\s\S]*) instead of non-greedy ([\s\S]*?) to handle cases
|
|
93
|
-
// where the content contains the string "</attempt_completion>" (e.g., in regex patterns or code examples).
|
|
94
|
-
// We want to find the LAST occurrence of </attempt_completion>, not the first one.
|
|
95
|
-
const openTagIndex = cleanedXmlString.indexOf('<attempt_completion>');
|
|
96
|
-
if (openTagIndex !== -1) {
|
|
97
|
-
const afterOpenTag = cleanedXmlString.substring(openTagIndex + '<attempt_completion>'.length);
|
|
98
|
-
const closeTagIndex = cleanedXmlString.lastIndexOf('</attempt_completion>');
|
|
99
|
-
|
|
100
|
-
let content;
|
|
101
|
-
let hasClosingTag = false;
|
|
102
|
-
|
|
103
|
-
if (closeTagIndex !== -1 && closeTagIndex >= openTagIndex + '<attempt_completion>'.length) {
|
|
104
|
-
// Found a closing tag at or after the opening tag - extract content between them
|
|
105
|
-
content = cleanedXmlString.substring(
|
|
106
|
-
openTagIndex + '<attempt_completion>'.length,
|
|
107
|
-
closeTagIndex
|
|
108
|
-
).trim();
|
|
109
|
-
hasClosingTag = true;
|
|
110
|
-
} else {
|
|
111
|
-
// No closing tag - use content from opening tag to end of string
|
|
112
|
-
content = afterOpenTag.trim();
|
|
113
|
-
hasClosingTag = false;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
if (content) {
|
|
117
|
-
// If there's content after the tag, use it as the result
|
|
118
|
-
return {
|
|
119
|
-
toolName: 'attempt_completion',
|
|
120
|
-
params: { result: content }
|
|
121
|
-
};
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
// If the tag exists but is empty:
|
|
125
|
-
// - With closing tag (e.g., "<attempt_completion></attempt_completion>"): use empty string
|
|
126
|
-
// - Without closing tag (e.g., "<attempt_completion>"): use previous response
|
|
127
|
-
return {
|
|
128
|
-
toolName: 'attempt_completion',
|
|
129
|
-
params: { result: hasClosingTag ? '' : '__PREVIOUS_RESPONSE__' }
|
|
130
|
-
};
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
// Enhanced recovery logic for attempt_complete shorthand
|
|
134
|
-
const attemptCompletePatterns = [
|
|
135
|
-
// Standard shorthand with optional whitespace
|
|
136
|
-
/^<attempt_complete>\s*$/,
|
|
137
|
-
// Empty with proper closing tag (common case from the logs)
|
|
138
|
-
/^<attempt_complete>\s*<\/attempt_complete>\s*$/,
|
|
139
|
-
// Self-closing variant
|
|
140
|
-
/^<attempt_complete\s*\/>\s*$/,
|
|
141
|
-
// Incomplete opening tag (missing closing bracket)
|
|
142
|
-
/^<attempt_complete\s*$/,
|
|
143
|
-
// With trailing content (extract just the tag part) - must come after empty tag pattern
|
|
144
|
-
/^<attempt_complete>(.*)$/s,
|
|
145
|
-
// Self-closing with trailing content
|
|
146
|
-
/^<attempt_complete\s*\/>(.*)$/s
|
|
147
|
-
];
|
|
148
|
-
|
|
149
|
-
for (const pattern of attemptCompletePatterns) {
|
|
150
|
-
const match = cleanedXmlString.match(pattern);
|
|
151
|
-
if (match) {
|
|
152
|
-
// Convert any form of attempt_complete to the standard format
|
|
153
|
-
return {
|
|
154
|
-
toolName: 'attempt_completion',
|
|
155
|
-
params: { result: '__PREVIOUS_RESPONSE__' }
|
|
156
|
-
};
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
// Additional recovery: check if the string contains attempt_complete anywhere
|
|
161
|
-
// and treat the entire response as a completion signal if no other tool tags are found
|
|
162
|
-
if (cleanedXmlString.includes('<attempt_complete') && !hasOtherToolTags(cleanedXmlString, validTools)) {
|
|
163
|
-
// This handles malformed cases where attempt_complete appears but is broken
|
|
164
|
-
return {
|
|
165
|
-
toolName: 'attempt_completion',
|
|
166
|
-
params: { result: '__PREVIOUS_RESPONSE__' }
|
|
167
|
-
};
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
return null;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
/**
|
|
174
|
-
* Helper function to check if the XML string contains other tool tags
|
|
175
|
-
* @param {string} xmlString - The XML string to check
|
|
176
|
-
* @param {string[]} validTools - List of valid tool names
|
|
177
|
-
* @returns {boolean} - True if other tool tags are found
|
|
178
|
-
*/
|
|
179
|
-
function hasOtherToolTags(xmlString, validTools = []) {
|
|
180
|
-
// Use the shared canonical tool list as default
|
|
181
|
-
const toolsToCheck = validTools.length > 0 ? validTools : DEFAULT_VALID_TOOLS;
|
|
182
|
-
|
|
183
|
-
// Check for any tool tags other than attempt_complete variants
|
|
184
|
-
for (const tool of toolsToCheck) {
|
|
185
|
-
if (tool !== 'attempt_completion' && xmlString.includes(`<${tool}`)) {
|
|
186
|
-
return true;
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
return false;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
/**
|
|
193
|
-
* Apply the full thinking tag removal and attempt_complete recovery logic
|
|
194
|
-
* This replicates the core logic from parseXmlToolCallWithThinking
|
|
195
|
-
* @param {string} xmlString - The XML string to process
|
|
196
|
-
* @param {Array<string>} validTools - List of valid tool names
|
|
197
|
-
* @returns {Object} - Processing result with cleanedXml and potentialRecovery
|
|
198
|
-
*/
|
|
199
|
-
export function processXmlWithThinkingAndRecovery(xmlString, validTools = []) {
|
|
200
|
-
// Extract thinking content if present (for potential logging or analysis)
|
|
201
|
-
const thinkingContent = extractThinkingContent(xmlString);
|
|
202
|
-
|
|
203
|
-
// Remove thinking tags and their content from the XML string
|
|
204
|
-
// Forward validTools so that tool tags (e.g. edit, create) inside unclosed
|
|
205
|
-
// thinking blocks are preserved when they are in the valid tools list
|
|
206
|
-
const cleanedXmlString = removeThinkingTags(xmlString, validTools.length > 0 ? validTools : undefined);
|
|
207
|
-
|
|
208
|
-
// Check for attempt_complete recovery patterns
|
|
209
|
-
const recoveryResult = checkAttemptCompleteRecovery(cleanedXmlString, validTools);
|
|
210
|
-
|
|
211
|
-
// If debugging is enabled, log the thinking content
|
|
212
|
-
if (process.env.DEBUG === '1' && thinkingContent) {
|
|
213
|
-
console.log(`[DEBUG] AI Thinking Process:\n${thinkingContent}`);
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
return {
|
|
217
|
-
cleanedXmlString,
|
|
218
|
-
thinkingContent,
|
|
219
|
-
recoveryResult
|
|
220
|
-
};
|
|
221
|
-
}
|
|
@@ -1,221 +0,0 @@
|
|
|
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
|
-
import { DEFAULT_VALID_TOOLS, buildToolTagPattern } from '../tools/common.js';
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Remove thinking tags and their content from XML string
|
|
10
|
-
* Handles both closed and unclosed thinking tags
|
|
11
|
-
* @param {string} xmlString - The XML string to clean
|
|
12
|
-
* @returns {string} - Cleaned XML string without thinking tags
|
|
13
|
-
*/
|
|
14
|
-
export function removeThinkingTags(xmlString, validTools = DEFAULT_VALID_TOOLS) {
|
|
15
|
-
let result = xmlString;
|
|
16
|
-
|
|
17
|
-
// Remove all properly closed thinking tags first
|
|
18
|
-
result = result.replace(/<thinking>[\s\S]*?<\/thinking>/g, '');
|
|
19
|
-
|
|
20
|
-
// Handle unclosed thinking tags
|
|
21
|
-
// Find any remaining <thinking> tag (which means it's unclosed)
|
|
22
|
-
const thinkingIndex = result.indexOf('<thinking>');
|
|
23
|
-
if (thinkingIndex !== -1) {
|
|
24
|
-
// Check if there's a tool tag after the thinking tag
|
|
25
|
-
// We want to preserve tool tags even if they're after unclosed thinking
|
|
26
|
-
const afterThinking = result.substring(thinkingIndex + '<thinking>'.length);
|
|
27
|
-
|
|
28
|
-
// Look for any tool tags in the remaining content
|
|
29
|
-
// Use the provided valid tools list to build the pattern dynamically
|
|
30
|
-
const toolPattern = buildToolTagPattern(validTools);
|
|
31
|
-
const toolMatch = afterThinking.match(toolPattern);
|
|
32
|
-
|
|
33
|
-
if (toolMatch) {
|
|
34
|
-
// Found a tool tag - remove thinking tag and its content up to the tool tag
|
|
35
|
-
const toolStart = thinkingIndex + '<thinking>'.length + toolMatch.index;
|
|
36
|
-
result = result.substring(0, thinkingIndex) + result.substring(toolStart);
|
|
37
|
-
} else {
|
|
38
|
-
// No tool tag found - remove everything from <thinking> onwards
|
|
39
|
-
result = result.substring(0, thinkingIndex);
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
return result.trim();
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Extract thinking content for potential logging
|
|
48
|
-
* Handles nested thinking tags by recursively stripping inner tags.
|
|
49
|
-
* @param {string} xmlString - The XML string to extract from
|
|
50
|
-
* @returns {string|null} - Thinking content (cleaned of nested tags) or null if not found
|
|
51
|
-
*/
|
|
52
|
-
export function extractThinkingContent(xmlString) {
|
|
53
|
-
const thinkingMatch = xmlString.match(/<thinking>([\s\S]*?)<\/thinking>/);
|
|
54
|
-
if (!thinkingMatch) {
|
|
55
|
-
return null;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
let content = thinkingMatch[1].trim();
|
|
59
|
-
|
|
60
|
-
// Handle nested thinking tags: if the extracted content itself starts with <thinking>,
|
|
61
|
-
// recursively extract from it until we get clean content.
|
|
62
|
-
// This handles: <thinking><thinking>content</thinking></thinking>
|
|
63
|
-
// where non-greedy match captures "<thinking>content" (issue #439)
|
|
64
|
-
while (content.startsWith('<thinking>')) {
|
|
65
|
-
const innerMatch = content.match(/<thinking>([\s\S]*?)<\/thinking>/);
|
|
66
|
-
if (innerMatch) {
|
|
67
|
-
content = innerMatch[1].trim();
|
|
68
|
-
} else {
|
|
69
|
-
// Unclosed inner <thinking> tag - strip the opening tag and use remaining content
|
|
70
|
-
// e.g., "<thinking>content" becomes "content"
|
|
71
|
-
content = content.substring('<thinking>'.length).trim();
|
|
72
|
-
break;
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// Also strip any remaining thinking tags that might be embedded in the content
|
|
77
|
-
content = content.replace(/<\/?thinking>/g, '').trim();
|
|
78
|
-
|
|
79
|
-
return content || null;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* Check for attempt_complete recovery patterns and return standardized result
|
|
84
|
-
* @param {string} cleanedXmlString - XML string with thinking tags already removed
|
|
85
|
-
* @param {Array<string>} validTools - List of valid tool names
|
|
86
|
-
* @returns {Object|null} - Standardized attempt_completion result or null
|
|
87
|
-
*/
|
|
88
|
-
export function checkAttemptCompleteRecovery(cleanedXmlString, validTools = []) {
|
|
89
|
-
// Check for <attempt_completion> with content (with or without closing tag)
|
|
90
|
-
// This handles: "<attempt_completion>content" or "<attempt_completion>content</attempt_completion>"
|
|
91
|
-
|
|
92
|
-
// IMPORTANT: Use greedy match ([\s\S]*) instead of non-greedy ([\s\S]*?) to handle cases
|
|
93
|
-
// where the content contains the string "</attempt_completion>" (e.g., in regex patterns or code examples).
|
|
94
|
-
// We want to find the LAST occurrence of </attempt_completion>, not the first one.
|
|
95
|
-
const openTagIndex = cleanedXmlString.indexOf('<attempt_completion>');
|
|
96
|
-
if (openTagIndex !== -1) {
|
|
97
|
-
const afterOpenTag = cleanedXmlString.substring(openTagIndex + '<attempt_completion>'.length);
|
|
98
|
-
const closeTagIndex = cleanedXmlString.lastIndexOf('</attempt_completion>');
|
|
99
|
-
|
|
100
|
-
let content;
|
|
101
|
-
let hasClosingTag = false;
|
|
102
|
-
|
|
103
|
-
if (closeTagIndex !== -1 && closeTagIndex >= openTagIndex + '<attempt_completion>'.length) {
|
|
104
|
-
// Found a closing tag at or after the opening tag - extract content between them
|
|
105
|
-
content = cleanedXmlString.substring(
|
|
106
|
-
openTagIndex + '<attempt_completion>'.length,
|
|
107
|
-
closeTagIndex
|
|
108
|
-
).trim();
|
|
109
|
-
hasClosingTag = true;
|
|
110
|
-
} else {
|
|
111
|
-
// No closing tag - use content from opening tag to end of string
|
|
112
|
-
content = afterOpenTag.trim();
|
|
113
|
-
hasClosingTag = false;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
if (content) {
|
|
117
|
-
// If there's content after the tag, use it as the result
|
|
118
|
-
return {
|
|
119
|
-
toolName: 'attempt_completion',
|
|
120
|
-
params: { result: content }
|
|
121
|
-
};
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
// If the tag exists but is empty:
|
|
125
|
-
// - With closing tag (e.g., "<attempt_completion></attempt_completion>"): use empty string
|
|
126
|
-
// - Without closing tag (e.g., "<attempt_completion>"): use previous response
|
|
127
|
-
return {
|
|
128
|
-
toolName: 'attempt_completion',
|
|
129
|
-
params: { result: hasClosingTag ? '' : '__PREVIOUS_RESPONSE__' }
|
|
130
|
-
};
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
// Enhanced recovery logic for attempt_complete shorthand
|
|
134
|
-
const attemptCompletePatterns = [
|
|
135
|
-
// Standard shorthand with optional whitespace
|
|
136
|
-
/^<attempt_complete>\s*$/,
|
|
137
|
-
// Empty with proper closing tag (common case from the logs)
|
|
138
|
-
/^<attempt_complete>\s*<\/attempt_complete>\s*$/,
|
|
139
|
-
// Self-closing variant
|
|
140
|
-
/^<attempt_complete\s*\/>\s*$/,
|
|
141
|
-
// Incomplete opening tag (missing closing bracket)
|
|
142
|
-
/^<attempt_complete\s*$/,
|
|
143
|
-
// With trailing content (extract just the tag part) - must come after empty tag pattern
|
|
144
|
-
/^<attempt_complete>(.*)$/s,
|
|
145
|
-
// Self-closing with trailing content
|
|
146
|
-
/^<attempt_complete\s*\/>(.*)$/s
|
|
147
|
-
];
|
|
148
|
-
|
|
149
|
-
for (const pattern of attemptCompletePatterns) {
|
|
150
|
-
const match = cleanedXmlString.match(pattern);
|
|
151
|
-
if (match) {
|
|
152
|
-
// Convert any form of attempt_complete to the standard format
|
|
153
|
-
return {
|
|
154
|
-
toolName: 'attempt_completion',
|
|
155
|
-
params: { result: '__PREVIOUS_RESPONSE__' }
|
|
156
|
-
};
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
// Additional recovery: check if the string contains attempt_complete anywhere
|
|
161
|
-
// and treat the entire response as a completion signal if no other tool tags are found
|
|
162
|
-
if (cleanedXmlString.includes('<attempt_complete') && !hasOtherToolTags(cleanedXmlString, validTools)) {
|
|
163
|
-
// This handles malformed cases where attempt_complete appears but is broken
|
|
164
|
-
return {
|
|
165
|
-
toolName: 'attempt_completion',
|
|
166
|
-
params: { result: '__PREVIOUS_RESPONSE__' }
|
|
167
|
-
};
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
return null;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
/**
|
|
174
|
-
* Helper function to check if the XML string contains other tool tags
|
|
175
|
-
* @param {string} xmlString - The XML string to check
|
|
176
|
-
* @param {string[]} validTools - List of valid tool names
|
|
177
|
-
* @returns {boolean} - True if other tool tags are found
|
|
178
|
-
*/
|
|
179
|
-
function hasOtherToolTags(xmlString, validTools = []) {
|
|
180
|
-
// Use the shared canonical tool list as default
|
|
181
|
-
const toolsToCheck = validTools.length > 0 ? validTools : DEFAULT_VALID_TOOLS;
|
|
182
|
-
|
|
183
|
-
// Check for any tool tags other than attempt_complete variants
|
|
184
|
-
for (const tool of toolsToCheck) {
|
|
185
|
-
if (tool !== 'attempt_completion' && xmlString.includes(`<${tool}`)) {
|
|
186
|
-
return true;
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
return false;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
/**
|
|
193
|
-
* Apply the full thinking tag removal and attempt_complete recovery logic
|
|
194
|
-
* This replicates the core logic from parseXmlToolCallWithThinking
|
|
195
|
-
* @param {string} xmlString - The XML string to process
|
|
196
|
-
* @param {Array<string>} validTools - List of valid tool names
|
|
197
|
-
* @returns {Object} - Processing result with cleanedXml and potentialRecovery
|
|
198
|
-
*/
|
|
199
|
-
export function processXmlWithThinkingAndRecovery(xmlString, validTools = []) {
|
|
200
|
-
// Extract thinking content if present (for potential logging or analysis)
|
|
201
|
-
const thinkingContent = extractThinkingContent(xmlString);
|
|
202
|
-
|
|
203
|
-
// Remove thinking tags and their content from the XML string
|
|
204
|
-
// Forward validTools so that tool tags (e.g. edit, create) inside unclosed
|
|
205
|
-
// thinking blocks are preserved when they are in the valid tools list
|
|
206
|
-
const cleanedXmlString = removeThinkingTags(xmlString, validTools.length > 0 ? validTools : undefined);
|
|
207
|
-
|
|
208
|
-
// Check for attempt_complete recovery patterns
|
|
209
|
-
const recoveryResult = checkAttemptCompleteRecovery(cleanedXmlString, validTools);
|
|
210
|
-
|
|
211
|
-
// If debugging is enabled, log the thinking content
|
|
212
|
-
if (process.env.DEBUG === '1' && thinkingContent) {
|
|
213
|
-
console.log(`[DEBUG] AI Thinking Process:\n${thinkingContent}`);
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
return {
|
|
217
|
-
cleanedXmlString,
|
|
218
|
-
thinkingContent,
|
|
219
|
-
recoveryResult
|
|
220
|
-
};
|
|
221
|
-
}
|