@probelabs/probe 0.6.0-rc115 → 0.6.0-rc117
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/ProbeAgent.js +100 -10
- package/build/agent/index.js +3853 -306
- package/build/agent/probeTool.js +93 -18
- package/build/agent/schemaUtils.js +1 -1
- package/build/index.js +4 -0
- package/cjs/agent/ProbeAgent.cjs +3856 -309
- package/cjs/index.cjs +3601 -50
- package/package.json +2 -2
- package/src/agent/ProbeAgent.js +100 -10
- package/src/agent/probeTool.js +93 -18
- package/src/agent/schemaUtils.js +1 -1
- package/src/index.js +4 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@probelabs/probe",
|
|
3
|
-
"version": "0.6.0-
|
|
3
|
+
"version": "0.6.0-rc117",
|
|
4
4
|
"description": "Node.js wrapper for the probe code search tool",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"module": "src/index.js",
|
|
@@ -80,7 +80,7 @@
|
|
|
80
80
|
"@opentelemetry/sdk-node": "^0.203.0",
|
|
81
81
|
"@opentelemetry/sdk-trace-base": "^1.30.0",
|
|
82
82
|
"@opentelemetry/semantic-conventions": "^1.36.0",
|
|
83
|
-
"@probelabs/maid": "^0.0.
|
|
83
|
+
"@probelabs/maid": "^0.0.7",
|
|
84
84
|
"ai": "^5.0.0",
|
|
85
85
|
"axios": "^1.8.3",
|
|
86
86
|
"fs-extra": "^11.1.1",
|
package/src/agent/ProbeAgent.js
CHANGED
|
@@ -188,9 +188,23 @@ export class ProbeAgent {
|
|
|
188
188
|
if (this.enableBash && wrappedTools.bashToolInstance) {
|
|
189
189
|
this.toolImplementations.bash = wrappedTools.bashToolInstance;
|
|
190
190
|
}
|
|
191
|
-
|
|
191
|
+
|
|
192
192
|
// Store wrapped tools for ACP system
|
|
193
193
|
this.wrappedTools = wrappedTools;
|
|
194
|
+
|
|
195
|
+
// Log available tools in debug mode
|
|
196
|
+
if (this.debug) {
|
|
197
|
+
console.error('\n[DEBUG] ========================================');
|
|
198
|
+
console.error('[DEBUG] ProbeAgent Tools Initialized');
|
|
199
|
+
console.error('[DEBUG] Session ID:', this.sessionId);
|
|
200
|
+
console.error('[DEBUG] Available tools:');
|
|
201
|
+
for (const toolName of Object.keys(this.toolImplementations)) {
|
|
202
|
+
console.error(`[DEBUG] - ${toolName}`);
|
|
203
|
+
}
|
|
204
|
+
console.error('[DEBUG] Allowed folders:', this.allowedFolders);
|
|
205
|
+
console.error('[DEBUG] Outline mode:', this.outline);
|
|
206
|
+
console.error('[DEBUG] ========================================\n');
|
|
207
|
+
}
|
|
194
208
|
}
|
|
195
209
|
|
|
196
210
|
/**
|
|
@@ -1117,34 +1131,74 @@ When troubleshooting:
|
|
|
1117
1131
|
if (type === 'mcp' && this.mcpBridge && this.mcpBridge.isMcpTool(toolName)) {
|
|
1118
1132
|
// Execute MCP tool
|
|
1119
1133
|
try {
|
|
1120
|
-
|
|
1134
|
+
// Log MCP tool execution in debug mode
|
|
1135
|
+
if (this.debug) {
|
|
1136
|
+
console.error(`\n[DEBUG] ========================================`);
|
|
1137
|
+
console.error(`[DEBUG] Executing MCP tool: ${toolName}`);
|
|
1138
|
+
console.error(`[DEBUG] Arguments:`);
|
|
1139
|
+
for (const [key, value] of Object.entries(params)) {
|
|
1140
|
+
const displayValue = typeof value === 'string' && value.length > 100
|
|
1141
|
+
? value.substring(0, 100) + '...'
|
|
1142
|
+
: value;
|
|
1143
|
+
console.error(`[DEBUG] ${key}: ${JSON.stringify(displayValue)}`);
|
|
1144
|
+
}
|
|
1145
|
+
console.error(`[DEBUG] ========================================\n`);
|
|
1146
|
+
}
|
|
1121
1147
|
|
|
1122
1148
|
// Execute MCP tool through the bridge
|
|
1123
1149
|
const executionResult = await this.mcpBridge.mcpTools[toolName].execute(params);
|
|
1124
1150
|
|
|
1125
1151
|
const toolResultContent = typeof executionResult === 'string' ? executionResult : JSON.stringify(executionResult, null, 2);
|
|
1126
|
-
|
|
1152
|
+
|
|
1153
|
+
// Log MCP tool result in debug mode
|
|
1127
1154
|
if (this.debug) {
|
|
1128
|
-
|
|
1155
|
+
const preview = toolResultContent.length > 500 ? toolResultContent.substring(0, 500) + '...' : toolResultContent;
|
|
1156
|
+
console.error(`[DEBUG] ========================================`);
|
|
1157
|
+
console.error(`[DEBUG] MCP tool '${toolName}' completed successfully`);
|
|
1158
|
+
console.error(`[DEBUG] Result preview:`);
|
|
1159
|
+
console.error(preview);
|
|
1160
|
+
console.error(`[DEBUG] ========================================\n`);
|
|
1129
1161
|
}
|
|
1130
1162
|
|
|
1131
1163
|
currentMessages.push({ role: 'user', content: `<tool_result>\n${toolResultContent}\n</tool_result>` });
|
|
1132
1164
|
} catch (error) {
|
|
1133
1165
|
console.error(`Error executing MCP tool ${toolName}:`, error);
|
|
1134
1166
|
const toolResultContent = `Error executing MCP tool ${toolName}: ${error.message}`;
|
|
1135
|
-
|
|
1167
|
+
|
|
1168
|
+
// Log MCP tool error in debug mode
|
|
1169
|
+
if (this.debug) {
|
|
1170
|
+
console.error(`[DEBUG] ========================================`);
|
|
1171
|
+
console.error(`[DEBUG] MCP tool '${toolName}' failed with error:`);
|
|
1172
|
+
console.error(`[DEBUG] ${error.message}`);
|
|
1173
|
+
console.error(`[DEBUG] ========================================\n`);
|
|
1174
|
+
}
|
|
1175
|
+
|
|
1136
1176
|
currentMessages.push({ role: 'user', content: `<tool_result>\n${toolResultContent}\n</tool_result>` });
|
|
1137
1177
|
}
|
|
1138
1178
|
} else if (this.toolImplementations[toolName]) {
|
|
1139
1179
|
// Execute native tool
|
|
1140
1180
|
try {
|
|
1141
1181
|
// Add sessionId and workingDirectory to params for tool execution
|
|
1142
|
-
const toolParams = {
|
|
1143
|
-
...params,
|
|
1182
|
+
const toolParams = {
|
|
1183
|
+
...params,
|
|
1144
1184
|
sessionId: this.sessionId,
|
|
1145
1185
|
workingDirectory: (this.allowedFolders && this.allowedFolders[0]) || process.cwd()
|
|
1146
1186
|
};
|
|
1147
|
-
|
|
1187
|
+
|
|
1188
|
+
// Log tool execution in debug mode
|
|
1189
|
+
if (this.debug) {
|
|
1190
|
+
console.error(`\n[DEBUG] ========================================`);
|
|
1191
|
+
console.error(`[DEBUG] Executing tool: ${toolName}`);
|
|
1192
|
+
console.error(`[DEBUG] Arguments:`);
|
|
1193
|
+
for (const [key, value] of Object.entries(params)) {
|
|
1194
|
+
const displayValue = typeof value === 'string' && value.length > 100
|
|
1195
|
+
? value.substring(0, 100) + '...'
|
|
1196
|
+
: value;
|
|
1197
|
+
console.error(`[DEBUG] ${key}: ${JSON.stringify(displayValue)}`);
|
|
1198
|
+
}
|
|
1199
|
+
console.error(`[DEBUG] ========================================\n`);
|
|
1200
|
+
}
|
|
1201
|
+
|
|
1148
1202
|
// Emit tool start event
|
|
1149
1203
|
this.events.emit('toolCall', {
|
|
1150
1204
|
timestamp: new Date().toISOString(),
|
|
@@ -1196,6 +1250,18 @@ When troubleshooting:
|
|
|
1196
1250
|
toolResult = await executeToolCall();
|
|
1197
1251
|
}
|
|
1198
1252
|
|
|
1253
|
+
// Log tool result in debug mode
|
|
1254
|
+
if (this.debug) {
|
|
1255
|
+
const resultPreview = typeof toolResult === 'string'
|
|
1256
|
+
? (toolResult.length > 500 ? toolResult.substring(0, 500) + '...' : toolResult)
|
|
1257
|
+
: (toolResult ? JSON.stringify(toolResult, null, 2).substring(0, 500) + '...' : 'No Result');
|
|
1258
|
+
console.error(`[DEBUG] ========================================`);
|
|
1259
|
+
console.error(`[DEBUG] Tool '${toolName}' completed successfully`);
|
|
1260
|
+
console.error(`[DEBUG] Result preview:`);
|
|
1261
|
+
console.error(resultPreview);
|
|
1262
|
+
console.error(`[DEBUG] ========================================\n`);
|
|
1263
|
+
}
|
|
1264
|
+
|
|
1199
1265
|
// Emit tool success event
|
|
1200
1266
|
this.events.emit('toolCall', {
|
|
1201
1267
|
timestamp: new Date().toISOString(),
|
|
@@ -1206,8 +1272,16 @@ When troubleshooting:
|
|
|
1206
1272
|
: (toolResult ? JSON.stringify(toolResult).substring(0, 200) + '...' : 'No Result'),
|
|
1207
1273
|
status: 'completed'
|
|
1208
1274
|
});
|
|
1209
|
-
|
|
1275
|
+
|
|
1210
1276
|
} catch (toolError) {
|
|
1277
|
+
// Log tool error in debug mode
|
|
1278
|
+
if (this.debug) {
|
|
1279
|
+
console.error(`[DEBUG] ========================================`);
|
|
1280
|
+
console.error(`[DEBUG] Tool '${toolName}' failed with error:`);
|
|
1281
|
+
console.error(`[DEBUG] ${toolError.message}`);
|
|
1282
|
+
console.error(`[DEBUG] ========================================\n`);
|
|
1283
|
+
}
|
|
1284
|
+
|
|
1211
1285
|
// Emit tool error event
|
|
1212
1286
|
this.events.emit('toolCall', {
|
|
1213
1287
|
timestamp: new Date().toISOString(),
|
|
@@ -1262,7 +1336,23 @@ When troubleshooting:
|
|
|
1262
1336
|
}
|
|
1263
1337
|
}
|
|
1264
1338
|
} else {
|
|
1265
|
-
// No tool call found
|
|
1339
|
+
// No tool call found
|
|
1340
|
+
// Special case: If response contains a mermaid code block and no schema was provided,
|
|
1341
|
+
// treat it as a valid completion (for mermaid diagram fixing workflow)
|
|
1342
|
+
const hasMermaidCodeBlock = /```mermaid\s*\n[\s\S]*?\n```/.test(assistantResponseContent);
|
|
1343
|
+
const hasNoSchemaOrTools = !options.schema && validTools.length === 0;
|
|
1344
|
+
|
|
1345
|
+
if (hasMermaidCodeBlock && hasNoSchemaOrTools) {
|
|
1346
|
+
// Accept mermaid code block as final answer for diagram fixing
|
|
1347
|
+
finalResult = assistantResponseContent;
|
|
1348
|
+
completionAttempted = true;
|
|
1349
|
+
if (this.debug) {
|
|
1350
|
+
console.error(`[DEBUG] Accepting mermaid code block as valid completion (no schema, no tools)`);
|
|
1351
|
+
}
|
|
1352
|
+
break;
|
|
1353
|
+
}
|
|
1354
|
+
|
|
1355
|
+
// Add assistant response and ask for tool usage
|
|
1266
1356
|
currentMessages.push({ role: 'assistant', content: assistantResponseContent });
|
|
1267
1357
|
|
|
1268
1358
|
// Build appropriate reminder message based on whether schema is provided
|
package/src/agent/probeTool.js
CHANGED
|
@@ -213,45 +213,99 @@ export function createWrappedTools(baseTools) {
|
|
|
213
213
|
return wrappedTools;
|
|
214
214
|
}
|
|
215
215
|
|
|
216
|
-
// Simple file listing tool
|
|
216
|
+
// Simple file listing tool with ls-like formatted output
|
|
217
217
|
export const listFilesTool = {
|
|
218
218
|
execute: async (params) => {
|
|
219
219
|
const { directory = '.', workingDirectory } = params;
|
|
220
|
-
|
|
220
|
+
|
|
221
221
|
// Use the provided working directory, or fall back to process.cwd()
|
|
222
222
|
const baseCwd = workingDirectory || process.cwd();
|
|
223
|
-
|
|
223
|
+
|
|
224
224
|
// Security: Validate path to prevent traversal attacks
|
|
225
225
|
const secureBaseDir = path.resolve(baseCwd);
|
|
226
226
|
const targetDir = path.resolve(secureBaseDir, directory);
|
|
227
227
|
if (!targetDir.startsWith(secureBaseDir + path.sep) && targetDir !== secureBaseDir) {
|
|
228
228
|
throw new Error('Path traversal attempt detected. Access denied.');
|
|
229
229
|
}
|
|
230
|
-
|
|
230
|
+
|
|
231
|
+
const debug = process.env.DEBUG === '1';
|
|
232
|
+
|
|
233
|
+
if (debug) {
|
|
234
|
+
console.log(`[DEBUG] Listing files in directory: ${targetDir}`);
|
|
235
|
+
}
|
|
236
|
+
|
|
231
237
|
try {
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
238
|
+
// Read the directory contents
|
|
239
|
+
const files = await fsPromises.readdir(targetDir, { withFileTypes: true });
|
|
240
|
+
|
|
241
|
+
// Format size for human readability
|
|
242
|
+
const formatSize = (size) => {
|
|
243
|
+
if (size < 1024) return `${size}B`;
|
|
244
|
+
if (size < 1024 * 1024) return `${(size / 1024).toFixed(1)}K`;
|
|
245
|
+
if (size < 1024 * 1024 * 1024) return `${(size / (1024 * 1024)).toFixed(1)}M`;
|
|
246
|
+
return `${(size / (1024 * 1024 * 1024)).toFixed(1)}G`;
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
// Format the results as ls-style output
|
|
250
|
+
const entries = await Promise.all(files.map(async (file) => {
|
|
251
|
+
const isDirectory = file.isDirectory();
|
|
252
|
+
const fullPath = path.join(targetDir, file.name);
|
|
253
|
+
|
|
254
|
+
let size = 0;
|
|
255
|
+
try {
|
|
256
|
+
const stats = await fsPromises.stat(fullPath);
|
|
257
|
+
size = stats.size;
|
|
258
|
+
} catch (statError) {
|
|
259
|
+
if (debug) {
|
|
260
|
+
console.log(`[DEBUG] Could not stat file ${file.name}:`, statError.message);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
return {
|
|
265
|
+
name: file.name,
|
|
266
|
+
isDirectory,
|
|
267
|
+
size
|
|
268
|
+
};
|
|
269
|
+
}));
|
|
270
|
+
|
|
271
|
+
// Sort: directories first, then files, both alphabetically
|
|
272
|
+
entries.sort((a, b) => {
|
|
273
|
+
if (a.isDirectory && !b.isDirectory) return -1;
|
|
274
|
+
if (!a.isDirectory && b.isDirectory) return 1;
|
|
275
|
+
return a.name.localeCompare(b.name);
|
|
237
276
|
});
|
|
238
|
-
|
|
239
|
-
|
|
277
|
+
|
|
278
|
+
// Format entries
|
|
279
|
+
const formatted = entries.map(entry => {
|
|
280
|
+
const type = entry.isDirectory ? 'dir ' : 'file';
|
|
281
|
+
const sizeStr = formatSize(entry.size).padStart(8);
|
|
282
|
+
return `${type} ${sizeStr} ${entry.name}`;
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
if (debug) {
|
|
286
|
+
console.log(`[DEBUG] Found ${entries.length} files/directories in ${targetDir}`);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// Return as formatted text output
|
|
290
|
+
const header = `${targetDir}:\n`;
|
|
291
|
+
const output = header + formatted.join('\n');
|
|
292
|
+
|
|
293
|
+
return output;
|
|
240
294
|
} catch (error) {
|
|
241
295
|
throw new Error(`Failed to list files: ${error.message}`);
|
|
242
296
|
}
|
|
243
297
|
}
|
|
244
298
|
};
|
|
245
299
|
|
|
246
|
-
// Simple file search tool
|
|
300
|
+
// Simple file search tool with timeout protection
|
|
247
301
|
export const searchFilesTool = {
|
|
248
302
|
execute: async (params) => {
|
|
249
303
|
const { pattern, directory = '.', recursive = true, workingDirectory } = params;
|
|
250
|
-
|
|
304
|
+
|
|
251
305
|
if (!pattern) {
|
|
252
306
|
throw new Error('Pattern is required for file search');
|
|
253
307
|
}
|
|
254
|
-
|
|
308
|
+
|
|
255
309
|
// Security: Validate path to prevent traversal attacks
|
|
256
310
|
const baseCwd = workingDirectory || process.cwd();
|
|
257
311
|
const secureBaseDir = path.resolve(baseCwd);
|
|
@@ -259,19 +313,40 @@ export const searchFilesTool = {
|
|
|
259
313
|
if (!targetDir.startsWith(secureBaseDir + path.sep) && targetDir !== secureBaseDir) {
|
|
260
314
|
throw new Error('Path traversal attempt detected. Access denied.');
|
|
261
315
|
}
|
|
262
|
-
|
|
316
|
+
|
|
317
|
+
// Validate pattern complexity to prevent DoS
|
|
318
|
+
if (pattern.includes('**/**') || pattern.split('*').length > 10) {
|
|
319
|
+
throw new Error('Pattern too complex. Please use a simpler glob pattern.');
|
|
320
|
+
}
|
|
321
|
+
|
|
263
322
|
try {
|
|
264
323
|
const options = {
|
|
265
324
|
cwd: targetDir,
|
|
266
325
|
ignore: ['node_modules/**', '.git/**'],
|
|
267
326
|
absolute: false
|
|
268
327
|
};
|
|
269
|
-
|
|
328
|
+
|
|
270
329
|
if (!recursive) {
|
|
271
330
|
options.deep = 1;
|
|
272
331
|
}
|
|
273
|
-
|
|
274
|
-
|
|
332
|
+
|
|
333
|
+
// Create a timeout promise (10 seconds)
|
|
334
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
335
|
+
setTimeout(() => reject(new Error('Search operation timed out after 10 seconds')), 10000);
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
// Race glob against timeout
|
|
339
|
+
const files = await Promise.race([
|
|
340
|
+
glob(pattern, options),
|
|
341
|
+
timeoutPromise
|
|
342
|
+
]);
|
|
343
|
+
|
|
344
|
+
// Limit results to prevent memory issues
|
|
345
|
+
const maxResults = 1000;
|
|
346
|
+
if (files.length > maxResults) {
|
|
347
|
+
return files.slice(0, maxResults);
|
|
348
|
+
}
|
|
349
|
+
|
|
275
350
|
return files;
|
|
276
351
|
} catch (error) {
|
|
277
352
|
throw new Error(`Failed to search files: ${error.message}`);
|
package/src/agent/schemaUtils.js
CHANGED
|
@@ -813,7 +813,7 @@ When presented with a broken Mermaid diagram, analyze it thoroughly and provide
|
|
|
813
813
|
debug: this.options.debug,
|
|
814
814
|
tracer: this.options.tracer,
|
|
815
815
|
allowEdit: this.options.allowEdit,
|
|
816
|
-
maxIterations:
|
|
816
|
+
maxIterations: 10, // Allow more iterations for mermaid fixing to handle complex diagrams
|
|
817
817
|
disableMermaidValidation: true // CRITICAL: Disable mermaid validation in nested agent to prevent infinite recursion
|
|
818
818
|
});
|
|
819
819
|
}
|
package/src/index.js
CHANGED
|
@@ -34,6 +34,7 @@ import { searchTool, queryTool, extractTool, delegateTool } from './tools/vercel
|
|
|
34
34
|
import { bashTool } from './tools/bash.js';
|
|
35
35
|
import { ProbeAgent } from './agent/ProbeAgent.js';
|
|
36
36
|
import { SimpleTelemetry, SimpleAppTracer, initializeSimpleTelemetryFromOptions } from './agent/simpleTelemetry.js';
|
|
37
|
+
import { listFilesToolInstance, searchFilesToolInstance } from './agent/probeTool.js';
|
|
37
38
|
|
|
38
39
|
export {
|
|
39
40
|
search,
|
|
@@ -57,6 +58,9 @@ export {
|
|
|
57
58
|
extractTool,
|
|
58
59
|
delegateTool,
|
|
59
60
|
bashTool,
|
|
61
|
+
// Export tool instances
|
|
62
|
+
listFilesToolInstance,
|
|
63
|
+
searchFilesToolInstance,
|
|
60
64
|
// Export schemas
|
|
61
65
|
searchSchema,
|
|
62
66
|
querySchema,
|