@probelabs/probe 0.6.0-rc229 → 0.6.0-rc230
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-rc230-aarch64-apple-darwin.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc230-aarch64-unknown-linux-musl.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc230-x86_64-apple-darwin.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc230-x86_64-pc-windows-msvc.zip +0 -0
- package/bin/binaries/probe-v0.6.0-rc230-x86_64-unknown-linux-musl.tar.gz +0 -0
- package/build/agent/ProbeAgent.js +123 -2
- package/build/agent/index.js +148 -4
- package/build/agent/mcp/config.js +7 -1
- package/build/agent/tools.js +4 -0
- package/build/index.js +4 -0
- package/build/tools/common.js +24 -0
- package/build/utils/path-validation.js +28 -2
- package/cjs/agent/ProbeAgent.cjs +8700 -10879
- package/cjs/index.cjs +8698 -10873
- package/package.json +1 -1
- package/src/agent/ProbeAgent.js +123 -2
- package/src/agent/mcp/config.js +7 -1
- package/src/agent/tools.js +4 -0
- package/src/index.js +4 -0
- package/src/tools/common.js +24 -0
- package/src/utils/path-validation.js +28 -2
- package/bin/binaries/probe-v0.6.0-rc229-aarch64-apple-darwin.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc229-aarch64-unknown-linux-musl.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc229-x86_64-apple-darwin.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc229-x86_64-pc-windows-msvc.zip +0 -0
- package/bin/binaries/probe-v0.6.0-rc229-x86_64-unknown-linux-musl.tar.gz +0 -0
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -58,6 +58,8 @@ import {
|
|
|
58
58
|
implementToolDefinition,
|
|
59
59
|
editToolDefinition,
|
|
60
60
|
createToolDefinition,
|
|
61
|
+
googleSearchToolDefinition,
|
|
62
|
+
urlContextToolDefinition,
|
|
61
63
|
attemptCompletionSchema,
|
|
62
64
|
parseXmlToolCallWithThinking
|
|
63
65
|
} from './tools.js';
|
|
@@ -404,6 +406,10 @@ export class ProbeAgent {
|
|
|
404
406
|
// Initialize the AI model
|
|
405
407
|
this.initializeModel();
|
|
406
408
|
|
|
409
|
+
// Gemini built-in tools (provider-defined, server-side)
|
|
410
|
+
// These are enabled automatically when the provider is Google
|
|
411
|
+
this._geminiToolsEnabled = this._initializeGeminiBuiltinTools();
|
|
412
|
+
|
|
407
413
|
// Note: MCP initialization is now done in initialize() method
|
|
408
414
|
// Constructor must remain synchronous for backward compatibility
|
|
409
415
|
}
|
|
@@ -1320,6 +1326,15 @@ export class ProbeAgent {
|
|
|
1320
1326
|
abortSignal: controller.signal
|
|
1321
1327
|
};
|
|
1322
1328
|
|
|
1329
|
+
// Strip Gemini provider-defined tools when falling back to non-Google provider
|
|
1330
|
+
// These tools have no execute function and would cause errors on other providers
|
|
1331
|
+
if (config.provider !== 'google' && fallbackOptions.tools) {
|
|
1332
|
+
delete fallbackOptions.tools;
|
|
1333
|
+
if (this.debug) {
|
|
1334
|
+
console.error(`[DEBUG] Stripped Gemini built-in tools for fallback to ${config.provider} provider`);
|
|
1335
|
+
}
|
|
1336
|
+
}
|
|
1337
|
+
|
|
1323
1338
|
const providerRetryManager = new RetryManager({
|
|
1324
1339
|
maxRetries: config.maxRetries ?? this.retryConfig.maxRetries ?? 3,
|
|
1325
1340
|
initialDelay: this.retryConfig.initialDelay ?? 1000,
|
|
@@ -1442,6 +1457,83 @@ export class ProbeAgent {
|
|
|
1442
1457
|
}
|
|
1443
1458
|
}
|
|
1444
1459
|
|
|
1460
|
+
/**
|
|
1461
|
+
* Initialize Gemini built-in tools (gemini_google_search, gemini_url_context).
|
|
1462
|
+
* These are provider-defined tools that execute server-side on Google's infrastructure.
|
|
1463
|
+
* They are only available when the provider is Google Gemini.
|
|
1464
|
+
* @returns {{ googleSearch: boolean, urlContext: boolean }} Which tools were enabled
|
|
1465
|
+
* @private
|
|
1466
|
+
*/
|
|
1467
|
+
_initializeGeminiBuiltinTools() {
|
|
1468
|
+
const isToolAllowed = (toolName) => this.allowedTools.isEnabled(toolName);
|
|
1469
|
+
const result = { googleSearch: false, urlContext: false };
|
|
1470
|
+
|
|
1471
|
+
if (this.apiType !== 'google') {
|
|
1472
|
+
// Log info about unavailability for non-Google providers
|
|
1473
|
+
if (isToolAllowed('gemini_google_search') || isToolAllowed('gemini_url_context')) {
|
|
1474
|
+
if (this.debug) {
|
|
1475
|
+
console.error(`[DEBUG] Gemini built-in tools (gemini_google_search, gemini_url_context) are not available: provider is '${this.apiType}', not 'google'. These tools require the Google Gemini provider.`);
|
|
1476
|
+
}
|
|
1477
|
+
}
|
|
1478
|
+
return result;
|
|
1479
|
+
}
|
|
1480
|
+
|
|
1481
|
+
// Check SDK support
|
|
1482
|
+
if (!this.provider || !this.provider.tools) {
|
|
1483
|
+
console.error('[ProbeAgent] Gemini built-in tools unavailable: @ai-sdk/google does not expose provider.tools. Upgrade to @ai-sdk/google v2.0.14+.');
|
|
1484
|
+
return result;
|
|
1485
|
+
}
|
|
1486
|
+
|
|
1487
|
+
if (isToolAllowed('gemini_google_search')) {
|
|
1488
|
+
result.googleSearch = true;
|
|
1489
|
+
if (this.debug) {
|
|
1490
|
+
console.error('[DEBUG] Gemini built-in tool enabled: gemini_google_search');
|
|
1491
|
+
}
|
|
1492
|
+
}
|
|
1493
|
+
|
|
1494
|
+
if (isToolAllowed('gemini_url_context')) {
|
|
1495
|
+
result.urlContext = true;
|
|
1496
|
+
if (this.debug) {
|
|
1497
|
+
console.error('[DEBUG] Gemini built-in tool enabled: gemini_url_context');
|
|
1498
|
+
}
|
|
1499
|
+
}
|
|
1500
|
+
|
|
1501
|
+
return result;
|
|
1502
|
+
}
|
|
1503
|
+
|
|
1504
|
+
/**
|
|
1505
|
+
* Build Gemini provider-defined tools object for streamText().
|
|
1506
|
+
* Returns undefined if no Gemini tools are enabled.
|
|
1507
|
+
* @returns {Object|undefined}
|
|
1508
|
+
* @private
|
|
1509
|
+
*/
|
|
1510
|
+
_buildGeminiProviderTools() {
|
|
1511
|
+
if (this.apiType !== 'google' || !this._geminiToolsEnabled) {
|
|
1512
|
+
return undefined;
|
|
1513
|
+
}
|
|
1514
|
+
|
|
1515
|
+
const { googleSearch, urlContext } = this._geminiToolsEnabled;
|
|
1516
|
+
if (!googleSearch && !urlContext) {
|
|
1517
|
+
return undefined;
|
|
1518
|
+
}
|
|
1519
|
+
|
|
1520
|
+
if (!this.provider || !this.provider.tools) {
|
|
1521
|
+
return undefined;
|
|
1522
|
+
}
|
|
1523
|
+
|
|
1524
|
+
const tools = {};
|
|
1525
|
+
const providerTools = this.provider.tools;
|
|
1526
|
+
|
|
1527
|
+
if (googleSearch && providerTools.googleSearch) {
|
|
1528
|
+
tools.google_search = providerTools.googleSearch({});
|
|
1529
|
+
}
|
|
1530
|
+
if (urlContext && providerTools.urlContext) {
|
|
1531
|
+
tools.url_context = providerTools.urlContext({});
|
|
1532
|
+
}
|
|
1533
|
+
|
|
1534
|
+
return Object.keys(tools).length > 0 ? tools : undefined;
|
|
1535
|
+
}
|
|
1536
|
+
|
|
1445
1537
|
/**
|
|
1446
1538
|
* Initialize AWS Bedrock model
|
|
1447
1539
|
*/
|
|
@@ -2420,6 +2512,14 @@ ${extractGuidance}
|
|
|
2420
2512
|
toolDefinitions += `${analyzeAllToolDefinition}\n`;
|
|
2421
2513
|
}
|
|
2422
2514
|
|
|
2515
|
+
// Gemini built-in tools (only when using Google provider)
|
|
2516
|
+
if (this._geminiToolsEnabled?.googleSearch && isToolAllowed('gemini_google_search')) {
|
|
2517
|
+
toolDefinitions += `${googleSearchToolDefinition}\n`;
|
|
2518
|
+
}
|
|
2519
|
+
if (this._geminiToolsEnabled?.urlContext && isToolAllowed('gemini_url_context')) {
|
|
2520
|
+
toolDefinitions += `${urlContextToolDefinition}\n`;
|
|
2521
|
+
}
|
|
2522
|
+
|
|
2423
2523
|
// Build XML tool guidelines with dynamic examples based on allowed tools
|
|
2424
2524
|
// Build examples only for allowed tools
|
|
2425
2525
|
let toolExamples = '';
|
|
@@ -2497,6 +2597,12 @@ The configuration is loaded from src/config.js lines 15-25 which contains the da
|
|
|
2497
2597
|
availableToolsList += '- attempt_completion: Finalize the task and provide the result to the user.\n';
|
|
2498
2598
|
availableToolsList += '- attempt_complete: Quick completion using previous response (shorthand).\n';
|
|
2499
2599
|
}
|
|
2600
|
+
if (this._geminiToolsEnabled?.googleSearch && isToolAllowed('gemini_google_search')) {
|
|
2601
|
+
availableToolsList += '- gemini_google_search: (auto) Web search via Google — invoked automatically by the model when it needs current information.\n';
|
|
2602
|
+
}
|
|
2603
|
+
if (this._geminiToolsEnabled?.urlContext && isToolAllowed('gemini_url_context')) {
|
|
2604
|
+
availableToolsList += '- gemini_url_context: (auto) URL content reader via Google — automatically fetches and reads URLs mentioned in the conversation.\n';
|
|
2605
|
+
}
|
|
2500
2606
|
|
|
2501
2607
|
let xmlToolGuidelines = `
|
|
2502
2608
|
# Tool Use Formatting
|
|
@@ -3049,12 +3155,21 @@ Follow these instructions carefully:
|
|
|
3049
3155
|
// Prepare messages with potential image content
|
|
3050
3156
|
const messagesForAI = this.prepareMessagesWithImages(currentMessages);
|
|
3051
3157
|
|
|
3052
|
-
|
|
3158
|
+
// Build streamText options, including Gemini provider-defined tools if applicable
|
|
3159
|
+
const streamOptions = {
|
|
3053
3160
|
model: this.provider ? this.provider(this.model) : this.model,
|
|
3054
3161
|
messages: messagesForAI,
|
|
3055
3162
|
maxTokens: maxResponseTokens,
|
|
3056
3163
|
temperature: 0.3,
|
|
3057
|
-
}
|
|
3164
|
+
};
|
|
3165
|
+
|
|
3166
|
+
// Inject Gemini built-in tools (gemini_google_search, gemini_url_context) when using Google provider
|
|
3167
|
+
const geminiProviderTools = this._buildGeminiProviderTools();
|
|
3168
|
+
if (geminiProviderTools) {
|
|
3169
|
+
streamOptions.tools = geminiProviderTools;
|
|
3170
|
+
}
|
|
3171
|
+
|
|
3172
|
+
const result = await this.streamTextWithRetryAndFallback(streamOptions);
|
|
3058
3173
|
|
|
3059
3174
|
// Get the promise reference BEFORE consuming stream (doesn't lock it)
|
|
3060
3175
|
const usagePromise = result.usage;
|
|
@@ -3585,6 +3700,12 @@ Follow these instructions carefully:
|
|
|
3585
3700
|
|
|
3586
3701
|
let toolResultContent = typeof toolResult === 'string' ? toolResult : JSON.stringify(toolResult, null, 2);
|
|
3587
3702
|
|
|
3703
|
+
// Convert absolute workspace paths to relative in tool results
|
|
3704
|
+
if (this.workspaceRoot && toolResultContent) {
|
|
3705
|
+
const wsPrefix = this.workspaceRoot.endsWith(sep) ? this.workspaceRoot : this.workspaceRoot + sep;
|
|
3706
|
+
toolResultContent = toolResultContent.split(wsPrefix).join('');
|
|
3707
|
+
}
|
|
3708
|
+
|
|
3588
3709
|
// Truncate if output exceeds token limit
|
|
3589
3710
|
try {
|
|
3590
3711
|
const truncateResult = await truncateIfNeeded(toolResultContent, this.tokenCounter, this.sessionId, this.maxOutputTokens);
|
package/build/agent/index.js
CHANGED
|
@@ -3314,6 +3314,29 @@ async function validateCwdPath(inputPath, defaultPath = process.cwd()) {
|
|
|
3314
3314
|
try {
|
|
3315
3315
|
const stats = await fs5.stat(normalizedPath);
|
|
3316
3316
|
if (!stats.isDirectory()) {
|
|
3317
|
+
const resolvedPath = safeRealpath(normalizedPath);
|
|
3318
|
+
const dirPath = path4.dirname(resolvedPath);
|
|
3319
|
+
try {
|
|
3320
|
+
const dirStats = await fs5.stat(dirPath);
|
|
3321
|
+
if (dirStats.isDirectory()) {
|
|
3322
|
+
return safeRealpath(dirPath);
|
|
3323
|
+
}
|
|
3324
|
+
} catch (dirError) {
|
|
3325
|
+
if (dirError.code === "ENOENT") {
|
|
3326
|
+
throw new PathError(`Parent directory does not exist for file: ${normalizedPath}`, {
|
|
3327
|
+
suggestion: "The specified path is a file whose parent directory does not exist.",
|
|
3328
|
+
details: { path: normalizedPath, parentPath: dirPath, type: "file" }
|
|
3329
|
+
});
|
|
3330
|
+
}
|
|
3331
|
+
if (dirError.code === "EACCES") {
|
|
3332
|
+
throw new PathError(`Permission denied accessing parent directory: ${dirPath}`, {
|
|
3333
|
+
recoverable: false,
|
|
3334
|
+
suggestion: "Permission denied accessing the parent directory of the specified file.",
|
|
3335
|
+
details: { path: normalizedPath, parentPath: dirPath, type: "file" }
|
|
3336
|
+
});
|
|
3337
|
+
}
|
|
3338
|
+
throw dirError;
|
|
3339
|
+
}
|
|
3317
3340
|
throw new PathError(`Path is not a directory: ${normalizedPath}`, {
|
|
3318
3341
|
suggestion: "The specified path is a file, not a directory. Please provide a directory path for searching.",
|
|
3319
3342
|
details: { path: normalizedPath, type: "file" }
|
|
@@ -9807,7 +9830,7 @@ function resolveTargetPath(target, cwd) {
|
|
|
9807
9830
|
}
|
|
9808
9831
|
return filePart + suffix;
|
|
9809
9832
|
}
|
|
9810
|
-
var searchSchema, querySchema, extractSchema, delegateSchema, listSkillsSchema, useSkillSchema, bashSchema, analyzeAllSchema, attemptCompletionSchema, searchToolDefinition, queryToolDefinition, extractToolDefinition, delegateToolDefinition, attemptCompletionToolDefinition, analyzeAllToolDefinition, bashToolDefinition, searchDescription, queryDescription, extractDescription, delegateDescription, analyzeAllDescription, DEFAULT_VALID_TOOLS;
|
|
9833
|
+
var searchSchema, querySchema, extractSchema, delegateSchema, listSkillsSchema, useSkillSchema, bashSchema, analyzeAllSchema, attemptCompletionSchema, searchToolDefinition, queryToolDefinition, extractToolDefinition, delegateToolDefinition, attemptCompletionToolDefinition, analyzeAllToolDefinition, bashToolDefinition, googleSearchToolDefinition, urlContextToolDefinition, searchDescription, queryDescription, extractDescription, delegateDescription, analyzeAllDescription, DEFAULT_VALID_TOOLS;
|
|
9811
9834
|
var init_common = __esm({
|
|
9812
9835
|
"src/tools/common.js"() {
|
|
9813
9836
|
"use strict";
|
|
@@ -10163,6 +10186,28 @@ User: Check system info
|
|
|
10163
10186
|
</bash>
|
|
10164
10187
|
|
|
10165
10188
|
</examples>
|
|
10189
|
+
`;
|
|
10190
|
+
googleSearchToolDefinition = `
|
|
10191
|
+
## gemini_google_search (Gemini Built-in)
|
|
10192
|
+
Description: Web search powered by Google. This is a built-in Gemini capability that automatically searches the web when the model needs current information. The model decides when to search and integrates results directly into its response with source citations.
|
|
10193
|
+
|
|
10194
|
+
This tool is invoked automatically by the model \u2014 you do NOT need to use XML tool calls for it. Simply ask questions that require up-to-date or real-world information and the model will search the web as needed.
|
|
10195
|
+
|
|
10196
|
+
Capabilities:
|
|
10197
|
+
- Real-time web search with grounded citations
|
|
10198
|
+
- Automatic query generation and result synthesis
|
|
10199
|
+
- Source attribution with URLs
|
|
10200
|
+
`;
|
|
10201
|
+
urlContextToolDefinition = `
|
|
10202
|
+
## gemini_url_context (Gemini Built-in)
|
|
10203
|
+
Description: URL content reader powered by Google. This is a built-in Gemini capability that automatically fetches and analyzes the content of URLs mentioned in the conversation. When you include URLs in your message, the model can read and understand their content.
|
|
10204
|
+
|
|
10205
|
+
This tool is invoked automatically by the model \u2014 you do NOT need to use XML tool calls for it. Simply include URLs in your message and the model will fetch and analyze their content.
|
|
10206
|
+
|
|
10207
|
+
Capabilities:
|
|
10208
|
+
- Fetch and read web page content from URLs in the prompt
|
|
10209
|
+
- Supports up to 20 URLs per request
|
|
10210
|
+
- Processes HTML content (does not execute JavaScript)
|
|
10166
10211
|
`;
|
|
10167
10212
|
searchDescription = "Search code in the repository. Free-form questions are accepted, but Elasticsearch-style keyword queries work best. Use this tool first for any code-related questions.";
|
|
10168
10213
|
queryDescription = "Search code using ast-grep structural pattern matching. Use this tool to find specific code structures like functions, classes, or methods.";
|
|
@@ -59411,7 +59456,13 @@ var init_config = __esm({
|
|
|
59411
59456
|
__filename4 = fileURLToPath6(import.meta.url);
|
|
59412
59457
|
__dirname4 = dirname3(__filename4);
|
|
59413
59458
|
DEFAULT_TIMEOUT = 3e4;
|
|
59414
|
-
MAX_TIMEOUT =
|
|
59459
|
+
MAX_TIMEOUT = (() => {
|
|
59460
|
+
if (process.env.MCP_MAX_TIMEOUT) {
|
|
59461
|
+
const parsed = parseInt(process.env.MCP_MAX_TIMEOUT, 10);
|
|
59462
|
+
if (!isNaN(parsed) && parsed >= 3e4 && parsed <= 72e5) return parsed;
|
|
59463
|
+
}
|
|
59464
|
+
return 18e5;
|
|
59465
|
+
})();
|
|
59415
59466
|
DEFAULT_CONFIG = {
|
|
59416
59467
|
mcpServers: {
|
|
59417
59468
|
// Example probe server configuration
|
|
@@ -70516,6 +70567,7 @@ var init_ProbeAgent = __esm({
|
|
|
70516
70567
|
this.fallbackManager = null;
|
|
70517
70568
|
this.engine = null;
|
|
70518
70569
|
this.initializeModel();
|
|
70570
|
+
this._geminiToolsEnabled = this._initializeGeminiBuiltinTools();
|
|
70519
70571
|
}
|
|
70520
70572
|
/**
|
|
70521
70573
|
* Parse allowedTools configuration
|
|
@@ -71248,6 +71300,12 @@ var init_ProbeAgent = __esm({
|
|
|
71248
71300
|
model: provider(model),
|
|
71249
71301
|
abortSignal: controller.signal
|
|
71250
71302
|
};
|
|
71303
|
+
if (config.provider !== "google" && fallbackOptions.tools) {
|
|
71304
|
+
delete fallbackOptions.tools;
|
|
71305
|
+
if (this.debug) {
|
|
71306
|
+
console.error(`[DEBUG] Stripped Gemini built-in tools for fallback to ${config.provider} provider`);
|
|
71307
|
+
}
|
|
71308
|
+
}
|
|
71251
71309
|
const providerRetryManager = new RetryManager({
|
|
71252
71310
|
maxRetries: config.maxRetries ?? this.retryConfig.maxRetries ?? 3,
|
|
71253
71311
|
initialDelay: this.retryConfig.initialDelay ?? 1e3,
|
|
@@ -71351,6 +71409,69 @@ var init_ProbeAgent = __esm({
|
|
|
71351
71409
|
console.log(`Using Google API with model: ${this.model}${apiUrl ? ` (URL: ${apiUrl})` : ""}`);
|
|
71352
71410
|
}
|
|
71353
71411
|
}
|
|
71412
|
+
/**
|
|
71413
|
+
* Initialize Gemini built-in tools (gemini_google_search, gemini_url_context).
|
|
71414
|
+
* These are provider-defined tools that execute server-side on Google's infrastructure.
|
|
71415
|
+
* They are only available when the provider is Google Gemini.
|
|
71416
|
+
* @returns {{ googleSearch: boolean, urlContext: boolean }} Which tools were enabled
|
|
71417
|
+
* @private
|
|
71418
|
+
*/
|
|
71419
|
+
_initializeGeminiBuiltinTools() {
|
|
71420
|
+
const isToolAllowed = (toolName) => this.allowedTools.isEnabled(toolName);
|
|
71421
|
+
const result = { googleSearch: false, urlContext: false };
|
|
71422
|
+
if (this.apiType !== "google") {
|
|
71423
|
+
if (isToolAllowed("gemini_google_search") || isToolAllowed("gemini_url_context")) {
|
|
71424
|
+
if (this.debug) {
|
|
71425
|
+
console.error(`[DEBUG] Gemini built-in tools (gemini_google_search, gemini_url_context) are not available: provider is '${this.apiType}', not 'google'. These tools require the Google Gemini provider.`);
|
|
71426
|
+
}
|
|
71427
|
+
}
|
|
71428
|
+
return result;
|
|
71429
|
+
}
|
|
71430
|
+
if (!this.provider || !this.provider.tools) {
|
|
71431
|
+
console.error("[ProbeAgent] Gemini built-in tools unavailable: @ai-sdk/google does not expose provider.tools. Upgrade to @ai-sdk/google v2.0.14+.");
|
|
71432
|
+
return result;
|
|
71433
|
+
}
|
|
71434
|
+
if (isToolAllowed("gemini_google_search")) {
|
|
71435
|
+
result.googleSearch = true;
|
|
71436
|
+
if (this.debug) {
|
|
71437
|
+
console.error("[DEBUG] Gemini built-in tool enabled: gemini_google_search");
|
|
71438
|
+
}
|
|
71439
|
+
}
|
|
71440
|
+
if (isToolAllowed("gemini_url_context")) {
|
|
71441
|
+
result.urlContext = true;
|
|
71442
|
+
if (this.debug) {
|
|
71443
|
+
console.error("[DEBUG] Gemini built-in tool enabled: gemini_url_context");
|
|
71444
|
+
}
|
|
71445
|
+
}
|
|
71446
|
+
return result;
|
|
71447
|
+
}
|
|
71448
|
+
/**
|
|
71449
|
+
* Build Gemini provider-defined tools object for streamText().
|
|
71450
|
+
* Returns undefined if no Gemini tools are enabled.
|
|
71451
|
+
* @returns {Object|undefined}
|
|
71452
|
+
* @private
|
|
71453
|
+
*/
|
|
71454
|
+
_buildGeminiProviderTools() {
|
|
71455
|
+
if (this.apiType !== "google" || !this._geminiToolsEnabled) {
|
|
71456
|
+
return void 0;
|
|
71457
|
+
}
|
|
71458
|
+
const { googleSearch, urlContext } = this._geminiToolsEnabled;
|
|
71459
|
+
if (!googleSearch && !urlContext) {
|
|
71460
|
+
return void 0;
|
|
71461
|
+
}
|
|
71462
|
+
if (!this.provider || !this.provider.tools) {
|
|
71463
|
+
return void 0;
|
|
71464
|
+
}
|
|
71465
|
+
const tools2 = {};
|
|
71466
|
+
const providerTools = this.provider.tools;
|
|
71467
|
+
if (googleSearch && providerTools.googleSearch) {
|
|
71468
|
+
tools2.google_search = providerTools.googleSearch({});
|
|
71469
|
+
}
|
|
71470
|
+
if (urlContext && providerTools.urlContext) {
|
|
71471
|
+
tools2.url_context = providerTools.urlContext({});
|
|
71472
|
+
}
|
|
71473
|
+
return Object.keys(tools2).length > 0 ? tools2 : void 0;
|
|
71474
|
+
}
|
|
71354
71475
|
/**
|
|
71355
71476
|
* Initialize AWS Bedrock model
|
|
71356
71477
|
*/
|
|
@@ -72139,6 +72260,14 @@ Workspace: ${this.allowedFolders.join(", ")}`;
|
|
|
72139
72260
|
}
|
|
72140
72261
|
if (isToolAllowed("analyze_all")) {
|
|
72141
72262
|
toolDefinitions += `${analyzeAllToolDefinition}
|
|
72263
|
+
`;
|
|
72264
|
+
}
|
|
72265
|
+
if (this._geminiToolsEnabled?.googleSearch && isToolAllowed("gemini_google_search")) {
|
|
72266
|
+
toolDefinitions += `${googleSearchToolDefinition}
|
|
72267
|
+
`;
|
|
72268
|
+
}
|
|
72269
|
+
if (this._geminiToolsEnabled?.urlContext && isToolAllowed("gemini_url_context")) {
|
|
72270
|
+
toolDefinitions += `${urlContextToolDefinition}
|
|
72142
72271
|
`;
|
|
72143
72272
|
}
|
|
72144
72273
|
let toolExamples = "";
|
|
@@ -72215,6 +72344,12 @@ The configuration is loaded from src/config.js lines 15-25 which contains the da
|
|
|
72215
72344
|
availableToolsList += "- attempt_completion: Finalize the task and provide the result to the user.\n";
|
|
72216
72345
|
availableToolsList += "- attempt_complete: Quick completion using previous response (shorthand).\n";
|
|
72217
72346
|
}
|
|
72347
|
+
if (this._geminiToolsEnabled?.googleSearch && isToolAllowed("gemini_google_search")) {
|
|
72348
|
+
availableToolsList += "- gemini_google_search: (auto) Web search via Google \u2014 invoked automatically by the model when it needs current information.\n";
|
|
72349
|
+
}
|
|
72350
|
+
if (this._geminiToolsEnabled?.urlContext && isToolAllowed("gemini_url_context")) {
|
|
72351
|
+
availableToolsList += "- gemini_url_context: (auto) URL content reader via Google \u2014 automatically fetches and reads URLs mentioned in the conversation.\n";
|
|
72352
|
+
}
|
|
72218
72353
|
let xmlToolGuidelines = `
|
|
72219
72354
|
# Tool Use Formatting
|
|
72220
72355
|
|
|
@@ -72665,12 +72800,17 @@ You are working with a workspace. Available paths: ${workspaceDesc}
|
|
|
72665
72800
|
try {
|
|
72666
72801
|
const executeAIRequest = async () => {
|
|
72667
72802
|
const messagesForAI = this.prepareMessagesWithImages(currentMessages);
|
|
72668
|
-
const
|
|
72803
|
+
const streamOptions = {
|
|
72669
72804
|
model: this.provider ? this.provider(this.model) : this.model,
|
|
72670
72805
|
messages: messagesForAI,
|
|
72671
72806
|
maxTokens: maxResponseTokens,
|
|
72672
72807
|
temperature: 0.3
|
|
72673
|
-
}
|
|
72808
|
+
};
|
|
72809
|
+
const geminiProviderTools = this._buildGeminiProviderTools();
|
|
72810
|
+
if (geminiProviderTools) {
|
|
72811
|
+
streamOptions.tools = geminiProviderTools;
|
|
72812
|
+
}
|
|
72813
|
+
const result = await this.streamTextWithRetryAndFallback(streamOptions);
|
|
72674
72814
|
const usagePromise = result.usage;
|
|
72675
72815
|
for await (const delta of result.textStream) {
|
|
72676
72816
|
assistantResponseContent += delta;
|
|
@@ -73060,6 +73200,10 @@ ${errorXml}
|
|
|
73060
73200
|
}
|
|
73061
73201
|
currentMessages.push({ role: "assistant", content: assistantResponseContent });
|
|
73062
73202
|
let toolResultContent = typeof toolResult === "string" ? toolResult : JSON.stringify(toolResult, null, 2);
|
|
73203
|
+
if (this.workspaceRoot && toolResultContent) {
|
|
73204
|
+
const wsPrefix = this.workspaceRoot.endsWith(sep5) ? this.workspaceRoot : this.workspaceRoot + sep5;
|
|
73205
|
+
toolResultContent = toolResultContent.split(wsPrefix).join("");
|
|
73206
|
+
}
|
|
73063
73207
|
try {
|
|
73064
73208
|
const truncateResult = await truncateIfNeeded(toolResultContent, this.tokenCounter, this.sessionId, this.maxOutputTokens);
|
|
73065
73209
|
if (truncateResult.truncated) {
|
|
@@ -15,7 +15,13 @@ const __dirname = dirname(__filename);
|
|
|
15
15
|
* Timeout configuration constants
|
|
16
16
|
*/
|
|
17
17
|
export const DEFAULT_TIMEOUT = 30000; // 30 seconds
|
|
18
|
-
export const MAX_TIMEOUT =
|
|
18
|
+
export const MAX_TIMEOUT = (() => {
|
|
19
|
+
if (process.env.MCP_MAX_TIMEOUT) {
|
|
20
|
+
const parsed = parseInt(process.env.MCP_MAX_TIMEOUT, 10);
|
|
21
|
+
if (!isNaN(parsed) && parsed >= 30000 && parsed <= 7200000) return parsed;
|
|
22
|
+
}
|
|
23
|
+
return 1800000; // 30 minutes default - workflow tools (code checkouts, AI exploration) need time
|
|
24
|
+
})();
|
|
19
25
|
|
|
20
26
|
/**
|
|
21
27
|
* Validate and normalize a timeout value
|
package/build/agent/tools.js
CHANGED
|
@@ -27,6 +27,8 @@ import {
|
|
|
27
27
|
bashToolDefinition,
|
|
28
28
|
editToolDefinition,
|
|
29
29
|
createToolDefinition,
|
|
30
|
+
googleSearchToolDefinition,
|
|
31
|
+
urlContextToolDefinition,
|
|
30
32
|
parseXmlToolCall
|
|
31
33
|
} from '../index.js';
|
|
32
34
|
import { randomUUID } from 'crypto';
|
|
@@ -108,6 +110,8 @@ export {
|
|
|
108
110
|
editToolDefinition,
|
|
109
111
|
createToolDefinition,
|
|
110
112
|
attemptCompletionToolDefinition,
|
|
113
|
+
googleSearchToolDefinition,
|
|
114
|
+
urlContextToolDefinition,
|
|
111
115
|
parseXmlToolCall
|
|
112
116
|
};
|
|
113
117
|
|
package/build/index.js
CHANGED
|
@@ -35,6 +35,8 @@ import {
|
|
|
35
35
|
analyzeAllToolDefinition,
|
|
36
36
|
attemptCompletionToolDefinition,
|
|
37
37
|
bashToolDefinition,
|
|
38
|
+
googleSearchToolDefinition,
|
|
39
|
+
urlContextToolDefinition,
|
|
38
40
|
parseXmlToolCall
|
|
39
41
|
} from './tools/common.js';
|
|
40
42
|
import {
|
|
@@ -114,6 +116,8 @@ export {
|
|
|
114
116
|
bashToolDefinition,
|
|
115
117
|
editToolDefinition,
|
|
116
118
|
createToolDefinition,
|
|
119
|
+
googleSearchToolDefinition,
|
|
120
|
+
urlContextToolDefinition,
|
|
117
121
|
// Export parser function
|
|
118
122
|
parseXmlToolCall,
|
|
119
123
|
// Export task management
|
package/build/tools/common.js
CHANGED
|
@@ -386,6 +386,30 @@ User: Check system info
|
|
|
386
386
|
</examples>
|
|
387
387
|
`;
|
|
388
388
|
|
|
389
|
+
export const googleSearchToolDefinition = `
|
|
390
|
+
## gemini_google_search (Gemini Built-in)
|
|
391
|
+
Description: Web search powered by Google. This is a built-in Gemini capability that automatically searches the web when the model needs current information. The model decides when to search and integrates results directly into its response with source citations.
|
|
392
|
+
|
|
393
|
+
This tool is invoked automatically by the model — you do NOT need to use XML tool calls for it. Simply ask questions that require up-to-date or real-world information and the model will search the web as needed.
|
|
394
|
+
|
|
395
|
+
Capabilities:
|
|
396
|
+
- Real-time web search with grounded citations
|
|
397
|
+
- Automatic query generation and result synthesis
|
|
398
|
+
- Source attribution with URLs
|
|
399
|
+
`;
|
|
400
|
+
|
|
401
|
+
export const urlContextToolDefinition = `
|
|
402
|
+
## gemini_url_context (Gemini Built-in)
|
|
403
|
+
Description: URL content reader powered by Google. This is a built-in Gemini capability that automatically fetches and analyzes the content of URLs mentioned in the conversation. When you include URLs in your message, the model can read and understand their content.
|
|
404
|
+
|
|
405
|
+
This tool is invoked automatically by the model — you do NOT need to use XML tool calls for it. Simply include URLs in your message and the model will fetch and analyze their content.
|
|
406
|
+
|
|
407
|
+
Capabilities:
|
|
408
|
+
- Fetch and read web page content from URLs in the prompt
|
|
409
|
+
- Supports up to 20 URLs per request
|
|
410
|
+
- Processes HTML content (does not execute JavaScript)
|
|
411
|
+
`;
|
|
412
|
+
|
|
389
413
|
export const searchDescription = 'Search code in the repository. Free-form questions are accepted, but Elasticsearch-style keyword queries work best. Use this tool first for any code-related questions.';
|
|
390
414
|
export const queryDescription = 'Search code using ast-grep structural pattern matching. Use this tool to find specific code structures like functions, classes, or methods.';
|
|
391
415
|
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.';
|
|
@@ -55,9 +55,9 @@ export function safeRealpath(inputPath) {
|
|
|
55
55
|
* - Does NOT restrict access to specific directories (that's the responsibility
|
|
56
56
|
* of higher-level components like ProbeAgent with allowedFolders)
|
|
57
57
|
*
|
|
58
|
-
* @param {string} inputPath - The path to validate
|
|
58
|
+
* @param {string} inputPath - The path to validate (can be a file or directory; file paths are resolved to their parent directory)
|
|
59
59
|
* @param {string} [defaultPath] - Default path to use if inputPath is not provided
|
|
60
|
-
* @returns {Promise<string>} Normalized absolute path
|
|
60
|
+
* @returns {Promise<string>} Normalized absolute directory path. If inputPath is a file, returns its parent directory.
|
|
61
61
|
* @throws {PathError} If the path is invalid or doesn't exist
|
|
62
62
|
*/
|
|
63
63
|
export async function validateCwdPath(inputPath, defaultPath = process.cwd()) {
|
|
@@ -72,6 +72,32 @@ export async function validateCwdPath(inputPath, defaultPath = process.cwd()) {
|
|
|
72
72
|
try {
|
|
73
73
|
const stats = await fs.stat(normalizedPath);
|
|
74
74
|
if (!stats.isDirectory()) {
|
|
75
|
+
// If the path is a file, resolve to its parent directory
|
|
76
|
+
// This handles cases where a file path is passed as cwd
|
|
77
|
+
// Use safeRealpath to resolve symlinks before extracting parent directory
|
|
78
|
+
const resolvedPath = safeRealpath(normalizedPath);
|
|
79
|
+
const dirPath = path.dirname(resolvedPath);
|
|
80
|
+
try {
|
|
81
|
+
const dirStats = await fs.stat(dirPath);
|
|
82
|
+
if (dirStats.isDirectory()) {
|
|
83
|
+
return safeRealpath(dirPath);
|
|
84
|
+
}
|
|
85
|
+
} catch (dirError) {
|
|
86
|
+
if (dirError.code === 'ENOENT') {
|
|
87
|
+
throw new PathError(`Parent directory does not exist for file: ${normalizedPath}`, {
|
|
88
|
+
suggestion: 'The specified path is a file whose parent directory does not exist.',
|
|
89
|
+
details: { path: normalizedPath, parentPath: dirPath, type: 'file' }
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
if (dirError.code === 'EACCES') {
|
|
93
|
+
throw new PathError(`Permission denied accessing parent directory: ${dirPath}`, {
|
|
94
|
+
recoverable: false,
|
|
95
|
+
suggestion: 'Permission denied accessing the parent directory of the specified file.',
|
|
96
|
+
details: { path: normalizedPath, parentPath: dirPath, type: 'file' }
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
throw dirError;
|
|
100
|
+
}
|
|
75
101
|
throw new PathError(`Path is not a directory: ${normalizedPath}`, {
|
|
76
102
|
suggestion: 'The specified path is a file, not a directory. Please provide a directory path for searching.',
|
|
77
103
|
details: { path: normalizedPath, type: 'file' }
|