@probelabs/probe 0.6.0-rc105 → 0.6.0-rc107
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 +12 -1
- package/build/agent/bashCommandUtils.js +200 -0
- package/build/agent/bashDefaults.js +202 -0
- package/build/agent/bashExecutor.js +319 -0
- package/build/agent/bashPermissions.js +228 -0
- package/build/agent/index.js +1523 -145
- package/build/agent/probeTool.js +9 -0
- package/build/agent/schemaUtils.js +89 -12
- package/build/agent/tools.js +13 -1
- package/build/index.js +6 -0
- package/build/tools/bash.js +216 -0
- package/build/tools/common.js +64 -0
- package/build/tools/index.js +6 -0
- package/cjs/agent/ProbeAgent.cjs +1479 -167
- package/cjs/index.cjs +1554 -176
- package/package.json +9 -2
- package/src/agent/ProbeAgent.js +12 -1
- package/src/agent/bashCommandUtils.js +200 -0
- package/src/agent/bashDefaults.js +202 -0
- package/src/agent/bashExecutor.js +319 -0
- package/src/agent/bashPermissions.js +228 -0
- package/src/agent/index.js +83 -2
- package/src/agent/probeTool.js +9 -0
- package/src/agent/schemaUtils.js +89 -12
- package/src/agent/tools.js +13 -1
- package/src/index.js +6 -0
- package/src/tools/bash.js +216 -0
- package/src/tools/common.js +64 -0
- package/src/tools/index.js +6 -0
package/src/agent/schemaUtils.js
CHANGED
|
@@ -14,6 +14,7 @@ const HTML_ENTITY_MAP = {
|
|
|
14
14
|
'&': '&',
|
|
15
15
|
'"': '"',
|
|
16
16
|
''': "'",
|
|
17
|
+
''': "'", // Also handle XML/HTML5 apostrophe entity
|
|
17
18
|
' ': ' '
|
|
18
19
|
};
|
|
19
20
|
|
|
@@ -1183,16 +1184,35 @@ export async function validateAndFixMermaidResponse(response, options = {}) {
|
|
|
1183
1184
|
// Enhanced auto-fixing for square bracket nodes [...]
|
|
1184
1185
|
if (trimmedLine.match(/\[[^\]]*\]/)) {
|
|
1185
1186
|
modifiedLine = modifiedLine.replace(/\[([^\]]*)\]/g, (match, content) => {
|
|
1186
|
-
//
|
|
1187
|
+
// Check if already properly quoted with outer quotes
|
|
1187
1188
|
if (content.trim().startsWith('"') && content.trim().endsWith('"')) {
|
|
1189
|
+
// Extract the inner content (between the outer quotes)
|
|
1190
|
+
const innerContent = content.trim().slice(1, -1);
|
|
1191
|
+
// Check if inner content has unescaped quotes that need escaping
|
|
1192
|
+
if (innerContent.includes('"') || innerContent.includes("'")) {
|
|
1193
|
+
wasFixed = true;
|
|
1194
|
+
// Decode any existing HTML entities first, then re-encode ALL quotes
|
|
1195
|
+
const decodedContent = decodeHtmlEntities(innerContent);
|
|
1196
|
+
const safeContent = decodedContent
|
|
1197
|
+
.replace(/"/g, '"') // Replace ALL double quotes with HTML entity
|
|
1198
|
+
.replace(/'/g, '''); // Replace ALL single quotes with HTML entity
|
|
1199
|
+
return `["${safeContent}"]`;
|
|
1200
|
+
}
|
|
1188
1201
|
return match;
|
|
1189
1202
|
}
|
|
1190
1203
|
|
|
1191
1204
|
// Check if content needs quoting (contains problematic patterns)
|
|
1192
1205
|
if (needsQuoting(content)) {
|
|
1193
1206
|
wasFixed = true;
|
|
1194
|
-
//
|
|
1195
|
-
|
|
1207
|
+
// Use HTML entities for quotes as per Mermaid best practices:
|
|
1208
|
+
// - GitHub doesn't support single quotes in node labels (causes 'got PS' error)
|
|
1209
|
+
// - HTML entities are the official way to escape quotes in Mermaid
|
|
1210
|
+
// - Always use double quotes with square brackets ["..."] for node labels
|
|
1211
|
+
// IMPORTANT: Decode any existing HTML entities first to avoid double-encoding
|
|
1212
|
+
const decodedContent = decodeHtmlEntities(content);
|
|
1213
|
+
const safeContent = decodedContent
|
|
1214
|
+
.replace(/"/g, '"') // Replace double quotes with HTML entity
|
|
1215
|
+
.replace(/'/g, '''); // Replace single quotes with HTML entity
|
|
1196
1216
|
return `["${safeContent}"]`;
|
|
1197
1217
|
}
|
|
1198
1218
|
|
|
@@ -1203,16 +1223,35 @@ export async function validateAndFixMermaidResponse(response, options = {}) {
|
|
|
1203
1223
|
// Enhanced auto-fixing for diamond nodes {...}
|
|
1204
1224
|
if (trimmedLine.match(/\{[^{}]*\}/)) {
|
|
1205
1225
|
modifiedLine = modifiedLine.replace(/\{([^{}]*)\}/g, (match, content) => {
|
|
1206
|
-
//
|
|
1226
|
+
// Check if already properly quoted with outer quotes
|
|
1207
1227
|
if (content.trim().startsWith('"') && content.trim().endsWith('"')) {
|
|
1228
|
+
// Extract the inner content (between the outer quotes)
|
|
1229
|
+
const innerContent = content.trim().slice(1, -1);
|
|
1230
|
+
// Check if inner content has unescaped quotes that need escaping
|
|
1231
|
+
if (innerContent.includes('"') || innerContent.includes("'")) {
|
|
1232
|
+
wasFixed = true;
|
|
1233
|
+
// Decode any existing HTML entities first, then re-encode ALL quotes
|
|
1234
|
+
const decodedContent = decodeHtmlEntities(innerContent);
|
|
1235
|
+
const safeContent = decodedContent
|
|
1236
|
+
.replace(/"/g, '"') // Replace ALL double quotes with HTML entity
|
|
1237
|
+
.replace(/'/g, '''); // Replace ALL single quotes with HTML entity
|
|
1238
|
+
return `{"${safeContent}"}`;
|
|
1239
|
+
}
|
|
1208
1240
|
return match;
|
|
1209
1241
|
}
|
|
1210
1242
|
|
|
1211
1243
|
// Check if content needs quoting (contains problematic patterns)
|
|
1212
1244
|
if (needsQuoting(content)) {
|
|
1213
1245
|
wasFixed = true;
|
|
1214
|
-
//
|
|
1215
|
-
|
|
1246
|
+
// Use HTML entities for quotes as per Mermaid best practices:
|
|
1247
|
+
// - GitHub doesn't support single quotes in node labels (causes 'got PS' error)
|
|
1248
|
+
// - HTML entities are the official way to escape quotes in Mermaid
|
|
1249
|
+
// - Always use double quotes with curly brackets {"..."} for diamond nodes
|
|
1250
|
+
// IMPORTANT: Decode any existing HTML entities first to avoid double-encoding
|
|
1251
|
+
const decodedContent = decodeHtmlEntities(content);
|
|
1252
|
+
const safeContent = decodedContent
|
|
1253
|
+
.replace(/"/g, '"') // Replace double quotes with HTML entity
|
|
1254
|
+
.replace(/'/g, '''); // Replace single quotes with HTML entity
|
|
1216
1255
|
return `{"${safeContent}"}`;
|
|
1217
1256
|
}
|
|
1218
1257
|
|
|
@@ -1438,16 +1477,35 @@ export async function validateAndFixMermaidResponse(response, options = {}) {
|
|
|
1438
1477
|
// Look for any node labels that contain special characters and aren't already quoted
|
|
1439
1478
|
if (trimmedLine.match(/\[[^\]]*\]/)) {
|
|
1440
1479
|
modifiedLine = modifiedLine.replace(/\[([^\]]*)\]/g, (match, content) => {
|
|
1441
|
-
//
|
|
1480
|
+
// Check if already properly quoted with outer quotes
|
|
1442
1481
|
if (content.trim().startsWith('"') && content.trim().endsWith('"')) {
|
|
1482
|
+
// Extract the inner content (between the outer quotes)
|
|
1483
|
+
const innerContent = content.trim().slice(1, -1);
|
|
1484
|
+
// Check if inner content has unescaped quotes that need escaping
|
|
1485
|
+
if (innerContent.includes('"') || innerContent.includes("'")) {
|
|
1486
|
+
wasFixed = true;
|
|
1487
|
+
// Decode any existing HTML entities first, then re-encode ALL quotes
|
|
1488
|
+
const decodedContent = decodeHtmlEntities(innerContent);
|
|
1489
|
+
const safeContent = decodedContent
|
|
1490
|
+
.replace(/"/g, '"') // Replace ALL double quotes with HTML entity
|
|
1491
|
+
.replace(/'/g, '''); // Replace ALL single quotes with HTML entity
|
|
1492
|
+
return `["${safeContent}"]`;
|
|
1493
|
+
}
|
|
1443
1494
|
return match;
|
|
1444
1495
|
}
|
|
1445
1496
|
|
|
1446
1497
|
// Check if content needs quoting (contains problematic patterns)
|
|
1447
1498
|
if (needsQuoting(content)) {
|
|
1448
1499
|
wasFixed = true;
|
|
1449
|
-
//
|
|
1450
|
-
|
|
1500
|
+
// Use HTML entities for quotes as per Mermaid best practices:
|
|
1501
|
+
// - GitHub doesn't support single quotes in node labels (causes 'got PS' error)
|
|
1502
|
+
// - HTML entities are the official way to escape quotes in Mermaid
|
|
1503
|
+
// - Always use double quotes with square brackets ["..."] for node labels
|
|
1504
|
+
// IMPORTANT: Decode any existing HTML entities first to avoid double-encoding
|
|
1505
|
+
const decodedContent = decodeHtmlEntities(content);
|
|
1506
|
+
const safeContent = decodedContent
|
|
1507
|
+
.replace(/"/g, '"') // Replace double quotes with HTML entity
|
|
1508
|
+
.replace(/'/g, '''); // Replace single quotes with HTML entity
|
|
1451
1509
|
return `["${safeContent}"]`;
|
|
1452
1510
|
}
|
|
1453
1511
|
|
|
@@ -1458,16 +1516,35 @@ export async function validateAndFixMermaidResponse(response, options = {}) {
|
|
|
1458
1516
|
// Enhanced auto-fixing for diamond nodes {...}
|
|
1459
1517
|
if (trimmedLine.match(/\{[^{}]*\}/)) {
|
|
1460
1518
|
modifiedLine = modifiedLine.replace(/\{([^{}]*)\}/g, (match, content) => {
|
|
1461
|
-
//
|
|
1519
|
+
// Check if already properly quoted with outer quotes
|
|
1462
1520
|
if (content.trim().startsWith('"') && content.trim().endsWith('"')) {
|
|
1521
|
+
// Extract the inner content (between the outer quotes)
|
|
1522
|
+
const innerContent = content.trim().slice(1, -1);
|
|
1523
|
+
// Check if inner content has unescaped quotes that need escaping
|
|
1524
|
+
if (innerContent.includes('"') || innerContent.includes("'")) {
|
|
1525
|
+
wasFixed = true;
|
|
1526
|
+
// Decode any existing HTML entities first, then re-encode ALL quotes
|
|
1527
|
+
const decodedContent = decodeHtmlEntities(innerContent);
|
|
1528
|
+
const safeContent = decodedContent
|
|
1529
|
+
.replace(/"/g, '"') // Replace ALL double quotes with HTML entity
|
|
1530
|
+
.replace(/'/g, '''); // Replace ALL single quotes with HTML entity
|
|
1531
|
+
return `{"${safeContent}"}`;
|
|
1532
|
+
}
|
|
1463
1533
|
return match;
|
|
1464
1534
|
}
|
|
1465
1535
|
|
|
1466
1536
|
// Check if content needs quoting (contains problematic patterns)
|
|
1467
1537
|
if (needsQuoting(content)) {
|
|
1468
1538
|
wasFixed = true;
|
|
1469
|
-
//
|
|
1470
|
-
|
|
1539
|
+
// Use HTML entities for quotes as per Mermaid best practices:
|
|
1540
|
+
// - GitHub doesn't support single quotes in node labels (causes 'got PS' error)
|
|
1541
|
+
// - HTML entities are the official way to escape quotes in Mermaid
|
|
1542
|
+
// - Always use double quotes with curly brackets {"..."} for diamond nodes
|
|
1543
|
+
// IMPORTANT: Decode any existing HTML entities first to avoid double-encoding
|
|
1544
|
+
const decodedContent = decodeHtmlEntities(content);
|
|
1545
|
+
const safeContent = decodedContent
|
|
1546
|
+
.replace(/"/g, '"') // Replace double quotes with HTML entity
|
|
1547
|
+
.replace(/'/g, '''); // Replace single quotes with HTML entity
|
|
1471
1548
|
return `{"${safeContent}"}`;
|
|
1472
1549
|
}
|
|
1473
1550
|
|
package/src/agent/tools.js
CHANGED
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
queryTool,
|
|
5
5
|
extractTool,
|
|
6
6
|
delegateTool,
|
|
7
|
+
bashTool,
|
|
7
8
|
DEFAULT_SYSTEM_MESSAGE,
|
|
8
9
|
attemptCompletionSchema,
|
|
9
10
|
attemptCompletionToolDefinition,
|
|
@@ -11,10 +12,12 @@ import {
|
|
|
11
12
|
querySchema,
|
|
12
13
|
extractSchema,
|
|
13
14
|
delegateSchema,
|
|
15
|
+
bashSchema,
|
|
14
16
|
searchToolDefinition,
|
|
15
17
|
queryToolDefinition,
|
|
16
18
|
extractToolDefinition,
|
|
17
19
|
delegateToolDefinition,
|
|
20
|
+
bashToolDefinition,
|
|
18
21
|
parseXmlToolCall
|
|
19
22
|
} from '../index.js';
|
|
20
23
|
import { randomUUID } from 'crypto';
|
|
@@ -22,12 +25,19 @@ import { processXmlWithThinkingAndRecovery } from './xmlParsingUtils.js';
|
|
|
22
25
|
|
|
23
26
|
// Create configured tool instances
|
|
24
27
|
export function createTools(configOptions) {
|
|
25
|
-
|
|
28
|
+
const tools = {
|
|
26
29
|
searchTool: searchTool(configOptions),
|
|
27
30
|
queryTool: queryTool(configOptions),
|
|
28
31
|
extractTool: extractTool(configOptions),
|
|
29
32
|
delegateTool: delegateTool(configOptions)
|
|
30
33
|
};
|
|
34
|
+
|
|
35
|
+
// Add bash tool if enabled
|
|
36
|
+
if (configOptions.enableBash) {
|
|
37
|
+
tools.bashTool = bashTool(configOptions);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return tools;
|
|
31
41
|
}
|
|
32
42
|
|
|
33
43
|
// Export tool definitions and schemas
|
|
@@ -37,11 +47,13 @@ export {
|
|
|
37
47
|
querySchema,
|
|
38
48
|
extractSchema,
|
|
39
49
|
delegateSchema,
|
|
50
|
+
bashSchema,
|
|
40
51
|
attemptCompletionSchema,
|
|
41
52
|
searchToolDefinition,
|
|
42
53
|
queryToolDefinition,
|
|
43
54
|
extractToolDefinition,
|
|
44
55
|
delegateToolDefinition,
|
|
56
|
+
bashToolDefinition,
|
|
45
57
|
attemptCompletionToolDefinition,
|
|
46
58
|
parseXmlToolCall
|
|
47
59
|
};
|
package/src/index.js
CHANGED
|
@@ -21,14 +21,17 @@ import {
|
|
|
21
21
|
extractSchema,
|
|
22
22
|
delegateSchema,
|
|
23
23
|
attemptCompletionSchema,
|
|
24
|
+
bashSchema,
|
|
24
25
|
searchToolDefinition,
|
|
25
26
|
queryToolDefinition,
|
|
26
27
|
extractToolDefinition,
|
|
27
28
|
delegateToolDefinition,
|
|
28
29
|
attemptCompletionToolDefinition,
|
|
30
|
+
bashToolDefinition,
|
|
29
31
|
parseXmlToolCall
|
|
30
32
|
} from './tools/common.js';
|
|
31
33
|
import { searchTool, queryTool, extractTool, delegateTool } from './tools/vercel.js';
|
|
34
|
+
import { bashTool } from './tools/bash.js';
|
|
32
35
|
import { ProbeAgent } from './agent/ProbeAgent.js';
|
|
33
36
|
import { SimpleTelemetry, SimpleAppTracer, initializeSimpleTelemetryFromOptions } from './agent/simpleTelemetry.js';
|
|
34
37
|
|
|
@@ -53,18 +56,21 @@ export {
|
|
|
53
56
|
queryTool,
|
|
54
57
|
extractTool,
|
|
55
58
|
delegateTool,
|
|
59
|
+
bashTool,
|
|
56
60
|
// Export schemas
|
|
57
61
|
searchSchema,
|
|
58
62
|
querySchema,
|
|
59
63
|
extractSchema,
|
|
60
64
|
delegateSchema,
|
|
61
65
|
attemptCompletionSchema,
|
|
66
|
+
bashSchema,
|
|
62
67
|
// Export tool definitions
|
|
63
68
|
searchToolDefinition,
|
|
64
69
|
queryToolDefinition,
|
|
65
70
|
extractToolDefinition,
|
|
66
71
|
delegateToolDefinition,
|
|
67
72
|
attemptCompletionToolDefinition,
|
|
73
|
+
bashToolDefinition,
|
|
68
74
|
// Export parser function
|
|
69
75
|
parseXmlToolCall
|
|
70
76
|
};
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bash command execution tool for Vercel AI SDK
|
|
3
|
+
* @module tools/bash
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { tool } from 'ai';
|
|
7
|
+
import { resolve } from 'path';
|
|
8
|
+
import { BashPermissionChecker } from '../agent/bashPermissions.js';
|
|
9
|
+
import { executeBashCommand, formatExecutionResult, validateExecutionOptions } from '../agent/bashExecutor.js';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Bash tool generator
|
|
13
|
+
*
|
|
14
|
+
* @param {Object} [options] - Configuration options
|
|
15
|
+
* @param {Object} [options.bashConfig] - Bash-specific configuration
|
|
16
|
+
* @param {string[]} [options.bashConfig.allow] - Additional allow patterns
|
|
17
|
+
* @param {string[]} [options.bashConfig.deny] - Additional deny patterns
|
|
18
|
+
* @param {boolean} [options.bashConfig.disableDefaultAllow] - Disable default allow list
|
|
19
|
+
* @param {boolean} [options.bashConfig.disableDefaultDeny] - Disable default deny list
|
|
20
|
+
* @param {number} [options.bashConfig.timeout=120000] - Command timeout in milliseconds
|
|
21
|
+
* @param {string} [options.bashConfig.workingDirectory] - Default working directory
|
|
22
|
+
* @param {Object} [options.bashConfig.env={}] - Default environment variables
|
|
23
|
+
* @param {number} [options.bashConfig.maxBuffer] - Maximum output buffer size
|
|
24
|
+
* @param {boolean} [options.debug=false] - Enable debug logging
|
|
25
|
+
* @param {string} [options.defaultPath] - Default working directory from probe config
|
|
26
|
+
* @param {string[]} [options.allowedFolders] - Allowed directories for execution
|
|
27
|
+
* @returns {Object} Configured bash tool
|
|
28
|
+
*/
|
|
29
|
+
export const bashTool = (options = {}) => {
|
|
30
|
+
const {
|
|
31
|
+
bashConfig = {},
|
|
32
|
+
debug = false,
|
|
33
|
+
defaultPath,
|
|
34
|
+
allowedFolders = []
|
|
35
|
+
} = options;
|
|
36
|
+
|
|
37
|
+
// Create permission checker
|
|
38
|
+
const permissionChecker = new BashPermissionChecker({
|
|
39
|
+
allow: bashConfig.allow,
|
|
40
|
+
deny: bashConfig.deny,
|
|
41
|
+
disableDefaultAllow: bashConfig.disableDefaultAllow,
|
|
42
|
+
disableDefaultDeny: bashConfig.disableDefaultDeny,
|
|
43
|
+
debug
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
// Determine default working directory
|
|
47
|
+
const getDefaultWorkingDirectory = () => {
|
|
48
|
+
if (bashConfig.workingDirectory) {
|
|
49
|
+
return bashConfig.workingDirectory;
|
|
50
|
+
}
|
|
51
|
+
if (defaultPath) {
|
|
52
|
+
return defaultPath;
|
|
53
|
+
}
|
|
54
|
+
if (allowedFolders && allowedFolders.length > 0) {
|
|
55
|
+
return allowedFolders[0];
|
|
56
|
+
}
|
|
57
|
+
return process.cwd();
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
return tool({
|
|
61
|
+
name: 'bash',
|
|
62
|
+
description: `Execute bash commands for system exploration and development tasks.
|
|
63
|
+
|
|
64
|
+
Security: This tool has built-in security with allow/deny lists. By default, only safe read-only commands are allowed for code exploration.
|
|
65
|
+
|
|
66
|
+
Parameters:
|
|
67
|
+
- command: (required) The bash command to execute
|
|
68
|
+
- workingDirectory: (optional) Directory to execute command in
|
|
69
|
+
- timeout: (optional) Command timeout in milliseconds
|
|
70
|
+
- env: (optional) Additional environment variables
|
|
71
|
+
|
|
72
|
+
Examples of allowed commands by default:
|
|
73
|
+
- File exploration: ls, cat, head, tail, find, grep
|
|
74
|
+
- Git operations: git status, git log, git diff, git show
|
|
75
|
+
- Package info: npm list, pip list, cargo --version
|
|
76
|
+
- System info: whoami, pwd, uname, date
|
|
77
|
+
|
|
78
|
+
Dangerous commands are blocked by default (rm -rf, sudo, npm install, etc.)`,
|
|
79
|
+
|
|
80
|
+
inputSchema: {
|
|
81
|
+
type: 'object',
|
|
82
|
+
properties: {
|
|
83
|
+
command: {
|
|
84
|
+
type: 'string',
|
|
85
|
+
description: 'The bash command to execute'
|
|
86
|
+
},
|
|
87
|
+
workingDirectory: {
|
|
88
|
+
type: 'string',
|
|
89
|
+
description: 'Directory to execute the command in (optional)'
|
|
90
|
+
},
|
|
91
|
+
timeout: {
|
|
92
|
+
type: 'number',
|
|
93
|
+
description: 'Command timeout in milliseconds (optional)',
|
|
94
|
+
minimum: 1000,
|
|
95
|
+
maximum: 600000
|
|
96
|
+
},
|
|
97
|
+
env: {
|
|
98
|
+
type: 'object',
|
|
99
|
+
description: 'Additional environment variables (optional)',
|
|
100
|
+
additionalProperties: {
|
|
101
|
+
type: 'string'
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
required: ['command'],
|
|
106
|
+
additionalProperties: false
|
|
107
|
+
},
|
|
108
|
+
|
|
109
|
+
execute: async ({ command, workingDirectory, timeout, env }) => {
|
|
110
|
+
try {
|
|
111
|
+
// Validate command
|
|
112
|
+
if (command === null || command === undefined || typeof command !== 'string') {
|
|
113
|
+
return 'Error: Command is required and must be a string';
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (command.trim().length === 0) {
|
|
117
|
+
return 'Error: Command cannot be empty';
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Check permissions
|
|
121
|
+
const permissionResult = permissionChecker.check(command.trim());
|
|
122
|
+
if (!permissionResult.allowed) {
|
|
123
|
+
if (debug) {
|
|
124
|
+
console.log(`[BashTool] Permission denied for command: "${command}"`);
|
|
125
|
+
console.log(`[BashTool] Reason: ${permissionResult.reason}`);
|
|
126
|
+
}
|
|
127
|
+
return `Permission denied: ${permissionResult.reason}
|
|
128
|
+
|
|
129
|
+
This command is not allowed by the current security policy.
|
|
130
|
+
|
|
131
|
+
Common reasons:
|
|
132
|
+
1. The command is in the deny list (potentially dangerous)
|
|
133
|
+
2. The command is not in the allow list (not a recognized safe command)
|
|
134
|
+
|
|
135
|
+
If you believe this command should be allowed, you can:
|
|
136
|
+
- Use the --bash-allow option to add specific patterns
|
|
137
|
+
- Use the --no-default-bash-deny flag to remove default restrictions (not recommended)
|
|
138
|
+
|
|
139
|
+
For code exploration, try these safe alternatives:
|
|
140
|
+
- ls, cat, head, tail for file operations
|
|
141
|
+
- find, grep, rg for searching
|
|
142
|
+
- git status, git log, git show for git operations
|
|
143
|
+
- npm list, pip list for package information`;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Determine working directory
|
|
147
|
+
const workingDir = workingDirectory || getDefaultWorkingDirectory();
|
|
148
|
+
|
|
149
|
+
// Validate working directory is within allowed folders if specified
|
|
150
|
+
if (allowedFolders && allowedFolders.length > 0) {
|
|
151
|
+
const resolvedWorkingDir = resolve(workingDir);
|
|
152
|
+
const isAllowed = allowedFolders.some(folder => {
|
|
153
|
+
const resolvedFolder = resolve(folder);
|
|
154
|
+
return resolvedWorkingDir.startsWith(resolvedFolder);
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
if (!isAllowed) {
|
|
158
|
+
return `Error: Working directory "${workingDir}" is not within allowed folders: ${allowedFolders.join(', ')}`;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Prepare execution options
|
|
163
|
+
const executionOptions = {
|
|
164
|
+
workingDirectory: workingDir,
|
|
165
|
+
timeout: timeout || bashConfig.timeout || 120000,
|
|
166
|
+
env: { ...bashConfig.env, ...env },
|
|
167
|
+
maxBuffer: bashConfig.maxBuffer,
|
|
168
|
+
debug
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
// Validate execution options
|
|
172
|
+
const validation = validateExecutionOptions(executionOptions);
|
|
173
|
+
if (!validation.valid) {
|
|
174
|
+
return `Error: Invalid execution options: ${validation.errors.join(', ')}`;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (validation.warnings.length > 0 && debug) {
|
|
178
|
+
console.log('[BashTool] Warnings:', validation.warnings);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if (debug) {
|
|
182
|
+
console.log(`[BashTool] Executing command: "${command}"`);
|
|
183
|
+
console.log(`[BashTool] Working directory: "${workingDir}"`);
|
|
184
|
+
console.log(`[BashTool] Timeout: ${executionOptions.timeout}ms`);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Execute command
|
|
188
|
+
const result = await executeBashCommand(command.trim(), executionOptions);
|
|
189
|
+
|
|
190
|
+
if (debug) {
|
|
191
|
+
console.log(`[BashTool] Command completed - Success: ${result.success}, Duration: ${result.duration}ms`);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Format and return result
|
|
195
|
+
const formattedResult = formatExecutionResult(result, debug);
|
|
196
|
+
|
|
197
|
+
// Add metadata for failed commands
|
|
198
|
+
if (!result.success) {
|
|
199
|
+
let errorInfo = `\n\nCommand failed with exit code ${result.exitCode}`;
|
|
200
|
+
if (result.killed) {
|
|
201
|
+
errorInfo += ` (${result.error})`;
|
|
202
|
+
}
|
|
203
|
+
return formattedResult + errorInfo;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
return formattedResult;
|
|
207
|
+
|
|
208
|
+
} catch (error) {
|
|
209
|
+
if (debug) {
|
|
210
|
+
console.error('[BashTool] Execution error:', error);
|
|
211
|
+
}
|
|
212
|
+
return `Error executing bash command: ${error.message}`;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
});
|
|
216
|
+
};
|
package/src/tools/common.js
CHANGED
|
@@ -37,6 +37,13 @@ export const delegateSchema = z.object({
|
|
|
37
37
|
task: z.string().describe('The task to delegate to a subagent. Be specific about what needs to be accomplished.')
|
|
38
38
|
});
|
|
39
39
|
|
|
40
|
+
export const bashSchema = z.object({
|
|
41
|
+
command: z.string().describe('The bash command to execute'),
|
|
42
|
+
workingDirectory: z.string().optional().describe('Directory to execute the command in (optional)'),
|
|
43
|
+
timeout: z.number().optional().describe('Command timeout in milliseconds (optional)'),
|
|
44
|
+
env: z.record(z.string()).optional().describe('Additional environment variables (optional)')
|
|
45
|
+
});
|
|
46
|
+
|
|
40
47
|
// Schema for the attempt_completion tool - flexible validation for direct XML response
|
|
41
48
|
export const attemptCompletionSchema = {
|
|
42
49
|
// Custom validation that requires result parameter but allows direct XML response
|
|
@@ -277,10 +284,67 @@ I have refactored the search module according to the requirements and verified t
|
|
|
277
284
|
</attempt_completion>
|
|
278
285
|
`;
|
|
279
286
|
|
|
287
|
+
export const bashToolDefinition = `
|
|
288
|
+
## bash
|
|
289
|
+
Description: Execute bash commands for system exploration and development tasks. This tool has built-in security with allow/deny lists. By default, only safe read-only commands are allowed for code exploration.
|
|
290
|
+
|
|
291
|
+
Parameters:
|
|
292
|
+
- command: (required) The bash command to execute
|
|
293
|
+
- workingDirectory: (optional) Directory to execute the command in
|
|
294
|
+
- timeout: (optional) Command timeout in milliseconds
|
|
295
|
+
- env: (optional) Additional environment variables as an object
|
|
296
|
+
|
|
297
|
+
Security: Commands are filtered through allow/deny lists for safety:
|
|
298
|
+
- Allowed by default: ls, cat, git status, npm list, find, grep, etc.
|
|
299
|
+
- Denied by default: rm -rf, sudo, npm install, dangerous system commands
|
|
300
|
+
|
|
301
|
+
Usage Examples:
|
|
302
|
+
|
|
303
|
+
<examples>
|
|
304
|
+
|
|
305
|
+
User: What files are in the src directory?
|
|
306
|
+
<bash>
|
|
307
|
+
<command>ls -la src/</command>
|
|
308
|
+
</bash>
|
|
309
|
+
|
|
310
|
+
User: Show me the git status
|
|
311
|
+
<bash>
|
|
312
|
+
<command>git status</command>
|
|
313
|
+
</bash>
|
|
314
|
+
|
|
315
|
+
User: Find all TypeScript files
|
|
316
|
+
<bash>
|
|
317
|
+
<command>find . -name "*.ts" -type f</command>
|
|
318
|
+
</bash>
|
|
319
|
+
|
|
320
|
+
User: Check installed npm packages
|
|
321
|
+
<bash>
|
|
322
|
+
<command>npm list --depth=0</command>
|
|
323
|
+
</bash>
|
|
324
|
+
|
|
325
|
+
User: Search for TODO comments in code
|
|
326
|
+
<bash>
|
|
327
|
+
<command>grep -r "TODO" src/</command>
|
|
328
|
+
</bash>
|
|
329
|
+
|
|
330
|
+
User: Show recent git commits
|
|
331
|
+
<bash>
|
|
332
|
+
<command>git log --oneline -10</command>
|
|
333
|
+
</bash>
|
|
334
|
+
|
|
335
|
+
User: Check system info
|
|
336
|
+
<bash>
|
|
337
|
+
<command>uname -a</command>
|
|
338
|
+
</bash>
|
|
339
|
+
|
|
340
|
+
</examples>
|
|
341
|
+
`;
|
|
342
|
+
|
|
280
343
|
export const searchDescription = 'Search code in the repository using Elasticsearch-like query syntax. Use this tool first for any code-related questions.';
|
|
281
344
|
export const queryDescription = 'Search code using ast-grep structural pattern matching. Use this tool to find specific code structures like functions, classes, or methods.';
|
|
282
345
|
export const extractDescription = 'Extract code blocks from files based on file paths and optional line numbers. Use this tool to see complete context after finding relevant files.';
|
|
283
346
|
export const delegateDescription = 'Automatically delegate big distinct tasks to specialized probe subagents within the agentic loop. Used by AI agents to break down complex requests into focused, parallel tasks.';
|
|
347
|
+
export const bashDescription = 'Execute bash commands for system exploration and development tasks. Secure by default with built-in allow/deny lists.';
|
|
284
348
|
|
|
285
349
|
// Valid tool names that should be parsed as tool calls
|
|
286
350
|
const DEFAULT_VALID_TOOLS = [
|
package/src/tools/index.js
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
// Export Vercel AI SDK tool generators
|
|
7
7
|
export { searchTool, queryTool, extractTool, delegateTool } from './vercel.js';
|
|
8
|
+
export { bashTool } from './bash.js';
|
|
8
9
|
|
|
9
10
|
// Export LangChain tools
|
|
10
11
|
export { createSearchTool, createQueryTool, createExtractTool } from './langchain.js';
|
|
@@ -15,8 +16,11 @@ export {
|
|
|
15
16
|
querySchema,
|
|
16
17
|
extractSchema,
|
|
17
18
|
delegateSchema,
|
|
19
|
+
bashSchema,
|
|
18
20
|
delegateDescription,
|
|
19
21
|
delegateToolDefinition,
|
|
22
|
+
bashDescription,
|
|
23
|
+
bashToolDefinition,
|
|
20
24
|
attemptCompletionSchema,
|
|
21
25
|
attemptCompletionToolDefinition
|
|
22
26
|
} from './common.js';
|
|
@@ -26,6 +30,7 @@ export { DEFAULT_SYSTEM_MESSAGE } from './system-message.js';
|
|
|
26
30
|
|
|
27
31
|
// For backward compatibility, create and export pre-configured tools
|
|
28
32
|
import { searchTool as searchToolGenerator, queryTool as queryToolGenerator, extractTool as extractToolGenerator, delegateTool as delegateToolGenerator } from './vercel.js';
|
|
33
|
+
import { bashTool as bashToolGenerator } from './bash.js';
|
|
29
34
|
import { DEFAULT_SYSTEM_MESSAGE } from './system-message.js';
|
|
30
35
|
|
|
31
36
|
// Create default tool instances (for backward compatibility)
|
|
@@ -34,6 +39,7 @@ const tools = {
|
|
|
34
39
|
queryTool: queryToolGenerator(),
|
|
35
40
|
extractTool: extractToolGenerator(),
|
|
36
41
|
delegateTool: delegateToolGenerator(),
|
|
42
|
+
bashTool: bashToolGenerator(),
|
|
37
43
|
DEFAULT_SYSTEM_MESSAGE
|
|
38
44
|
};
|
|
39
45
|
|