@probelabs/probe 0.6.0-rc279 → 0.6.0-rc281
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-rc281-aarch64-apple-darwin.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc281-aarch64-unknown-linux-musl.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc281-x86_64-apple-darwin.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc281-x86_64-pc-windows-msvc.zip +0 -0
- package/bin/binaries/probe-v0.6.0-rc281-x86_64-unknown-linux-musl.tar.gz +0 -0
- package/build/agent/ProbeAgent.js +68 -34
- package/build/agent/dsl/environment.js +1 -0
- package/build/agent/index.js +161 -77
- package/build/delegate.js +22 -12
- package/build/downloader.js +28 -25
- package/build/tools/analyzeAll.js +2 -6
- package/build/tools/common.js +5 -4
- package/build/tools/vercel.js +65 -6
- package/cjs/agent/ProbeAgent.cjs +161 -77
- package/cjs/index.cjs +162 -78
- package/package.json +1 -1
- package/src/agent/ProbeAgent.js +68 -34
- package/src/agent/dsl/environment.js +1 -0
- package/src/delegate.js +22 -12
- package/src/downloader.js +28 -25
- package/src/tools/analyzeAll.js +2 -6
- package/src/tools/common.js +5 -4
- package/src/tools/vercel.js +65 -6
- package/bin/binaries/probe-v0.6.0-rc279-aarch64-apple-darwin.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc279-aarch64-unknown-linux-musl.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc279-x86_64-apple-darwin.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc279-x86_64-pc-windows-msvc.zip +0 -0
- package/bin/binaries/probe-v0.6.0-rc279-x86_64-unknown-linux-musl.tar.gz +0 -0
package/cjs/index.cjs
CHANGED
|
@@ -636,9 +636,7 @@ async function acquireFileLock(lockPath, version2) {
|
|
|
636
636
|
};
|
|
637
637
|
try {
|
|
638
638
|
await import_fs_extra2.default.writeFile(lockPath, JSON.stringify(lockData), { flag: "wx" });
|
|
639
|
-
|
|
640
|
-
console.log(`Acquired file lock: ${lockPath}`);
|
|
641
|
-
}
|
|
639
|
+
console.log(`Acquired file lock: ${lockPath}`);
|
|
642
640
|
return true;
|
|
643
641
|
} catch (error2) {
|
|
644
642
|
if (error2.code === "EEXIST") {
|
|
@@ -646,15 +644,11 @@ async function acquireFileLock(lockPath, version2) {
|
|
|
646
644
|
const existingLock = JSON.parse(await import_fs_extra2.default.readFile(lockPath, "utf-8"));
|
|
647
645
|
const lockAge = Date.now() - existingLock.timestamp;
|
|
648
646
|
if (lockAge > LOCK_TIMEOUT_MS) {
|
|
649
|
-
|
|
650
|
-
console.log(`Removing stale lock file (age: ${Math.round(lockAge / 1e3)}s, pid: ${existingLock.pid})`);
|
|
651
|
-
}
|
|
647
|
+
console.log(`Removing stale lock file (age: ${Math.round(lockAge / 1e3)}s, pid: ${existingLock.pid})`);
|
|
652
648
|
await import_fs_extra2.default.remove(lockPath);
|
|
653
649
|
return false;
|
|
654
650
|
}
|
|
655
|
-
|
|
656
|
-
console.log(`Download in progress by process ${existingLock.pid}, waiting...`);
|
|
657
|
-
}
|
|
651
|
+
console.log(`Download in progress by process ${existingLock.pid}, waiting...`);
|
|
658
652
|
return false;
|
|
659
653
|
} catch (readError) {
|
|
660
654
|
if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") {
|
|
@@ -695,36 +689,36 @@ async function releaseFileLock(lockPath) {
|
|
|
695
689
|
}
|
|
696
690
|
async function waitForFileLock(lockPath, binaryPath) {
|
|
697
691
|
const startTime = Date.now();
|
|
692
|
+
let lastStatusTime = startTime;
|
|
693
|
+
console.log(`Waiting for file lock to clear: ${lockPath}`);
|
|
698
694
|
while (Date.now() - startTime < MAX_LOCK_WAIT_MS) {
|
|
699
695
|
if (await import_fs_extra2.default.pathExists(binaryPath)) {
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
}
|
|
696
|
+
const waitedSeconds = Math.round((Date.now() - startTime) / 1e3);
|
|
697
|
+
console.log(`Binary now available at ${binaryPath}, download completed by another process (waited ${waitedSeconds}s)`);
|
|
703
698
|
return true;
|
|
704
699
|
}
|
|
705
700
|
const lockExists = await import_fs_extra2.default.pathExists(lockPath);
|
|
706
701
|
if (!lockExists) {
|
|
707
|
-
|
|
708
|
-
console.log(`Lock file removed but binary not found - download may have failed`);
|
|
709
|
-
}
|
|
702
|
+
console.log(`Lock file removed but binary not found - download may have failed`);
|
|
710
703
|
return false;
|
|
711
704
|
}
|
|
712
705
|
try {
|
|
713
706
|
const lockData = JSON.parse(await import_fs_extra2.default.readFile(lockPath, "utf-8"));
|
|
714
707
|
const lockAge = Date.now() - lockData.timestamp;
|
|
715
708
|
if (lockAge > LOCK_TIMEOUT_MS) {
|
|
716
|
-
|
|
717
|
-
console.log(`Lock expired (age: ${Math.round(lockAge / 1e3)}s), will retry download`);
|
|
718
|
-
}
|
|
709
|
+
console.log(`Lock expired (age: ${Math.round(lockAge / 1e3)}s), will retry download`);
|
|
719
710
|
return false;
|
|
720
711
|
}
|
|
721
712
|
} catch {
|
|
722
713
|
}
|
|
714
|
+
if (Date.now() - lastStatusTime >= 15e3) {
|
|
715
|
+
const elapsedSeconds = Math.round((Date.now() - startTime) / 1e3);
|
|
716
|
+
console.log(`Still waiting for file lock (${elapsedSeconds}s/${MAX_LOCK_WAIT_MS / 1e3}s max)`);
|
|
717
|
+
lastStatusTime = Date.now();
|
|
718
|
+
}
|
|
723
719
|
await new Promise((resolve8) => setTimeout(resolve8, LOCK_POLL_INTERVAL_MS));
|
|
724
720
|
}
|
|
725
|
-
|
|
726
|
-
console.log(`Timeout waiting for file lock`);
|
|
727
|
-
}
|
|
721
|
+
console.log(`Timeout waiting for file lock after ${MAX_LOCK_WAIT_MS / 1e3}s`);
|
|
728
722
|
return false;
|
|
729
723
|
}
|
|
730
724
|
async function withDownloadLock(version2, downloadFn) {
|
|
@@ -738,9 +732,7 @@ async function withDownloadLock(version2, downloadFn) {
|
|
|
738
732
|
}
|
|
739
733
|
downloadLocks.delete(lockKey);
|
|
740
734
|
} else {
|
|
741
|
-
|
|
742
|
-
console.log(`Download already in progress in this process for version ${lockKey}, waiting...`);
|
|
743
|
-
}
|
|
735
|
+
console.log(`Download already in progress in this process for version ${lockKey}, waiting...`);
|
|
744
736
|
try {
|
|
745
737
|
return await lock.promise;
|
|
746
738
|
} catch (error2) {
|
|
@@ -750,10 +742,16 @@ async function withDownloadLock(version2, downloadFn) {
|
|
|
750
742
|
}
|
|
751
743
|
}
|
|
752
744
|
}
|
|
745
|
+
let timeoutId = null;
|
|
753
746
|
const downloadPromise = Promise.race([
|
|
754
747
|
downloadFn(),
|
|
755
748
|
new Promise(
|
|
756
|
-
(_, reject2) =>
|
|
749
|
+
(_, reject2) => {
|
|
750
|
+
timeoutId = setTimeout(() => reject2(new Error(`Download timeout after ${LOCK_TIMEOUT_MS / 1e3}s`)), LOCK_TIMEOUT_MS);
|
|
751
|
+
if (timeoutId.unref) {
|
|
752
|
+
timeoutId.unref();
|
|
753
|
+
}
|
|
754
|
+
}
|
|
757
755
|
)
|
|
758
756
|
]);
|
|
759
757
|
downloadLocks.set(lockKey, {
|
|
@@ -764,6 +762,9 @@ async function withDownloadLock(version2, downloadFn) {
|
|
|
764
762
|
const result = await downloadPromise;
|
|
765
763
|
return result;
|
|
766
764
|
} finally {
|
|
765
|
+
if (timeoutId) {
|
|
766
|
+
clearTimeout(timeoutId);
|
|
767
|
+
}
|
|
767
768
|
downloadLocks.delete(lockKey);
|
|
768
769
|
}
|
|
769
770
|
}
|
|
@@ -36294,14 +36295,14 @@ function resolveTargetPath(target, cwd) {
|
|
|
36294
36295
|
}
|
|
36295
36296
|
return filePart + suffix;
|
|
36296
36297
|
}
|
|
36297
|
-
var import_path5, searchSchema, searchAllSchema, querySchema, extractSchema, delegateSchema, listSkillsSchema, useSkillSchema, listFilesSchema, searchFilesSchema, readImageSchema, bashSchema, analyzeAllSchema, executePlanSchema, cleanupExecutePlanSchema, attemptCompletionSchema, searchDescription, queryDescription, extractDescription, delegateDescription, bashDescription, analyzeAllDescription;
|
|
36298
|
+
var import_path5, searchSchema, searchAllSchema, querySchema, extractSchema, delegateSchema, listSkillsSchema, useSkillSchema, listFilesSchema, searchFilesSchema, readImageSchema, bashSchema, analyzeAllSchema, executePlanSchema, cleanupExecutePlanSchema, attemptCompletionSchema, searchDescription, searchDelegateDescription, queryDescription, extractDescription, delegateDescription, bashDescription, analyzeAllDescription;
|
|
36298
36299
|
var init_common2 = __esm({
|
|
36299
36300
|
"src/tools/common.js"() {
|
|
36300
36301
|
"use strict";
|
|
36301
36302
|
init_zod();
|
|
36302
36303
|
import_path5 = require("path");
|
|
36303
36304
|
searchSchema = external_exports.object({
|
|
36304
|
-
query: external_exports.string().describe("Search query
|
|
36305
|
+
query: external_exports.string().describe("Search query \u2014 natural language questions or Elasticsearch-style keywords both work. For keywords: use quotes for exact phrases, AND/OR for boolean logic, - for negation. Probe handles stemming and camelCase/snake_case splitting automatically, so do NOT try case or style variations of the same keyword."),
|
|
36305
36306
|
path: external_exports.string().optional().default(".").describe('Path to search in. For dependencies use "go:github.com/owner/repo", "js:package_name", or "rust:cargo_name" etc.'),
|
|
36306
36307
|
exact: external_exports.boolean().optional().default(false).describe('Default (false) enables stemming and keyword splitting for exploratory search - "getUserData" matches "get", "user", "data", etc. Set true for precise symbol lookup where "getUserData" matches only "getUserData". Use true when you know the exact symbol name.'),
|
|
36307
36308
|
maxTokens: external_exports.number().nullable().optional().describe("Maximum tokens to return. Default is 20000. Set to null for unlimited results."),
|
|
@@ -36309,7 +36310,7 @@ var init_common2 = __esm({
|
|
|
36309
36310
|
nextPage: external_exports.boolean().optional().default(false).describe("Set to true when requesting the next page of results. Requires passing the same session ID from the previous search output.")
|
|
36310
36311
|
});
|
|
36311
36312
|
searchAllSchema = external_exports.object({
|
|
36312
|
-
query: external_exports.string().describe("Search query
|
|
36313
|
+
query: external_exports.string().describe("Search query \u2014 natural language questions or Elasticsearch-style keywords both work. For keywords: use quotes for exact phrases, AND/OR for boolean logic, - for negation. Probe handles stemming and camelCase/snake_case splitting automatically, so do NOT try case or style variations of the same keyword."),
|
|
36313
36314
|
path: external_exports.string().optional().default(".").describe("Path to search in."),
|
|
36314
36315
|
exact: external_exports.boolean().optional().default(false).describe("Use exact matching instead of stemming."),
|
|
36315
36316
|
maxTokensPerPage: external_exports.number().optional().default(2e4).describe("Tokens per page when paginating. Default 20000."),
|
|
@@ -36416,11 +36417,12 @@ var init_common2 = __esm({
|
|
|
36416
36417
|
};
|
|
36417
36418
|
}
|
|
36418
36419
|
};
|
|
36419
|
-
searchDescription =
|
|
36420
|
+
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. NOTE: By default, search handles stemming, case-insensitive matching, and camelCase/snake_case splitting automatically \u2014 do NOT manually try keyword variations like "getAllUsers" then "get_all_users" then "GetAllUsers". One search covers all variations.';
|
|
36421
|
+
searchDelegateDescription = 'Search code in the repository by asking a question. Accepts natural language questions (e.g., "How does authentication work?", "Where is the user validation logic?"). A specialized subagent breaks down your question into targeted keyword searches and returns extracted code blocks. Do NOT formulate keyword queries yourself \u2014 just ask the question naturally.';
|
|
36420
36422
|
queryDescription = "Search code using ast-grep structural pattern matching. Use this tool to find specific code structures like functions, classes, or methods.";
|
|
36421
36423
|
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. Line numbers from output can be used with edit start_line/end_line for precise editing.";
|
|
36422
36424
|
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.";
|
|
36423
|
-
bashDescription = "Execute bash commands for system exploration and
|
|
36425
|
+
bashDescription = "Execute bash commands for system operations: building, running tests, git, package management, etc. NEVER use for code exploration (no grep, cat, find, head, tail) \u2014 use search and extract tools instead. Secure by default with built-in allow/deny lists.";
|
|
36424
36426
|
analyzeAllDescription = 'Answer questions that require analyzing ALL matching data in the codebase. Use for aggregate questions like "What features exist?", "List all API endpoints", "Count TODO comments". The AI automatically plans the search strategy, processes all results via map-reduce, and synthesizes a comprehensive answer. WARNING: Slower than search - only use when you need complete coverage.';
|
|
36425
36427
|
}
|
|
36426
36428
|
});
|
|
@@ -101736,6 +101738,7 @@ function generateSandboxGlobals(options) {
|
|
|
101736
101738
|
executing.add(p5);
|
|
101737
101739
|
results.push(p5);
|
|
101738
101740
|
if (executing.size >= mapConcurrency) {
|
|
101741
|
+
console.error(`[map] Concurrency limit reached (${executing.size}/${mapConcurrency}), waiting for a slot...`);
|
|
101739
101742
|
await Promise.race(executing);
|
|
101740
101743
|
}
|
|
101741
101744
|
}
|
|
@@ -106130,6 +106133,7 @@ var init_ProbeAgent = __esm({
|
|
|
106130
106133
|
this.completionPrompt = options.completionPrompt || null;
|
|
106131
106134
|
this.thinkingEffort = options.thinkingEffort || null;
|
|
106132
106135
|
const effectiveAllowedTools = options.disableTools ? [] : options.allowedTools;
|
|
106136
|
+
this._rawAllowedTools = options.allowedTools;
|
|
106133
106137
|
this.allowedTools = this._parseAllowedTools(effectiveAllowedTools);
|
|
106134
106138
|
this.storageAdapter = options.storageAdapter || new InMemoryStorageAdapter();
|
|
106135
106139
|
this.hooks = new HookManager();
|
|
@@ -106272,6 +106276,16 @@ var init_ProbeAgent = __esm({
|
|
|
106272
106276
|
_filterMcpTools(mcpToolNames) {
|
|
106273
106277
|
return mcpToolNames.filter((toolName) => this._isMcpToolAllowed(toolName));
|
|
106274
106278
|
}
|
|
106279
|
+
/**
|
|
106280
|
+
* Check if query tool was explicitly listed in allowedTools (not via wildcard).
|
|
106281
|
+
* Query (ast-grep) is excluded by default because models struggle with AST pattern syntax.
|
|
106282
|
+
* @returns {boolean}
|
|
106283
|
+
* @private
|
|
106284
|
+
*/
|
|
106285
|
+
_isQueryExplicitlyAllowed() {
|
|
106286
|
+
if (!this._rawAllowedTools) return false;
|
|
106287
|
+
return Array.isArray(this._rawAllowedTools) && this._rawAllowedTools.includes("query");
|
|
106288
|
+
}
|
|
106275
106289
|
/**
|
|
106276
106290
|
* Check if tracer is AppTracer (expects sessionId as first param) vs SimpleAppTracer
|
|
106277
106291
|
* @returns {boolean} - True if tracer is AppTracer style (requires sessionId)
|
|
@@ -106565,7 +106579,7 @@ var init_ProbeAgent = __esm({
|
|
|
106565
106579
|
if (wrappedTools.searchToolInstance && isToolAllowed("search")) {
|
|
106566
106580
|
this.toolImplementations.search = wrappedTools.searchToolInstance;
|
|
106567
106581
|
}
|
|
106568
|
-
if (wrappedTools.queryToolInstance && isToolAllowed("query")) {
|
|
106582
|
+
if (wrappedTools.queryToolInstance && isToolAllowed("query") && this._isQueryExplicitlyAllowed()) {
|
|
106569
106583
|
this.toolImplementations.query = wrappedTools.queryToolInstance;
|
|
106570
106584
|
}
|
|
106571
106585
|
if (wrappedTools.extractToolInstance && isToolAllowed("extract")) {
|
|
@@ -107504,12 +107518,13 @@ var init_ProbeAgent = __esm({
|
|
|
107504
107518
|
const toolMap = {
|
|
107505
107519
|
search: {
|
|
107506
107520
|
schema: searchSchema,
|
|
107507
|
-
description: "Search code in the repository using keyword queries with Elasticsearch syntax."
|
|
107508
|
-
},
|
|
107509
|
-
query: {
|
|
107510
|
-
schema: querySchema,
|
|
107511
|
-
description: "Search code using ast-grep structural pattern matching."
|
|
107521
|
+
description: this.searchDelegate ? "Search code in the repository by asking a question. Accepts natural language questions \u2014 a subagent breaks them into targeted keyword searches and returns extracted code blocks. Do NOT formulate keyword queries yourself." : "Search code in the repository using keyword queries with Elasticsearch syntax. Handles stemming, case-insensitive matching, and camelCase/snake_case splitting automatically \u2014 do NOT try keyword variations manually."
|
|
107512
107522
|
},
|
|
107523
|
+
// query tool (ast-grep) removed from AI-facing tools — models struggle with pattern syntax
|
|
107524
|
+
// query: {
|
|
107525
|
+
// schema: querySchema,
|
|
107526
|
+
// description: 'Search code using ast-grep structural pattern matching.'
|
|
107527
|
+
// },
|
|
107513
107528
|
extract: {
|
|
107514
107529
|
schema: extractSchema,
|
|
107515
107530
|
description: "Extract code blocks from files based on file paths and optional line numbers."
|
|
@@ -108177,25 +108192,27 @@ ${this.architectureContext.content}
|
|
|
108177
108192
|
} else {
|
|
108178
108193
|
systemPrompt += predefinedPrompts["code-explorer"] + "\n\n";
|
|
108179
108194
|
}
|
|
108195
|
+
const searchToolDesc1 = this.searchDelegate ? '- search: Ask natural language questions to find code (e.g., "How does authentication work?"). A subagent handles keyword searches and returns extracted code blocks. Do NOT formulate keyword queries \u2014 just ask questions.' : "- search: Find code patterns using keyword queries with Elasticsearch syntax. Handles stemming and case variations automatically \u2014 do NOT try manual keyword variations.";
|
|
108180
108196
|
systemPrompt += `You have access to powerful code search and analysis tools through MCP:
|
|
108181
|
-
|
|
108197
|
+
${searchToolDesc1}
|
|
108182
108198
|
- extract: Extract specific code sections with context
|
|
108183
|
-
- query: Use AST patterns for structural code matching
|
|
108184
108199
|
- listFiles: Browse directory contents
|
|
108185
108200
|
- searchFiles: Find files by name patterns`;
|
|
108186
108201
|
if (this.enableBash) {
|
|
108187
108202
|
systemPrompt += `
|
|
108188
|
-
- bash: Execute bash commands for system operations
|
|
108203
|
+
- bash: Execute bash commands for system operations (building, running tests, git, etc.). NEVER use bash for code exploration (no grep, cat, find, head, tail) \u2014 always use search and extract tools instead, they are faster and more accurate.`;
|
|
108189
108204
|
}
|
|
108190
|
-
const
|
|
108191
|
-
const
|
|
108205
|
+
const searchGuidance1 = this.searchDelegate ? "1. Start with search \u2014 ask a question about what you want to understand. It returns extracted code blocks directly." : "1. Start with search to find relevant code patterns. One search per concept is usually enough \u2014 probe handles stemming and case variations.";
|
|
108206
|
+
const extractGuidance1 = this.searchDelegate ? "2. Use extract only if you need more context or a full file" : "2. Use extract to get detailed context when needed";
|
|
108192
108207
|
systemPrompt += `
|
|
108193
108208
|
|
|
108194
108209
|
When exploring code:
|
|
108195
|
-
${
|
|
108196
|
-
${
|
|
108210
|
+
${searchGuidance1}
|
|
108211
|
+
${extractGuidance1}
|
|
108197
108212
|
3. Prefer focused, specific searches over broad queries
|
|
108198
|
-
4.
|
|
108213
|
+
4. Do NOT repeat the same search or try trivial keyword variations \u2014 probe handles stemming and case variations automatically
|
|
108214
|
+
5. If 2-3 consecutive searches return no results for a concept, stop searching for it \u2014 the term likely does not exist in that codebase
|
|
108215
|
+
6. Combine multiple tools to build complete understanding`;
|
|
108199
108216
|
if (this.allowedFolders && this.allowedFolders.length > 0) {
|
|
108200
108217
|
systemPrompt += `
|
|
108201
108218
|
|
|
@@ -108230,25 +108247,27 @@ Workspace: ${this.allowedFolders.join(", ")}`;
|
|
|
108230
108247
|
} else {
|
|
108231
108248
|
systemPrompt += predefinedPrompts["code-explorer"] + "\n\n";
|
|
108232
108249
|
}
|
|
108250
|
+
const searchToolDesc2 = this.searchDelegate ? '- search: Ask natural language questions to find code (e.g., "How does authentication work?"). A subagent handles keyword searches and returns extracted code blocks. Do NOT formulate keyword queries \u2014 just ask questions.' : "- search: Find code patterns using keyword queries with Elasticsearch syntax. Handles stemming and case variations automatically \u2014 do NOT try manual keyword variations.";
|
|
108233
108251
|
systemPrompt += `You have access to powerful code search and analysis tools through MCP:
|
|
108234
|
-
|
|
108252
|
+
${searchToolDesc2}
|
|
108235
108253
|
- extract: Extract specific code sections with context
|
|
108236
|
-
- query: Use AST patterns for structural code matching
|
|
108237
108254
|
- listFiles: Browse directory contents
|
|
108238
108255
|
- searchFiles: Find files by name patterns`;
|
|
108239
108256
|
if (this.enableBash) {
|
|
108240
108257
|
systemPrompt += `
|
|
108241
|
-
- bash: Execute bash commands for system operations
|
|
108258
|
+
- bash: Execute bash commands for system operations (building, running tests, git, etc.). NEVER use bash for code exploration (no grep, cat, find, head, tail) \u2014 always use search and extract tools instead, they are faster and more accurate.`;
|
|
108242
108259
|
}
|
|
108243
|
-
const
|
|
108244
|
-
const
|
|
108260
|
+
const searchGuidance2 = this.searchDelegate ? "1. Start with search \u2014 ask a question about what you want to understand. It returns extracted code blocks directly." : "1. Start with search to find relevant code patterns. One search per concept is usually enough \u2014 probe handles stemming and case variations.";
|
|
108261
|
+
const extractGuidance2 = this.searchDelegate ? "2. Use extract only if you need more context or a full file" : "2. Use extract to get detailed context when needed";
|
|
108245
108262
|
systemPrompt += `
|
|
108246
108263
|
|
|
108247
108264
|
When exploring code:
|
|
108248
|
-
${
|
|
108249
|
-
${
|
|
108265
|
+
${searchGuidance2}
|
|
108266
|
+
${extractGuidance2}
|
|
108250
108267
|
3. Prefer focused, specific searches over broad queries
|
|
108251
|
-
4.
|
|
108268
|
+
4. Do NOT repeat the same search or try trivial keyword variations \u2014 probe handles stemming and case variations automatically
|
|
108269
|
+
5. If 2-3 consecutive searches return no results for a concept, stop searching for it \u2014 the term likely does not exist in that codebase
|
|
108270
|
+
6. Combine multiple tools to build complete understanding`;
|
|
108252
108271
|
if (this.allowedFolders && this.allowedFolders.length > 0) {
|
|
108253
108272
|
systemPrompt += `
|
|
108254
108273
|
|
|
@@ -108299,10 +108318,11 @@ Workspace: ${this.allowedFolders.join(", ")}`;
|
|
|
108299
108318
|
Follow these instructions carefully:
|
|
108300
108319
|
1. Analyze the user's request.
|
|
108301
108320
|
2. Use the available tools step-by-step to fulfill the request.
|
|
108302
|
-
3. You should always prefer the search tool for code-related questions.${this.searchDelegate ? "
|
|
108321
|
+
3. You should always prefer the search tool for code-related questions.${this.searchDelegate ? " Ask natural language questions \u2014 the search subagent handles keyword formulation and returns extracted code blocks. Use extract only to expand context or read full files." : " Search handles stemming and case variations automatically \u2014 do NOT try keyword variations manually. Read full files only if really necessary."}
|
|
108303
108322
|
4. Ensure to get really deep and understand the full picture before answering.
|
|
108304
108323
|
5. Once the task is fully completed, use the attempt_completion tool to provide the final result.
|
|
108305
|
-
6. Prefer concise and focused search queries. Use specific keywords and phrases to narrow down results
|
|
108324
|
+
6. ${this.searchDelegate ? "Ask clear, specific questions when searching. Each search should target a distinct concept or question." : "Prefer concise and focused search queries. Use specific keywords and phrases to narrow down results."}
|
|
108325
|
+
7. NEVER use bash for code exploration (no grep, cat, find, head, tail, awk, sed) \u2014 always use search and extract tools instead. Bash is only for system operations like building, running tests, or git commands.${this.allowEdit ? `
|
|
108306
108326
|
7. When modifying files, choose the appropriate tool:
|
|
108307
108327
|
- Use 'edit' for all code modifications:
|
|
108308
108328
|
* PREFERRED: Use start_line (and optionally end_line) for line-targeted editing \u2014 this is the safest and most precise approach.${this.hashLines ? ' Use the line:hash references from extract/search output (e.g. "42:ab") for integrity verification.' : ""} Always use extract first to see line numbers${this.hashLines ? " and hashes" : ""}, then edit by line reference.
|
|
@@ -108316,7 +108336,14 @@ Follow these instructions carefully:
|
|
|
108316
108336
|
</instructions>
|
|
108317
108337
|
`;
|
|
108318
108338
|
let systemMessage = "";
|
|
108319
|
-
if (this.customPrompt) {
|
|
108339
|
+
if (this.customPrompt && this.promptType && predefinedPrompts[this.promptType]) {
|
|
108340
|
+
systemMessage = "<role>" + predefinedPrompts[this.promptType] + "</role>";
|
|
108341
|
+
systemMessage += commonInstructions;
|
|
108342
|
+
systemMessage += "\n<custom-instructions>\n" + this.customPrompt + "\n</custom-instructions>";
|
|
108343
|
+
if (this.debug) {
|
|
108344
|
+
console.log(`[DEBUG] Using predefined prompt: ${this.promptType} + custom prompt`);
|
|
108345
|
+
}
|
|
108346
|
+
} else if (this.customPrompt) {
|
|
108320
108347
|
systemMessage = "<role>" + this.customPrompt + "</role>";
|
|
108321
108348
|
if (this.debug) {
|
|
108322
108349
|
console.log(`[DEBUG] Using custom prompt`);
|
|
@@ -109895,10 +109922,9 @@ var init_delegate = __esm({
|
|
|
109895
109922
|
if (this.tryAcquire(parentSessionId)) {
|
|
109896
109923
|
return true;
|
|
109897
109924
|
}
|
|
109898
|
-
|
|
109899
|
-
console.error(`[DelegationManager] Slot unavailable (${this.globalActive}/${this.maxConcurrent}), queuing... (queue size: ${this.waitQueue.length}, timeout: ${effectiveTimeout}ms)`);
|
|
109900
|
-
}
|
|
109925
|
+
console.error(`[DelegationManager] Slot unavailable (${this.globalActive}/${this.maxConcurrent}), queuing... (queue size: ${this.waitQueue.length + 1}, timeout: ${effectiveTimeout}ms)`);
|
|
109901
109926
|
return new Promise((resolve8, reject2) => {
|
|
109927
|
+
const queuedAt = Date.now();
|
|
109902
109928
|
const entry = {
|
|
109903
109929
|
resolve: null,
|
|
109904
109930
|
// Will be wrapped below
|
|
@@ -109906,20 +109932,23 @@ var init_delegate = __esm({
|
|
|
109906
109932
|
// Will be wrapped below
|
|
109907
109933
|
parentSessionId,
|
|
109908
109934
|
debug,
|
|
109909
|
-
queuedAt
|
|
109910
|
-
timeoutId: null
|
|
109935
|
+
queuedAt,
|
|
109936
|
+
timeoutId: null,
|
|
109937
|
+
reminderId: null
|
|
109911
109938
|
};
|
|
109912
109939
|
let settled = false;
|
|
109913
109940
|
entry.resolve = (value) => {
|
|
109914
109941
|
if (settled) return;
|
|
109915
109942
|
settled = true;
|
|
109916
109943
|
if (entry.timeoutId) clearTimeout(entry.timeoutId);
|
|
109944
|
+
if (entry.reminderId) clearInterval(entry.reminderId);
|
|
109917
109945
|
resolve8(value);
|
|
109918
109946
|
};
|
|
109919
109947
|
entry.reject = (error2) => {
|
|
109920
109948
|
if (settled) return;
|
|
109921
109949
|
settled = true;
|
|
109922
109950
|
if (entry.timeoutId) clearTimeout(entry.timeoutId);
|
|
109951
|
+
if (entry.reminderId) clearInterval(entry.reminderId);
|
|
109923
109952
|
reject2(error2);
|
|
109924
109953
|
};
|
|
109925
109954
|
if (effectiveTimeout > 0) {
|
|
@@ -109931,6 +109960,13 @@ var init_delegate = __esm({
|
|
|
109931
109960
|
entry.reject(new Error(`Delegation queue timeout: waited ${effectiveTimeout}ms for an available slot`));
|
|
109932
109961
|
}, effectiveTimeout);
|
|
109933
109962
|
}
|
|
109963
|
+
entry.reminderId = setInterval(() => {
|
|
109964
|
+
const waitedSeconds = Math.round((Date.now() - queuedAt) / 1e3);
|
|
109965
|
+
console.error(`[DelegationManager] Still waiting for slot (${waitedSeconds}s). ${this.globalActive}/${this.maxConcurrent} active, ${this.waitQueue.length} queued.`);
|
|
109966
|
+
}, 15e3);
|
|
109967
|
+
if (entry.reminderId.unref) {
|
|
109968
|
+
entry.reminderId.unref();
|
|
109969
|
+
}
|
|
109934
109970
|
this.waitQueue.push(entry);
|
|
109935
109971
|
});
|
|
109936
109972
|
}
|
|
@@ -109969,18 +110005,14 @@ var init_delegate = __esm({
|
|
|
109969
110005
|
const sessionData = this.sessionDelegations.get(parentSessionId);
|
|
109970
110006
|
const sessionCount = sessionData?.count || 0;
|
|
109971
110007
|
if (sessionCount >= this.maxPerSession) {
|
|
109972
|
-
|
|
109973
|
-
console.error(`[DelegationManager] Session limit (${this.maxPerSession}) reached for queued item, rejecting`);
|
|
109974
|
-
}
|
|
110008
|
+
console.error(`[DelegationManager] Session limit (${this.maxPerSession}) reached for queued item, rejecting`);
|
|
109975
110009
|
toReject.push({ reject: reject2, error: new Error(`Maximum delegations per session (${this.maxPerSession}) reached for session ${parentSessionId}`) });
|
|
109976
110010
|
continue;
|
|
109977
110011
|
}
|
|
109978
110012
|
}
|
|
109979
110013
|
this._incrementCounters(parentSessionId);
|
|
109980
|
-
|
|
109981
|
-
|
|
109982
|
-
console.error(`[DelegationManager] Granted slot from queue (waited ${waitTime}ms). Active: ${this.globalActive}/${this.maxConcurrent}`);
|
|
109983
|
-
}
|
|
110014
|
+
const waitTime = Date.now() - queuedAt;
|
|
110015
|
+
console.error(`[DelegationManager] Granted slot from queue (waited ${waitTime}ms). Active: ${this.globalActive}/${this.maxConcurrent}`);
|
|
109984
110016
|
toResolve.push(resolve8);
|
|
109985
110017
|
}
|
|
109986
110018
|
if (toResolve.length > 0 || toReject.length > 0) {
|
|
@@ -110030,6 +110062,9 @@ var init_delegate = __esm({
|
|
|
110030
110062
|
if (entry.timeoutId) {
|
|
110031
110063
|
clearTimeout(entry.timeoutId);
|
|
110032
110064
|
}
|
|
110065
|
+
if (entry.reminderId) {
|
|
110066
|
+
clearInterval(entry.reminderId);
|
|
110067
|
+
}
|
|
110033
110068
|
if (entry.reject) {
|
|
110034
110069
|
entry.reject(new Error("DelegationManager was cleaned up"));
|
|
110035
110070
|
}
|
|
@@ -110174,16 +110209,12 @@ async function processChunksParallel(chunks, extractionPrompt, maxWorkers, optio
|
|
|
110174
110209
|
return result;
|
|
110175
110210
|
});
|
|
110176
110211
|
active.add(promise);
|
|
110177
|
-
|
|
110178
|
-
console.error(`[analyze_all] Started processing chunk ${chunk.id}/${chunk.total}`);
|
|
110179
|
-
}
|
|
110212
|
+
console.error(`[analyze_all] Started processing chunk ${chunk.id}/${chunk.total}`);
|
|
110180
110213
|
}
|
|
110181
110214
|
if (active.size > 0) {
|
|
110182
110215
|
const result = await Promise.race(active);
|
|
110183
110216
|
results.push(result);
|
|
110184
|
-
|
|
110185
|
-
console.error(`[analyze_all] Completed chunk ${result.chunk.id}/${result.chunk.total}`);
|
|
110186
|
-
}
|
|
110217
|
+
console.error(`[analyze_all] Completed chunk ${result.chunk.id}/${result.chunk.total}`);
|
|
110187
110218
|
}
|
|
110188
110219
|
}
|
|
110189
110220
|
results.sort((a5, b5) => a5.chunk.id - b5.chunk.id);
|
|
@@ -110693,11 +110724,41 @@ function buildSearchDelegateTask({ searchQuery, searchPath, exact, language, all
|
|
|
110693
110724
|
"- extract: Verify code snippets to ensure targets are actually relevant before including them.",
|
|
110694
110725
|
"- listFiles: Understand directory structure to find where relevant code might live.",
|
|
110695
110726
|
"",
|
|
110696
|
-
"
|
|
110727
|
+
"CRITICAL - How probe search works (do NOT ignore):",
|
|
110728
|
+
"- By default (exact=false), probe ALREADY handles stemming, case-insensitive matching, and camelCase/snake_case splitting.",
|
|
110729
|
+
'- Searching "allowed_ips" ALREADY matches "AllowedIPs", "allowedIps", "allowed_ips", etc. Do NOT manually try case/style variations.',
|
|
110730
|
+
'- Searching "getUserData" ALREADY matches "get", "user", "data" and their variations.',
|
|
110731
|
+
"- NEVER repeat the same search query \u2014 you will get the same results.",
|
|
110732
|
+
"- NEVER search trivial variations of the same keyword (e.g., AllowedIPs then allowedIps then allowed_ips). This is wasteful \u2014 probe handles it.",
|
|
110733
|
+
"- If a search returns no results, the term likely does not exist in that path. Try a genuinely DIFFERENT keyword or concept, not a variation.",
|
|
110734
|
+
"- If 2-3 consecutive searches return no results for a concept, STOP searching for it and move on.",
|
|
110735
|
+
"",
|
|
110736
|
+
"GOOD search strategy (do this):",
|
|
110737
|
+
' Query: "How does authentication work and how are sessions managed?"',
|
|
110738
|
+
' \u2192 search "authentication" \u2192 search "session management" (two different concepts)',
|
|
110739
|
+
' Query: "Find the IP allowlist middleware"',
|
|
110740
|
+
' \u2192 search "allowlist middleware" (one search, probe handles IP/ip/Ip variations)',
|
|
110741
|
+
' Query: "How does BM25 scoring work with SIMD optimization?"',
|
|
110742
|
+
' \u2192 search "BM25 scoring" \u2192 search "SIMD optimization" (two different concepts)',
|
|
110743
|
+
"",
|
|
110744
|
+
"BAD search strategy (never do this):",
|
|
110745
|
+
' \u2192 search "AllowedIPs" \u2192 search "allowedIps" \u2192 search "allowed_ips" (WRONG: these are trivial case variations, probe handles them)',
|
|
110746
|
+
' \u2192 search "CIDR" \u2192 search "cidr" \u2192 search "Cidr" \u2192 search "*cidr*" (WRONG: same keyword repeated with variations)',
|
|
110747
|
+
' \u2192 search "error handling" \u2192 search "error handling" \u2192 search "error handling" (WRONG: repeating exact same query)',
|
|
110748
|
+
"",
|
|
110749
|
+
"Keyword tips:",
|
|
110750
|
+
"- Common programming keywords are filtered as stopwords when unquoted: function, class, return, new, struct, impl, var, let, const, etc.",
|
|
110751
|
+
'- Avoid searching for these alone \u2014 combine with a specific term (e.g., "middleware function" is fine, "function" alone is too generic).',
|
|
110752
|
+
'- To bypass stopword filtering: wrap terms in quotes ("return", "struct") or set exact=true. Both disable stemming and splitting too.',
|
|
110753
|
+
"- Multiple words without operators use OR logic: foo bar = foo OR bar. Use AND explicitly if you need both: foo AND bar.",
|
|
110754
|
+
'- camelCase terms are split: getUserData becomes "get", "user", "data" \u2014 so one search covers all naming styles.',
|
|
110755
|
+
"",
|
|
110756
|
+
"Strategy:",
|
|
110697
110757
|
"1. Analyze the query - identify key concepts, entities, and relationships",
|
|
110698
|
-
|
|
110699
|
-
"3.
|
|
110700
|
-
"4.
|
|
110758
|
+
"2. Run ONE focused search per concept with the most natural keyword. Trust probe to handle variations.",
|
|
110759
|
+
"3. If a search returns results, use extract to verify relevance",
|
|
110760
|
+
"4. Only try a different keyword if the first one returned irrelevant results (not if it returned no results \u2014 that means the concept is absent)",
|
|
110761
|
+
"5. Combine all relevant targets in your final response",
|
|
110701
110762
|
"",
|
|
110702
110763
|
`Query: ${searchQuery}`,
|
|
110703
110764
|
`Search path(s): ${searchPath}`,
|
|
@@ -110750,9 +110811,12 @@ var init_vercel = __esm({
|
|
|
110750
110811
|
}
|
|
110751
110812
|
return result;
|
|
110752
110813
|
};
|
|
110814
|
+
const previousSearches = /* @__PURE__ */ new Set();
|
|
110815
|
+
const paginationCounts = /* @__PURE__ */ new Map();
|
|
110816
|
+
const MAX_PAGES_PER_QUERY = 3;
|
|
110753
110817
|
return (0, import_ai5.tool)({
|
|
110754
110818
|
name: "search",
|
|
110755
|
-
description: searchDelegate ?
|
|
110819
|
+
description: searchDelegate ? searchDelegateDescription : searchDescription,
|
|
110756
110820
|
inputSchema: searchSchema,
|
|
110757
110821
|
execute: async ({ query: searchQuery, path: path9, allow_tests, exact, maxTokens: paramMaxTokens, language, session, nextPage }) => {
|
|
110758
110822
|
const effectiveMaxTokens = paramMaxTokens || maxTokens;
|
|
@@ -110790,6 +110854,26 @@ var init_vercel = __esm({
|
|
|
110790
110854
|
return await search(searchOptions);
|
|
110791
110855
|
};
|
|
110792
110856
|
if (!searchDelegate) {
|
|
110857
|
+
const searchKey = `${searchQuery}::${searchPath}::${exact || false}`;
|
|
110858
|
+
if (!nextPage) {
|
|
110859
|
+
if (previousSearches.has(searchKey)) {
|
|
110860
|
+
if (debug) {
|
|
110861
|
+
console.error(`[DEDUP] Blocked duplicate search: "${searchQuery}" in "${searchPath}"`);
|
|
110862
|
+
}
|
|
110863
|
+
return "DUPLICATE SEARCH BLOCKED: You already searched for this exact query in this path. Do NOT repeat the same search. If you need more results, set nextPage=true with the session ID from the previous search. Otherwise, try a genuinely different keyword, use extract to examine results you already found, or use attempt_completion if you have enough information.";
|
|
110864
|
+
}
|
|
110865
|
+
previousSearches.add(searchKey);
|
|
110866
|
+
paginationCounts.set(searchKey, 0);
|
|
110867
|
+
} else {
|
|
110868
|
+
const pageCount = (paginationCounts.get(searchKey) || 0) + 1;
|
|
110869
|
+
paginationCounts.set(searchKey, pageCount);
|
|
110870
|
+
if (pageCount > MAX_PAGES_PER_QUERY) {
|
|
110871
|
+
if (debug) {
|
|
110872
|
+
console.error(`[DEDUP] Blocked excessive pagination (page ${pageCount}/${MAX_PAGES_PER_QUERY}): "${searchQuery}" in "${searchPath}"`);
|
|
110873
|
+
}
|
|
110874
|
+
return `PAGINATION LIMIT REACHED: You have already retrieved ${MAX_PAGES_PER_QUERY} pages of results for this query. You have enough results \u2014 use extract to examine specific files, or use attempt_completion to return your findings.`;
|
|
110875
|
+
}
|
|
110876
|
+
}
|
|
110793
110877
|
try {
|
|
110794
110878
|
const result = maybeAnnotate(await runRawSearch());
|
|
110795
110879
|
if (options.fileTracker && typeof result === "string") {
|