@probelabs/probe 0.6.0-rc302 → 0.6.0-rc304
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-rc302-aarch64-apple-darwin.tar.gz → probe-v0.6.0-rc304-aarch64-apple-darwin.tar.gz} +0 -0
- package/bin/binaries/{probe-v0.6.0-rc302-aarch64-unknown-linux-musl.tar.gz → probe-v0.6.0-rc304-aarch64-unknown-linux-musl.tar.gz} +0 -0
- package/bin/binaries/{probe-v0.6.0-rc302-x86_64-apple-darwin.tar.gz → probe-v0.6.0-rc304-x86_64-apple-darwin.tar.gz} +0 -0
- package/bin/binaries/{probe-v0.6.0-rc302-x86_64-pc-windows-msvc.zip → probe-v0.6.0-rc304-x86_64-pc-windows-msvc.zip} +0 -0
- package/bin/binaries/{probe-v0.6.0-rc302-x86_64-unknown-linux-musl.tar.gz → probe-v0.6.0-rc304-x86_64-unknown-linux-musl.tar.gz} +0 -0
- package/build/agent/FallbackManager.js +3 -57
- package/build/agent/ProbeAgent.js +48 -62
- package/build/delegate.js +15 -4
- package/build/tools/common.js +16 -1
- package/build/tools/vercel.js +448 -209
- package/build/utils/provider.js +106 -0
- package/cjs/agent/ProbeAgent.cjs +1078 -305
- package/cjs/index.cjs +529 -303
- package/package.json +1 -1
- package/src/agent/FallbackManager.js +3 -57
- package/src/agent/ProbeAgent.js +48 -62
- package/src/delegate.js +15 -4
- package/src/tools/common.js +16 -1
- package/src/tools/vercel.js +448 -209
- package/src/utils/provider.js +106 -0
package/cjs/index.cjs
CHANGED
|
@@ -24138,6 +24138,87 @@ var init_dist3 = __esm({
|
|
|
24138
24138
|
}
|
|
24139
24139
|
});
|
|
24140
24140
|
|
|
24141
|
+
// src/utils/provider.js
|
|
24142
|
+
function createProviderInstance(config2) {
|
|
24143
|
+
switch (config2.provider) {
|
|
24144
|
+
case "anthropic":
|
|
24145
|
+
return (0, import_anthropic.createAnthropic)({
|
|
24146
|
+
apiKey: config2.apiKey,
|
|
24147
|
+
...config2.baseURL && { baseURL: config2.baseURL }
|
|
24148
|
+
});
|
|
24149
|
+
case "openai":
|
|
24150
|
+
return (0, import_openai.createOpenAI)({
|
|
24151
|
+
compatibility: "strict",
|
|
24152
|
+
apiKey: config2.apiKey,
|
|
24153
|
+
...config2.baseURL && { baseURL: config2.baseURL }
|
|
24154
|
+
});
|
|
24155
|
+
case "google":
|
|
24156
|
+
return (0, import_google.createGoogleGenerativeAI)({
|
|
24157
|
+
apiKey: config2.apiKey,
|
|
24158
|
+
...config2.baseURL && { baseURL: config2.baseURL }
|
|
24159
|
+
});
|
|
24160
|
+
case "bedrock": {
|
|
24161
|
+
const bedrockConfig = {};
|
|
24162
|
+
if (config2.apiKey) {
|
|
24163
|
+
bedrockConfig.apiKey = config2.apiKey;
|
|
24164
|
+
} else if (config2.accessKeyId && config2.secretAccessKey) {
|
|
24165
|
+
bedrockConfig.accessKeyId = config2.accessKeyId;
|
|
24166
|
+
bedrockConfig.secretAccessKey = config2.secretAccessKey;
|
|
24167
|
+
if (config2.sessionToken) {
|
|
24168
|
+
bedrockConfig.sessionToken = config2.sessionToken;
|
|
24169
|
+
}
|
|
24170
|
+
}
|
|
24171
|
+
if (config2.region) bedrockConfig.region = config2.region;
|
|
24172
|
+
if (config2.baseURL) bedrockConfig.baseURL = config2.baseURL;
|
|
24173
|
+
return createAmazonBedrock(bedrockConfig);
|
|
24174
|
+
}
|
|
24175
|
+
default:
|
|
24176
|
+
throw new Error(`Unknown provider "${config2.provider}"`);
|
|
24177
|
+
}
|
|
24178
|
+
}
|
|
24179
|
+
function resolveApiKey(providerName) {
|
|
24180
|
+
switch (providerName) {
|
|
24181
|
+
case "anthropic":
|
|
24182
|
+
return process.env.ANTHROPIC_API_KEY || process.env.ANTHROPIC_AUTH_TOKEN;
|
|
24183
|
+
case "openai":
|
|
24184
|
+
return process.env.OPENAI_API_KEY;
|
|
24185
|
+
case "google":
|
|
24186
|
+
return process.env.GOOGLE_GENERATIVE_AI_API_KEY || process.env.GOOGLE_API_KEY || process.env.GEMINI_API_KEY;
|
|
24187
|
+
case "bedrock":
|
|
24188
|
+
return process.env.AWS_BEDROCK_API_KEY;
|
|
24189
|
+
default:
|
|
24190
|
+
return void 0;
|
|
24191
|
+
}
|
|
24192
|
+
}
|
|
24193
|
+
async function createLanguageModel(providerName, modelName) {
|
|
24194
|
+
if (!providerName) return null;
|
|
24195
|
+
const resolvedModel = modelName || DEFAULT_MODELS[providerName];
|
|
24196
|
+
if (!resolvedModel) return null;
|
|
24197
|
+
try {
|
|
24198
|
+
const apiKey = resolveApiKey(providerName);
|
|
24199
|
+
const provider = createProviderInstance({ provider: providerName, ...apiKey ? { apiKey } : {} });
|
|
24200
|
+
return provider(resolvedModel);
|
|
24201
|
+
} catch {
|
|
24202
|
+
return null;
|
|
24203
|
+
}
|
|
24204
|
+
}
|
|
24205
|
+
var import_anthropic, import_openai, import_google, DEFAULT_MODELS;
|
|
24206
|
+
var init_provider = __esm({
|
|
24207
|
+
"src/utils/provider.js"() {
|
|
24208
|
+
"use strict";
|
|
24209
|
+
import_anthropic = require("@ai-sdk/anthropic");
|
|
24210
|
+
import_openai = require("@ai-sdk/openai");
|
|
24211
|
+
import_google = require("@ai-sdk/google");
|
|
24212
|
+
init_dist3();
|
|
24213
|
+
DEFAULT_MODELS = {
|
|
24214
|
+
anthropic: "claude-sonnet-4-6",
|
|
24215
|
+
openai: "gpt-5.2",
|
|
24216
|
+
google: "gemini-2.5-flash",
|
|
24217
|
+
bedrock: "anthropic.claude-sonnet-4-6"
|
|
24218
|
+
};
|
|
24219
|
+
}
|
|
24220
|
+
});
|
|
24221
|
+
|
|
24141
24222
|
// node_modules/gpt-tokenizer/esm/bpeRanks/o200k_base.js
|
|
24142
24223
|
var c0, c1, bpe, o200k_base_default;
|
|
24143
24224
|
var init_o200k_base = __esm({
|
|
@@ -25993,6 +26074,13 @@ var init_otelLogBridge = __esm({
|
|
|
25993
26074
|
});
|
|
25994
26075
|
|
|
25995
26076
|
// src/agent/simpleTelemetry.js
|
|
26077
|
+
var simpleTelemetry_exports = {};
|
|
26078
|
+
__export(simpleTelemetry_exports, {
|
|
26079
|
+
SimpleAppTracer: () => SimpleAppTracer,
|
|
26080
|
+
SimpleTelemetry: () => SimpleTelemetry,
|
|
26081
|
+
initializeSimpleTelemetryFromOptions: () => initializeSimpleTelemetryFromOptions,
|
|
26082
|
+
truncateForSpan: () => truncateForSpan
|
|
26083
|
+
});
|
|
25996
26084
|
function truncateForSpan(text, maxLen = 4096) {
|
|
25997
26085
|
if (!text || text.length <= maxLen) return text || "";
|
|
25998
26086
|
const half = Math.floor((maxLen - 40) / 2);
|
|
@@ -27718,12 +27806,16 @@ function resolveTargetPath(target, cwd) {
|
|
|
27718
27806
|
}
|
|
27719
27807
|
return filePart + suffix;
|
|
27720
27808
|
}
|
|
27721
|
-
var import_path6, searchSchema, searchAllSchema, querySchema, extractSchema, delegateSchema, listSkillsSchema, useSkillSchema, listFilesSchema, searchFilesSchema, readImageSchema, bashSchema, analyzeAllSchema, executePlanSchema, cleanupExecutePlanSchema, searchDescription, searchDelegateDescription, queryDescription, extractDescription, delegateDescription, bashDescription, analyzeAllDescription;
|
|
27809
|
+
var import_path6, searchDelegateSchema, searchSchema, searchAllSchema, querySchema, extractSchema, delegateSchema, listSkillsSchema, useSkillSchema, listFilesSchema, searchFilesSchema, readImageSchema, bashSchema, analyzeAllSchema, executePlanSchema, cleanupExecutePlanSchema, searchDescription, searchDelegateDescription, queryDescription, extractDescription, delegateDescription, bashDescription, analyzeAllDescription;
|
|
27722
27810
|
var init_common = __esm({
|
|
27723
27811
|
"src/tools/common.js"() {
|
|
27724
27812
|
"use strict";
|
|
27725
27813
|
init_zod();
|
|
27726
27814
|
import_path6 = require("path");
|
|
27815
|
+
searchDelegateSchema = external_exports2.object({
|
|
27816
|
+
query: external_exports2.string().describe('Natural language question about the code (e.g., "How does authentication work?", "Where is the rate limiting middleware?"). Do NOT use keyword syntax \u2014 just describe what you are looking for in plain English. A subagent will handle keyword searches for you.'),
|
|
27817
|
+
path: external_exports2.string().optional().default(".").describe("Path to search in.")
|
|
27818
|
+
});
|
|
27727
27819
|
searchSchema = external_exports2.object({
|
|
27728
27820
|
query: external_exports2.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."),
|
|
27729
27821
|
path: external_exports2.string().optional().default(".").describe('Path to search in. For dependencies use "go:github.com/owner/repo", "js:package_name", or "rust:cargo_name" etc.'),
|
|
@@ -27789,7 +27881,17 @@ var init_common = __esm({
|
|
|
27789
27881
|
clearSessionStore: external_exports2.boolean().optional().default(false).describe("Clear the session store (persisted data across execute_plan calls)")
|
|
27790
27882
|
});
|
|
27791
27883
|
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.';
|
|
27792
|
-
searchDelegateDescription =
|
|
27884
|
+
searchDelegateDescription = `Find where relevant code is located by asking a natural language question. A subagent searches the codebase and returns file locations grouped by relevance, with reasons explaining why each group matters. Use extract() to read the actual code from the returned locations.
|
|
27885
|
+
|
|
27886
|
+
Returns JSON: { "confidence": "high|medium|low", "groups": [{ "reason": "why these files matter", "files": ["path#Symbol", ...] }] }
|
|
27887
|
+
|
|
27888
|
+
IMPORTANT \u2014 each call spawns a subagent (expensive, takes minutes). Be deliberate:
|
|
27889
|
+
- Ask plain English questions about WHERE code is, NOT keyword queries. Good: "How are user sessions extracted from cookies?" Bad: "ctxGetSession OR GetSession"
|
|
27890
|
+
- Each call should explore a DIFFERENT ANGLE of the problem. Don't rephrase \u2014 reframe:
|
|
27891
|
+
Good: 1) "How are sessions extracted from HTTP requests?" 2) "What middleware runs before route handlers?" 3) "How is the session cookie parsed and validated?"
|
|
27892
|
+
Bad: 1) "How does session extraction work?" 2) "Where is the session extracted?" 3) "Find session extraction code" \u2190 same question reworded
|
|
27893
|
+
- If a search returned no useful results, ask about a DIFFERENT part of the system. Think: what upstream/downstream component touches this?
|
|
27894
|
+
- After getting results, use extract() to read the files you need \u2014 search only locates, extract reads.`;
|
|
27793
27895
|
queryDescription = "Search code using ast-grep structural pattern matching. Use this tool to find specific code structures like functions, classes, or methods.";
|
|
27794
27896
|
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.";
|
|
27795
27897
|
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.";
|
|
@@ -83019,14 +83121,11 @@ function buildFallbackProvidersFromEnv(options = {}) {
|
|
|
83019
83121
|
}
|
|
83020
83122
|
return providers;
|
|
83021
83123
|
}
|
|
83022
|
-
var
|
|
83124
|
+
var FALLBACK_STRATEGIES, DEFAULT_MODELS2, FallbackManager;
|
|
83023
83125
|
var init_FallbackManager = __esm({
|
|
83024
83126
|
"src/agent/FallbackManager.js"() {
|
|
83025
83127
|
"use strict";
|
|
83026
|
-
|
|
83027
|
-
import_openai = require("@ai-sdk/openai");
|
|
83028
|
-
import_google = require("@ai-sdk/google");
|
|
83029
|
-
init_dist3();
|
|
83128
|
+
init_provider();
|
|
83030
83129
|
FALLBACK_STRATEGIES = {
|
|
83031
83130
|
SAME_MODEL: "same-model",
|
|
83032
83131
|
// Try same model on different providers
|
|
@@ -83037,12 +83136,7 @@ var init_FallbackManager = __esm({
|
|
|
83037
83136
|
CUSTOM: "custom"
|
|
83038
83137
|
// Use custom provider list
|
|
83039
83138
|
};
|
|
83040
|
-
|
|
83041
|
-
anthropic: "claude-sonnet-4-6",
|
|
83042
|
-
openai: "gpt-5.2",
|
|
83043
|
-
google: "gemini-2.5-flash",
|
|
83044
|
-
bedrock: "anthropic.claude-sonnet-4-6"
|
|
83045
|
-
};
|
|
83139
|
+
DEFAULT_MODELS2 = DEFAULT_MODELS;
|
|
83046
83140
|
FallbackManager = class {
|
|
83047
83141
|
/**
|
|
83048
83142
|
* Create a new FallbackManager
|
|
@@ -83115,45 +83209,7 @@ var init_FallbackManager = __esm({
|
|
|
83115
83209
|
*/
|
|
83116
83210
|
_createProviderInstance(config2) {
|
|
83117
83211
|
try {
|
|
83118
|
-
|
|
83119
|
-
case "anthropic":
|
|
83120
|
-
return (0, import_anthropic.createAnthropic)({
|
|
83121
|
-
apiKey: config2.apiKey,
|
|
83122
|
-
...config2.baseURL && { baseURL: config2.baseURL }
|
|
83123
|
-
});
|
|
83124
|
-
case "openai":
|
|
83125
|
-
return (0, import_openai.createOpenAI)({
|
|
83126
|
-
compatibility: "strict",
|
|
83127
|
-
apiKey: config2.apiKey,
|
|
83128
|
-
...config2.baseURL && { baseURL: config2.baseURL }
|
|
83129
|
-
});
|
|
83130
|
-
case "google":
|
|
83131
|
-
return (0, import_google.createGoogleGenerativeAI)({
|
|
83132
|
-
apiKey: config2.apiKey,
|
|
83133
|
-
...config2.baseURL && { baseURL: config2.baseURL }
|
|
83134
|
-
});
|
|
83135
|
-
case "bedrock": {
|
|
83136
|
-
const bedrockConfig = {};
|
|
83137
|
-
if (config2.apiKey) {
|
|
83138
|
-
bedrockConfig.apiKey = config2.apiKey;
|
|
83139
|
-
} else if (config2.accessKeyId && config2.secretAccessKey) {
|
|
83140
|
-
bedrockConfig.accessKeyId = config2.accessKeyId;
|
|
83141
|
-
bedrockConfig.secretAccessKey = config2.secretAccessKey;
|
|
83142
|
-
if (config2.sessionToken) {
|
|
83143
|
-
bedrockConfig.sessionToken = config2.sessionToken;
|
|
83144
|
-
}
|
|
83145
|
-
}
|
|
83146
|
-
if (config2.region) {
|
|
83147
|
-
bedrockConfig.region = config2.region;
|
|
83148
|
-
}
|
|
83149
|
-
if (config2.baseURL) {
|
|
83150
|
-
bedrockConfig.baseURL = config2.baseURL;
|
|
83151
|
-
}
|
|
83152
|
-
return createAmazonBedrock(bedrockConfig);
|
|
83153
|
-
}
|
|
83154
|
-
default:
|
|
83155
|
-
throw new Error(`FallbackManager: Unknown provider "${config2.provider}"`);
|
|
83156
|
-
}
|
|
83212
|
+
return createProviderInstance(config2);
|
|
83157
83213
|
} catch (error40) {
|
|
83158
83214
|
const providerName = this._getProviderDisplayName(config2);
|
|
83159
83215
|
throw new Error(`Failed to create provider instance for ${providerName}: ${error40.message}`);
|
|
@@ -83166,7 +83222,7 @@ var init_FallbackManager = __esm({
|
|
|
83166
83222
|
* @private
|
|
83167
83223
|
*/
|
|
83168
83224
|
_getModelName(config2) {
|
|
83169
|
-
return config2.model ||
|
|
83225
|
+
return config2.model || DEFAULT_MODELS2[config2.provider];
|
|
83170
83226
|
}
|
|
83171
83227
|
/**
|
|
83172
83228
|
* Get provider display name for logging
|
|
@@ -97429,15 +97485,12 @@ function debugLogToolResults(toolResults) {
|
|
|
97429
97485
|
console.log(`[DEBUG] tool: ${tr.toolName} | args: ${debugTruncate(argsStr)} | result: ${debugTruncate(resultStr)}`);
|
|
97430
97486
|
}
|
|
97431
97487
|
}
|
|
97432
|
-
var import_dotenv,
|
|
97488
|
+
var import_dotenv, import_ai4, import_crypto8, import_events4, import_fs11, import_promises6, import_path16, ENGINE_ACTIVITY_TIMEOUT_DEFAULT, ENGINE_ACTIVITY_TIMEOUT_MIN, ENGINE_ACTIVITY_TIMEOUT_MAX, MAX_TOOL_ITERATIONS, MAX_HISTORY_MESSAGES, MAX_IMAGE_FILE_SIZE, ProbeAgent;
|
|
97433
97489
|
var init_ProbeAgent = __esm({
|
|
97434
97490
|
"src/agent/ProbeAgent.js"() {
|
|
97435
97491
|
"use strict";
|
|
97436
97492
|
import_dotenv = __toESM(require_main(), 1);
|
|
97437
|
-
|
|
97438
|
-
import_openai2 = require("@ai-sdk/openai");
|
|
97439
|
-
import_google2 = require("@ai-sdk/google");
|
|
97440
|
-
init_dist3();
|
|
97493
|
+
init_provider();
|
|
97441
97494
|
import_ai4 = require("ai");
|
|
97442
97495
|
import_crypto8 = require("crypto");
|
|
97443
97496
|
import_events4 = require("events");
|
|
@@ -98676,11 +98729,8 @@ var init_ProbeAgent = __esm({
|
|
|
98676
98729
|
* Initialize Anthropic model
|
|
98677
98730
|
*/
|
|
98678
98731
|
initializeAnthropicModel(apiKey, apiUrl, modelName) {
|
|
98679
|
-
this.provider = (
|
|
98680
|
-
|
|
98681
|
-
...apiUrl && { baseURL: apiUrl }
|
|
98682
|
-
});
|
|
98683
|
-
this.model = modelName || "claude-sonnet-4-6";
|
|
98732
|
+
this.provider = createProviderInstance({ provider: "anthropic", apiKey, ...apiUrl && { baseURL: apiUrl } });
|
|
98733
|
+
this.model = modelName || DEFAULT_MODELS.anthropic;
|
|
98684
98734
|
this.apiType = "anthropic";
|
|
98685
98735
|
if (this.debug) {
|
|
98686
98736
|
console.log(`Using Anthropic API with model: ${this.model}${apiUrl ? ` (URL: ${apiUrl})` : ""}`);
|
|
@@ -98690,12 +98740,8 @@ var init_ProbeAgent = __esm({
|
|
|
98690
98740
|
* Initialize OpenAI model
|
|
98691
98741
|
*/
|
|
98692
98742
|
initializeOpenAIModel(apiKey, apiUrl, modelName) {
|
|
98693
|
-
this.provider = (
|
|
98694
|
-
|
|
98695
|
-
apiKey,
|
|
98696
|
-
...apiUrl && { baseURL: apiUrl }
|
|
98697
|
-
});
|
|
98698
|
-
this.model = modelName || "gpt-5.2";
|
|
98743
|
+
this.provider = createProviderInstance({ provider: "openai", apiKey, ...apiUrl && { baseURL: apiUrl } });
|
|
98744
|
+
this.model = modelName || DEFAULT_MODELS.openai;
|
|
98699
98745
|
this.apiType = "openai";
|
|
98700
98746
|
if (this.debug) {
|
|
98701
98747
|
console.log(`Using OpenAI API with model: ${this.model}${apiUrl ? ` (URL: ${apiUrl})` : ""}`);
|
|
@@ -98705,10 +98751,7 @@ var init_ProbeAgent = __esm({
|
|
|
98705
98751
|
* Initialize Google model
|
|
98706
98752
|
*/
|
|
98707
98753
|
initializeGoogleModel(apiKey, apiUrl, modelName) {
|
|
98708
|
-
this.provider = (
|
|
98709
|
-
apiKey,
|
|
98710
|
-
...apiUrl && { baseURL: apiUrl }
|
|
98711
|
-
});
|
|
98754
|
+
this.provider = createProviderInstance({ provider: "google", apiKey, ...apiUrl && { baseURL: apiUrl } });
|
|
98712
98755
|
this.model = modelName || "gemini-2.5-pro";
|
|
98713
98756
|
this.apiType = "google";
|
|
98714
98757
|
if (this.debug) {
|
|
@@ -99152,24 +99195,16 @@ var init_ProbeAgent = __esm({
|
|
|
99152
99195
|
* Initialize AWS Bedrock model
|
|
99153
99196
|
*/
|
|
99154
99197
|
initializeBedrockModel(accessKeyId, secretAccessKey, region, sessionToken, apiKey, baseURL, modelName) {
|
|
99155
|
-
|
|
99156
|
-
|
|
99157
|
-
|
|
99158
|
-
|
|
99159
|
-
|
|
99160
|
-
|
|
99161
|
-
|
|
99162
|
-
|
|
99163
|
-
|
|
99164
|
-
|
|
99165
|
-
if (region) {
|
|
99166
|
-
config2.region = region;
|
|
99167
|
-
}
|
|
99168
|
-
if (baseURL) {
|
|
99169
|
-
config2.baseURL = baseURL;
|
|
99170
|
-
}
|
|
99171
|
-
this.provider = createAmazonBedrock(config2);
|
|
99172
|
-
this.model = modelName || "anthropic.claude-sonnet-4-6";
|
|
99198
|
+
this.provider = createProviderInstance({
|
|
99199
|
+
provider: "bedrock",
|
|
99200
|
+
apiKey,
|
|
99201
|
+
accessKeyId,
|
|
99202
|
+
secretAccessKey,
|
|
99203
|
+
sessionToken,
|
|
99204
|
+
region,
|
|
99205
|
+
baseURL
|
|
99206
|
+
});
|
|
99207
|
+
this.model = modelName || DEFAULT_MODELS.bedrock;
|
|
99173
99208
|
this.apiType = "bedrock";
|
|
99174
99209
|
if (this.debug) {
|
|
99175
99210
|
const authMethod = apiKey ? "API Key" : "AWS Credentials";
|
|
@@ -99752,7 +99787,7 @@ ${this.architectureContext.content}
|
|
|
99752
99787
|
} else {
|
|
99753
99788
|
systemPrompt += predefinedPrompts["code-explorer"] + "\n\n";
|
|
99754
99789
|
}
|
|
99755
|
-
const searchToolDesc1 = this.searchDelegate ? '- search: Ask natural language questions to find code (e.g., "How does authentication work?").
|
|
99790
|
+
const searchToolDesc1 = this.searchDelegate ? '- search: Ask natural language questions to find code locations (e.g., "How does authentication work?"). Returns structured JSON with file locations grouped by relevance. Use extract() on the returned files to read the actual code. 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.";
|
|
99756
99791
|
systemPrompt += `You have access to powerful code search and analysis tools through MCP:
|
|
99757
99792
|
${searchToolDesc1}
|
|
99758
99793
|
- extract: Extract specific code sections with context
|
|
@@ -99762,8 +99797,8 @@ ${searchToolDesc1}
|
|
|
99762
99797
|
systemPrompt += `
|
|
99763
99798
|
- 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.`;
|
|
99764
99799
|
}
|
|
99765
|
-
const searchGuidance1 = this.searchDelegate ? "1. Start with search \u2014 ask a question about what you want to understand. It returns
|
|
99766
|
-
const extractGuidance1 = this.searchDelegate ?
|
|
99800
|
+
const searchGuidance1 = this.searchDelegate ? "1. Start with search \u2014 ask a question about what you want to understand. It returns file locations grouped by relevance (JSON with confidence and groups)." : "1. Start with search to find relevant code patterns. One search per concept is usually enough \u2014 probe handles stemming and case variations.";
|
|
99801
|
+
const extractGuidance1 = this.searchDelegate ? '2. Use extract on the file locations returned by search to read the actual code. Each group has a "reason" explaining why those files matter.' : "2. Use extract to get detailed context when needed";
|
|
99767
99802
|
systemPrompt += `
|
|
99768
99803
|
|
|
99769
99804
|
When exploring code:
|
|
@@ -99807,7 +99842,7 @@ Workspace: ${this.allowedFolders.join(", ")}`;
|
|
|
99807
99842
|
} else {
|
|
99808
99843
|
systemPrompt += predefinedPrompts["code-explorer"] + "\n\n";
|
|
99809
99844
|
}
|
|
99810
|
-
const searchToolDesc2 = this.searchDelegate ? '- search: Ask natural language questions to find code (e.g., "How does authentication work?").
|
|
99845
|
+
const searchToolDesc2 = this.searchDelegate ? '- search: Ask natural language questions to find code locations (e.g., "How does authentication work?"). Returns structured JSON with file locations grouped by relevance. Use extract() on the returned files to read the actual code. 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.";
|
|
99811
99846
|
systemPrompt += `You have access to powerful code search and analysis tools through MCP:
|
|
99812
99847
|
${searchToolDesc2}
|
|
99813
99848
|
- extract: Extract specific code sections with context
|
|
@@ -99817,8 +99852,8 @@ ${searchToolDesc2}
|
|
|
99817
99852
|
systemPrompt += `
|
|
99818
99853
|
- 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.`;
|
|
99819
99854
|
}
|
|
99820
|
-
const searchGuidance2 = this.searchDelegate ? "1. Start with search \u2014 ask a question about what you want to understand. It returns
|
|
99821
|
-
const extractGuidance2 = this.searchDelegate ?
|
|
99855
|
+
const searchGuidance2 = this.searchDelegate ? "1. Start with search \u2014 ask a question about what you want to understand. It returns file locations grouped by relevance (JSON with confidence and groups)." : "1. Start with search to find relevant code patterns. One search per concept is usually enough \u2014 probe handles stemming and case variations.";
|
|
99856
|
+
const extractGuidance2 = this.searchDelegate ? '2. Use extract on the file locations returned by search to read the actual code. Each group has a "reason" explaining why those files matter.' : "2. Use extract to get detailed context when needed";
|
|
99822
99857
|
systemPrompt += `
|
|
99823
99858
|
|
|
99824
99859
|
When exploring code:
|
|
@@ -99878,10 +99913,10 @@ Workspace: ${this.allowedFolders.join(", ")}`;
|
|
|
99878
99913
|
Follow these instructions carefully:
|
|
99879
99914
|
1. Analyze the user's request.
|
|
99880
99915
|
2. Use the available tools step-by-step to fulfill the request.
|
|
99881
|
-
3. You MUST use the search tool before answering ANY code-related question. NEVER answer from memory or general knowledge \u2014 your answers must be grounded in actual code found via search/extract.${this.searchDelegate ? " Ask natural language questions \u2014 the search subagent handles keyword formulation and returns
|
|
99916
|
+
3. You MUST use the search tool before answering ANY code-related question. NEVER answer from memory or general knowledge \u2014 your answers must be grounded in actual code found via search/extract.${this.searchDelegate ? " Ask natural language questions \u2014 the search subagent handles keyword formulation and returns file locations grouped by relevance. Then use extract() on those locations to read the actual code." : " Search handles stemming and case variations automatically \u2014 do NOT try keyword variations manually. Read full files only if really necessary."}
|
|
99882
99917
|
4. Ensure to get really deep and understand the full picture before answering. Follow call chains \u2014 if function A calls B, search for B too. Look for related subsystems (e.g., if asked about rate limiting, also check for quota, throttling, smoothing).
|
|
99883
99918
|
5. Once the task is fully completed, provide your final answer directly as text. Always cite specific files and line numbers as evidence. Do NOT output planning or thinking text \u2014 go straight to the answer.
|
|
99884
|
-
6. ${this.searchDelegate ?
|
|
99919
|
+
6. ${this.searchDelegate ? 'Ask clear, specific questions when searching. Each search should target a distinct concept or question. NEVER re-search the same concept with different phrasing \u2014 if you already searched for "wrapToolWithEmitter", do NOT search again for "definition of wrapToolWithEmitter" or "how wrapToolWithEmitter works". Use extract() on the files already found instead. Limit yourself to one search per distinct concept. When formulating queries, describe WHAT you are looking for, not WHERE \u2014 the search agent will search the full codebase. Do NOT include file names or class names in the query unless that IS the concept (e.g., say "search dedup logic" not "search dedup ProbeAgent").' : "Prefer concise and focused search queries. Use specific keywords and phrases to narrow down results."}
|
|
99885
99920
|
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 ? `
|
|
99886
99921
|
7. When modifying files, choose the appropriate tool:
|
|
99887
99922
|
- Use 'edit' for all code modifications:
|
|
@@ -100608,9 +100643,11 @@ Provide your BEST answer NOW using the information you have already gathered. Do
|
|
|
100608
100643
|
const searchesTried = _toolCallLog.filter((tc) => tc.name === "search").map((tc) => `"${tc.args.query || ""}"${tc.args.exact ? " (exact)" : ""}`).filter((v, i, a) => a.indexOf(v) === i);
|
|
100609
100644
|
const searchSummary = searchesTried.length > 0 ? `
|
|
100610
100645
|
Searches attempted: ${searchesTried.join(", ")}` : "";
|
|
100646
|
+
const isCodeSearcher = this.promptType === "code-searcher";
|
|
100647
|
+
const lastIterMessage = isCodeSearcher ? `\u26A0\uFE0F LAST ITERATION \u2014 you are out of tool calls. Output your JSON response NOW with whatever files you have verified so far. Set confidence to "low" if your search was incomplete. Include the "searches" array listing all search queries you made with their paths and outcomes.${searchSummary}` : `\u26A0\uFE0F LAST ITERATION \u2014 you are out of tool calls. Provide your BEST answer NOW with the information gathered so far. If you could not find what was requested, explain exactly what you searched for and why it did not work, so the caller can try a different approach.${searchSummary}`;
|
|
100611
100648
|
return {
|
|
100612
100649
|
toolChoice: "none",
|
|
100613
|
-
userMessage:
|
|
100650
|
+
userMessage: lastIterMessage
|
|
100614
100651
|
};
|
|
100615
100652
|
}
|
|
100616
100653
|
if (steps.length >= 2) {
|
|
@@ -101145,29 +101182,41 @@ Be thorough \u2014 this is the user's only response. Include all useful informat
|
|
|
101145
101182
|
if (!finalResult || finalResult === DEFAULT_MAX_ITER_MSG) {
|
|
101146
101183
|
try {
|
|
101147
101184
|
const searchQueries = [];
|
|
101185
|
+
const searchDetails = [];
|
|
101148
101186
|
const toolCounts = {};
|
|
101149
101187
|
for (const tc of _toolCallLog) {
|
|
101150
101188
|
toolCounts[tc.name] = (toolCounts[tc.name] || 0) + 1;
|
|
101151
101189
|
if (tc.name === "search") {
|
|
101152
101190
|
const q = tc.args.query || "";
|
|
101191
|
+
const p = tc.args.path || ".";
|
|
101153
101192
|
const exact = tc.args.exact ? " (exact)" : "";
|
|
101154
101193
|
searchQueries.push(`"${q}"${exact}`);
|
|
101194
|
+
searchDetails.push({ query: q, path: p, had_results: false });
|
|
101155
101195
|
}
|
|
101156
101196
|
}
|
|
101157
101197
|
const toolBreakdown = Object.entries(toolCounts).map(([name15, count]) => `${name15}: ${count}x`).join(", ");
|
|
101158
101198
|
const uniqueSearches = [...new Set(searchQueries)];
|
|
101159
|
-
|
|
101199
|
+
if (this.promptType === "code-searcher") {
|
|
101200
|
+
finalResult = JSON.stringify({
|
|
101201
|
+
confidence: "low",
|
|
101202
|
+
reason: "Search incomplete \u2014 iteration limit reached",
|
|
101203
|
+
groups: [],
|
|
101204
|
+
searches: searchDetails
|
|
101205
|
+
});
|
|
101206
|
+
} else {
|
|
101207
|
+
let summary = `I was unable to complete your request after ${currentIteration} tool iterations.
|
|
101160
101208
|
|
|
101161
101209
|
`;
|
|
101162
|
-
|
|
101210
|
+
summary += `Tool calls made: ${toolBreakdown || "none"}
|
|
101163
101211
|
`;
|
|
101164
|
-
|
|
101165
|
-
|
|
101212
|
+
if (uniqueSearches.length > 0) {
|
|
101213
|
+
summary += `Search queries tried: ${uniqueSearches.join(", ")}
|
|
101166
101214
|
`;
|
|
101167
|
-
|
|
101168
|
-
|
|
101215
|
+
}
|
|
101216
|
+
summary += `
|
|
101169
101217
|
The search approach may be fundamentally wrong for this query. Consider: using exact=true for literal string matching, using bash/grep for pattern-based file searches, or trying a completely different strategy instead of repeating similar searches.`;
|
|
101170
|
-
|
|
101218
|
+
finalResult = summary;
|
|
101219
|
+
}
|
|
101171
101220
|
} catch {
|
|
101172
101221
|
finalResult = DEFAULT_MAX_ITER_MSG;
|
|
101173
101222
|
}
|
|
@@ -102008,6 +102057,7 @@ async function delegate({
|
|
|
102008
102057
|
});
|
|
102009
102058
|
let parentAbortHandler;
|
|
102010
102059
|
let parentAbortHardCancelId = null;
|
|
102060
|
+
let raceSettled = false;
|
|
102011
102061
|
const parentAbortPromise = new Promise((_, reject2) => {
|
|
102012
102062
|
if (parentAbortSignal) {
|
|
102013
102063
|
if (parentAbortSignal.aborted) {
|
|
@@ -102016,6 +102066,7 @@ async function delegate({
|
|
|
102016
102066
|
return;
|
|
102017
102067
|
}
|
|
102018
102068
|
parentAbortHandler = () => {
|
|
102069
|
+
if (raceSettled) return;
|
|
102019
102070
|
subagent.triggerGracefulWindDown();
|
|
102020
102071
|
if (debug) {
|
|
102021
102072
|
console.error(`[DELEGATE] Parent abort signal received \u2014 triggered graceful wind-down on subagent ${sessionId}`);
|
|
@@ -102028,6 +102079,7 @@ async function delegate({
|
|
|
102028
102079
|
});
|
|
102029
102080
|
}
|
|
102030
102081
|
parentAbortHardCancelId = setTimeout(() => {
|
|
102082
|
+
if (raceSettled) return;
|
|
102031
102083
|
if (debug) {
|
|
102032
102084
|
console.error(`[DELEGATE] Graceful wind-down deadline expired \u2014 hard cancelling subagent ${sessionId}`);
|
|
102033
102085
|
}
|
|
@@ -102053,6 +102105,7 @@ async function delegate({
|
|
|
102053
102105
|
try {
|
|
102054
102106
|
response = await Promise.race(racers);
|
|
102055
102107
|
} finally {
|
|
102108
|
+
raceSettled = true;
|
|
102056
102109
|
if (parentAbortHandler && parentAbortSignal) {
|
|
102057
102110
|
parentAbortSignal.removeEventListener("abort", parentAbortHandler);
|
|
102058
102111
|
}
|
|
@@ -102093,10 +102146,12 @@ async function delegate({
|
|
|
102093
102146
|
"delegation.success": true
|
|
102094
102147
|
});
|
|
102095
102148
|
if (delegationSpan) {
|
|
102149
|
+
const { truncateForSpan: truncateForSpan2 } = await Promise.resolve().then(() => (init_simpleTelemetry(), simpleTelemetry_exports));
|
|
102096
102150
|
delegationSpan.setAttributes({
|
|
102097
102151
|
"delegation.result.success": true,
|
|
102098
102152
|
"delegation.result.response_length": response.length,
|
|
102099
|
-
"delegation.result.duration_ms": duration3
|
|
102153
|
+
"delegation.result.duration_ms": duration3,
|
|
102154
|
+
"delegation.result": truncateForSpan2(response, 4096)
|
|
102100
102155
|
});
|
|
102101
102156
|
delegationSpan.setStatus({ code: 1 });
|
|
102102
102157
|
delegationSpan.end();
|
|
@@ -102149,9 +102204,13 @@ var init_delegate = __esm({
|
|
|
102149
102204
|
init_ProbeAgent();
|
|
102150
102205
|
DelegationManager = class {
|
|
102151
102206
|
constructor(options = {}) {
|
|
102152
|
-
|
|
102153
|
-
|
|
102154
|
-
|
|
102207
|
+
const parseSafe = (val, fallback) => {
|
|
102208
|
+
const n = parseInt(val, 10);
|
|
102209
|
+
return Number.isNaN(n) ? fallback : n;
|
|
102210
|
+
};
|
|
102211
|
+
this.maxConcurrent = options.maxConcurrent ?? parseSafe(process.env.MAX_CONCURRENT_DELEGATIONS, 3);
|
|
102212
|
+
this.maxPerSession = options.maxPerSession ?? parseSafe(process.env.MAX_DELEGATIONS_PER_SESSION, 10);
|
|
102213
|
+
this.defaultQueueTimeout = options.queueTimeout ?? parseSafe(process.env.DELEGATION_QUEUE_TIMEOUT, 6e4);
|
|
102155
102214
|
this.sessionDelegations = /* @__PURE__ */ new Map();
|
|
102156
102215
|
this.globalActive = 0;
|
|
102157
102216
|
this.waitQueue = [];
|
|
@@ -102962,6 +103021,72 @@ function autoQuoteSearchTerms(query2) {
|
|
|
102962
103021
|
});
|
|
102963
103022
|
return result.join(" ");
|
|
102964
103023
|
}
|
|
103024
|
+
async function checkDelegateDedup(newQuery, previousQueries, model, debug) {
|
|
103025
|
+
if (!model || previousQueries.length === 0) {
|
|
103026
|
+
return { action: "allow", reason: "no previous queries" };
|
|
103027
|
+
}
|
|
103028
|
+
const previousList = previousQueries.map((q, i) => {
|
|
103029
|
+
let line = `${i + 1}. "${q.query}" (path: ${q.path}, found results: ${q.hadResults})`;
|
|
103030
|
+
if (q.reason) line += `
|
|
103031
|
+
Outcome: ${q.reason}`;
|
|
103032
|
+
if (q.groups && q.groups.length > 0) {
|
|
103033
|
+
line += `
|
|
103034
|
+
Found: ${q.groups.map((g) => g.reason).join("; ")}`;
|
|
103035
|
+
}
|
|
103036
|
+
return line;
|
|
103037
|
+
}).join("\n");
|
|
103038
|
+
try {
|
|
103039
|
+
const result = await (0, import_ai5.generateText)({
|
|
103040
|
+
model,
|
|
103041
|
+
maxTokens: 150,
|
|
103042
|
+
temperature: 0,
|
|
103043
|
+
prompt: `You decide if a code search query is redundant given previous queries in the same session.
|
|
103044
|
+
|
|
103045
|
+
PREVIOUS QUERIES:
|
|
103046
|
+
${previousList}
|
|
103047
|
+
|
|
103048
|
+
NEW QUERY: "${newQuery}"
|
|
103049
|
+
|
|
103050
|
+
Respond with exactly one line: ACTION|REASON
|
|
103051
|
+
For rewrites: rewrite|REASON|REWRITTEN_QUERY
|
|
103052
|
+
|
|
103053
|
+
BLOCK when:
|
|
103054
|
+
- Same concept, different phrasing: "find X" / "definition of X" / "where is X" / "X implementation" \u2192 all the same
|
|
103055
|
+
- Synonym or narrower term of a previous query: "dedup" \u2192 "duplicate" \u2192 "unique" \u2192 all the same concept
|
|
103056
|
+
- Single generic word that's just a synonym of a previous failed query
|
|
103057
|
+
- Query is trying to brute-force the same concept with different keywords after previous failures
|
|
103058
|
+
|
|
103059
|
+
REWRITE when:
|
|
103060
|
+
- Previous query was too narrow and failed, new query targets the same goal but could use a FUNDAMENTALLY different search strategy (e.g. searching for a caller instead of the function name, or searching the config/registration site instead of the implementation)
|
|
103061
|
+
- Previous query found WRONG results (e.g. found "FallbackManager" when looking for "dedup logic") \u2014 rewrite to target the actual concept more precisely using implementation-level terms
|
|
103062
|
+
|
|
103063
|
+
ALLOW only when:
|
|
103064
|
+
- The new query targets a COMPLETELY DIFFERENT feature, module, or subsystem \u2014 not just a different word for the same thing
|
|
103065
|
+
|
|
103066
|
+
Only BLOCK when you are CERTAIN the queries target the same concept. When uncertain, ALLOW \u2014 a missed dedup is cheaper than blocking a valid search.
|
|
103067
|
+
|
|
103068
|
+
Examples:
|
|
103069
|
+
- Prev: "wrapToolWithEmitter" \u2192 New: "definition of wrapToolWithEmitter" \u2192 block|Same symbol
|
|
103070
|
+
- Prev: "search dedup" (no results) \u2192 New: "dedup" \u2192 block|Synonym of failed query
|
|
103071
|
+
- Prev: "dedup" (no results) \u2192 New: "duplicate" \u2192 block|Synonym of failed query
|
|
103072
|
+
- Prev: "dedup" (no results) \u2192 New: "unique" \u2192 block|Synonym of failed query
|
|
103073
|
+
- Prev: "auth middleware" \u2192 New: "rate limiting" \u2192 allow|Different subsystem
|
|
103074
|
+
- Prev: "search dedup" (no results) \u2192 New: "previousSearches Map" \u2192 rewrite|Searching for implementation detail instead of concept|previousSearches OR searchKey`
|
|
103075
|
+
});
|
|
103076
|
+
const line = result.text.trim().split("\n")[0];
|
|
103077
|
+
const parts = line.split("|");
|
|
103078
|
+
const action = (parts[0] || "").toLowerCase().trim();
|
|
103079
|
+
if (action === "block") {
|
|
103080
|
+
return { action: "block", reason: parts[1]?.trim() || "duplicate query" };
|
|
103081
|
+
} else if (action === "rewrite" && parts[2]) {
|
|
103082
|
+
return { action: "rewrite", reason: parts[1]?.trim() || "refined query", rewritten: parts[2].trim() };
|
|
103083
|
+
}
|
|
103084
|
+
return { action: "allow", reason: parts[1]?.trim() || "new concept" };
|
|
103085
|
+
} catch (err) {
|
|
103086
|
+
if (debug) console.error("[DEDUP-LLM] Error:", err.message);
|
|
103087
|
+
return { action: "allow", reason: "dedup check failed, allowing" };
|
|
103088
|
+
}
|
|
103089
|
+
}
|
|
102965
103090
|
function normalizeTargets(targets) {
|
|
102966
103091
|
if (!Array.isArray(targets)) return [];
|
|
102967
103092
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -103024,8 +103149,8 @@ function fallbackTargetsFromText(text) {
|
|
|
103024
103149
|
}
|
|
103025
103150
|
return candidates;
|
|
103026
103151
|
}
|
|
103027
|
-
function
|
|
103028
|
-
if (!rawResponse || typeof rawResponse !== "string") return
|
|
103152
|
+
function parseDelegatedResponse(rawResponse) {
|
|
103153
|
+
if (!rawResponse || typeof rawResponse !== "string") return null;
|
|
103029
103154
|
const trimmed = rawResponse.trim();
|
|
103030
103155
|
const tryParse = (text) => {
|
|
103031
103156
|
try {
|
|
@@ -103042,14 +103167,37 @@ function parseDelegatedTargets(rawResponse) {
|
|
|
103042
103167
|
}
|
|
103043
103168
|
}
|
|
103044
103169
|
if (parsed) {
|
|
103045
|
-
if (Array.isArray(parsed)) {
|
|
103046
|
-
return
|
|
103170
|
+
if (Array.isArray(parsed.groups)) {
|
|
103171
|
+
return {
|
|
103172
|
+
confidence: parsed.confidence || "medium",
|
|
103173
|
+
reason: parsed.reason || "",
|
|
103174
|
+
groups: parsed.groups.map((g) => ({
|
|
103175
|
+
reason: g.reason || "",
|
|
103176
|
+
files: normalizeTargets(g.files || [])
|
|
103177
|
+
})).filter((g) => g.files.length > 0),
|
|
103178
|
+
searches: Array.isArray(parsed.searches) ? parsed.searches : []
|
|
103179
|
+
};
|
|
103047
103180
|
}
|
|
103048
103181
|
if (Array.isArray(parsed.targets)) {
|
|
103049
|
-
|
|
103182
|
+
const files2 = normalizeTargets(parsed.targets);
|
|
103183
|
+
if (files2.length > 0) {
|
|
103184
|
+
return { confidence: "medium", reason: "", groups: [{ reason: "Search results", files: files2 }], searches: [] };
|
|
103185
|
+
}
|
|
103186
|
+
return null;
|
|
103050
103187
|
}
|
|
103188
|
+
if (Array.isArray(parsed)) {
|
|
103189
|
+
const files2 = normalizeTargets(parsed);
|
|
103190
|
+
if (files2.length > 0) {
|
|
103191
|
+
return { confidence: "medium", reason: "", groups: [{ reason: "Search results", files: files2 }], searches: [] };
|
|
103192
|
+
}
|
|
103193
|
+
return null;
|
|
103194
|
+
}
|
|
103195
|
+
}
|
|
103196
|
+
const files = normalizeTargets(fallbackTargetsFromText(trimmed));
|
|
103197
|
+
if (files.length > 0) {
|
|
103198
|
+
return { confidence: "low", reason: "", groups: [{ reason: "Search results", files }], searches: [] };
|
|
103051
103199
|
}
|
|
103052
|
-
return
|
|
103200
|
+
return null;
|
|
103053
103201
|
}
|
|
103054
103202
|
function splitTargetSuffix(target) {
|
|
103055
103203
|
const searchStart = target.length > 2 && target[1] === ":" && /[a-zA-Z]/.test(target[0]) ? 2 : 0;
|
|
@@ -103063,129 +103211,78 @@ function splitTargetSuffix(target) {
|
|
|
103063
103211
|
return { filePart: target, suffix: "" };
|
|
103064
103212
|
}
|
|
103065
103213
|
function buildSearchDelegateTask({ searchQuery, searchPath, exact, language, allowTests }) {
|
|
103066
|
-
return
|
|
103067
|
-
|
|
103068
|
-
|
|
103069
|
-
|
|
103070
|
-
|
|
103071
|
-
|
|
103072
|
-
|
|
103073
|
-
|
|
103074
|
-
|
|
103075
|
-
|
|
103076
|
-
|
|
103077
|
-
|
|
103078
|
-
|
|
103079
|
-
|
|
103080
|
-
|
|
103081
|
-
|
|
103082
|
-
|
|
103083
|
-
|
|
103084
|
-
|
|
103085
|
-
|
|
103086
|
-
|
|
103087
|
-
|
|
103088
|
-
|
|
103089
|
-
|
|
103090
|
-
|
|
103091
|
-
|
|
103092
|
-
|
|
103093
|
-
|
|
103094
|
-
|
|
103095
|
-
|
|
103096
|
-
|
|
103097
|
-
|
|
103098
|
-
|
|
103099
|
-
|
|
103100
|
-
|
|
103101
|
-
|
|
103102
|
-
|
|
103103
|
-
|
|
103104
|
-
|
|
103105
|
-
|
|
103106
|
-
|
|
103107
|
-
|
|
103108
|
-
|
|
103109
|
-
|
|
103110
|
-
|
|
103111
|
-
|
|
103112
|
-
|
|
103113
|
-
|
|
103114
|
-
|
|
103115
|
-
|
|
103116
|
-
|
|
103117
|
-
|
|
103118
|
-
|
|
103119
|
-
|
|
103120
|
-
|
|
103121
|
-
|
|
103122
|
-
|
|
103123
|
-
|
|
103124
|
-
|
|
103125
|
-
|
|
103126
|
-
|
|
103127
|
-
|
|
103128
|
-
|
|
103129
|
-
|
|
103130
|
-
|
|
103131
|
-
|
|
103132
|
-
|
|
103133
|
-
|
|
103134
|
-
|
|
103135
|
-
|
|
103136
|
-
|
|
103137
|
-
|
|
103138
|
-
"- Common programming keywords are filtered as stopwords when unquoted: function, class, return, new, struct, impl, var, let, const, etc.",
|
|
103139
|
-
'- Avoid searching for these alone \u2014 combine with a specific term (e.g., "middleware function" is fine, "function" alone is too generic).',
|
|
103140
|
-
'- To bypass stopword filtering: wrap terms in quotes ("return", "struct") or set exact=true. Both disable stemming and splitting too.',
|
|
103141
|
-
'- camelCase terms are split: getUserData becomes "get", "user", "data" \u2014 so one search covers all naming styles.',
|
|
103142
|
-
'- Do NOT search for full function signatures like "func (r *Type) Method(args)". Just search for the method name with exact=true.',
|
|
103143
|
-
'- Do NOT search for file names (e.g., "sliding_log.go"). Use listFiles to discover files by name.',
|
|
103144
|
-
"",
|
|
103145
|
-
"PAGINATION:",
|
|
103146
|
-
"- Search results are paginated (~20k tokens per page).",
|
|
103147
|
-
"- If your search returned relevant files, call the same query with nextPage=true to check for more.",
|
|
103148
|
-
'- Keep paginating while results stay relevant. Stop when results are off-topic or "All results retrieved".',
|
|
103149
|
-
"",
|
|
103150
|
-
"WHEN TO STOP:",
|
|
103151
|
-
"- After you have explored the main concept AND related subsystems.",
|
|
103152
|
-
"- Once you have 5-15 targets covering different aspects of the query.",
|
|
103153
|
-
'- If you get a "DUPLICATE SEARCH BLOCKED" message, do NOT rephrase the same query \u2014 try a FUNDAMENTALLY different approach:',
|
|
103154
|
-
" * Switch between exact=true and exact=false",
|
|
103155
|
-
" * Search for a broader term and filter results manually",
|
|
103156
|
-
" * Use listFiles to browse the directory structure directly",
|
|
103157
|
-
" * Look for related/surrounding patterns instead of the exact string",
|
|
103158
|
-
"- If 2-3 genuinely different search approaches fail, STOP and report what you tried and why it failed.",
|
|
103159
|
-
" Do NOT keep trying variations of the same failing concept.",
|
|
103160
|
-
"",
|
|
103161
|
-
"Strategy:",
|
|
103162
|
-
"1. Analyze the query \u2014 identify key concepts, then brainstorm SYNONYMS and alternative terms for each.",
|
|
103163
|
-
' Code naming often differs from the concept: "authentication" \u2192 verify, credentials, login, auth;',
|
|
103164
|
-
' "rate limiting" \u2192 throttle, quota, limiter, bucket; "error handling" \u2192 catch, recover, panic.',
|
|
103165
|
-
" Think about what a developer would NAME the function/struct/variable, not just the concept.",
|
|
103166
|
-
"2. Run INDEPENDENT searches in PARALLEL \u2014 search for the main concept AND synonyms simultaneously.",
|
|
103167
|
-
" After each search, check if results are relevant. If yes, call nextPage=true for more results.",
|
|
103168
|
-
`3. Combine related symbols into OR searches: '"symbolA" OR "symbolB"' finds files with either.`,
|
|
103169
|
-
"4. For known symbol names use exact=true. For concepts use default (exact=false).",
|
|
103170
|
-
"5. After your first round of searches, READ the extracted code and look for connected code:",
|
|
103171
|
-
" - Function calls to other important functions \u2192 include those targets.",
|
|
103172
|
-
" - Type references and imports \u2192 include type definitions.",
|
|
103173
|
-
" - Registered handlers/middleware \u2192 include all registered items.",
|
|
103174
|
-
"6. If a search returns results, use extract to verify relevance. Run multiple extracts in parallel too.",
|
|
103175
|
-
"7. If a search returns NO results, the term does not exist. Do NOT retry with variations. Move on.",
|
|
103176
|
-
"8. Once you have enough targets (typically 5-15), output your final JSON answer immediately.",
|
|
103177
|
-
"",
|
|
103178
|
-
`Query: ${searchQuery}`,
|
|
103179
|
-
`Search path(s): ${searchPath}`,
|
|
103180
|
-
`Options: exact=${exact ? "true" : "false"}, language=${language || "auto"}, allow_tests=${allowTests ? "true" : "false"}.`,
|
|
103181
|
-
"",
|
|
103182
|
-
'Return ONLY valid JSON: {"targets": ["path/to/file.ext#Symbol", "path/to/file.ext:line", "path/to/file.ext:start-end"]}',
|
|
103183
|
-
'IMPORTANT: Use ABSOLUTE file paths in targets (e.g., "/full/path/to/file.ext#Symbol"). If you only have relative paths, make them relative to the search path above.',
|
|
103184
|
-
"Prefer #Symbol when a function/class name is clear; otherwise use line numbers.",
|
|
103185
|
-
"Deduplicate targets. Do NOT explain or answer - ONLY return the JSON targets.",
|
|
103186
|
-
"",
|
|
103187
|
-
"Remember: if your search returned relevant results, use nextPage=true to check for more before outputting."
|
|
103188
|
-
].join("\n");
|
|
103214
|
+
return `<role>
|
|
103215
|
+
You are a code-location subagent. Your job is to find WHERE relevant code lives for the given question.
|
|
103216
|
+
You are NOT answering the question \u2014 you are finding the code locations that would help answer it.
|
|
103217
|
+
</role>
|
|
103218
|
+
|
|
103219
|
+
<task>
|
|
103220
|
+
<question>${searchQuery}</question>
|
|
103221
|
+
<search-path>${searchPath}</search-path>
|
|
103222
|
+
<options language="${language || "auto"}" allow_tests="${allowTests ? "true" : "false"}" />
|
|
103223
|
+
</task>
|
|
103224
|
+
|
|
103225
|
+
<tools>
|
|
103226
|
+
<tool name="search">
|
|
103227
|
+
Find code matching keywords or patterns. Results are paginated \u2014 use nextPage=true when results are relevant to get more.
|
|
103228
|
+
</tool>
|
|
103229
|
+
<tool name="extract">
|
|
103230
|
+
Read code to verify a file is actually relevant before including it.
|
|
103231
|
+
</tool>
|
|
103232
|
+
<tool name="listFiles">
|
|
103233
|
+
Browse directory structure to discover where code might live.
|
|
103234
|
+
</tool>
|
|
103235
|
+
</tools>
|
|
103236
|
+
|
|
103237
|
+
<search-engine-behavior>
|
|
103238
|
+
- Probe handles stemming, case-insensitive matching, and camelCase/snake_case splitting automatically.
|
|
103239
|
+
- "allowed_ips" ALREADY matches "AllowedIPs", "allowedIps", etc. Do NOT try case/style variations.
|
|
103240
|
+
- NEVER repeat the same search query \u2014 you will get the same results.
|
|
103241
|
+
- If a search returns no results at workspace root, the term does not exist. Move on.
|
|
103242
|
+
- If a search returns no results in a subfolder, try the workspace root or a different directory.
|
|
103243
|
+
- Use exact=true for known symbol names. Use default for conceptual/exploratory queries.
|
|
103244
|
+
- Combine related symbols with OR: "SymbolA" OR "SymbolB" finds files with either.
|
|
103245
|
+
- Run INDEPENDENT searches in PARALLEL \u2014 do not wait between unrelated searches.
|
|
103246
|
+
</search-engine-behavior>
|
|
103247
|
+
|
|
103248
|
+
<strategy>
|
|
103249
|
+
1. Analyze the question \u2014 identify key concepts and brainstorm what a developer would NAME the relevant code.
|
|
103250
|
+
2. Start your first search with the FULL search-path provided above. Do NOT narrow to a subdirectory on first try \u2014 the code may live anywhere in the tree.
|
|
103251
|
+
3. Search for the main concept and synonyms in parallel.
|
|
103252
|
+
4. Use extract to verify relevance \u2014 skim the code to confirm it ACTUALLY relates to the question.
|
|
103253
|
+
5. Follow the trail: if you find a function, look for its callers, type definitions, and registered handlers.
|
|
103254
|
+
6. Group your findings by WHY they are relevant (not by how you found them).
|
|
103255
|
+
</strategy>
|
|
103256
|
+
|
|
103257
|
+
<relevance-filtering priority="critical">
|
|
103258
|
+
- Only include files you have VERIFIED are relevant by reading them with extract.
|
|
103259
|
+
- Do NOT include files just because they matched a keyword \u2014 confirm the match is meaningful.
|
|
103260
|
+
- A file that mentions "session" in a comment is NOT relevant to "How do sessions work?" \u2014 look for the actual implementation.
|
|
103261
|
+
- Fewer verified-relevant files are far more valuable than many unverified keyword matches.
|
|
103262
|
+
- If a file is tangentially related but not core to the question, leave it out.
|
|
103263
|
+
- If NO files are truly relevant, return EMPTY groups with confidence "low". An honest empty result is far better than a wrong result. Never fill groups with loosely related files just to have something.
|
|
103264
|
+
</relevance-filtering>
|
|
103265
|
+
|
|
103266
|
+
<stop-conditions>
|
|
103267
|
+
- Once you have found locations covering the main concept and related subsystems.
|
|
103268
|
+
- If 2-3 different search approaches fail, stop and report what you have.
|
|
103269
|
+
- Do NOT keep trying quote/syntax variations of the same failing keyword.
|
|
103270
|
+
</stop-conditions>
|
|
103271
|
+
|
|
103272
|
+
<on-iteration-limit>
|
|
103273
|
+
If you run out of tool iterations, you MUST still output your JSON response with whatever you found so far.
|
|
103274
|
+
Set confidence to "low" if your search was incomplete.
|
|
103275
|
+
Include ALL files you verified as relevant, even if coverage is partial.
|
|
103276
|
+
The "searches" field helps the caller understand what was attempted.
|
|
103277
|
+
</on-iteration-limit>
|
|
103278
|
+
|
|
103279
|
+
<output-rules>
|
|
103280
|
+
- Return ONLY valid JSON matching the schema. No markdown, no explanation.
|
|
103281
|
+
- ONLY include files you have verified are relevant. No noise.
|
|
103282
|
+
- Group files by RELEVANCE to the question, not by search query.
|
|
103283
|
+
- Use ABSOLUTE file paths. Prefer #Symbol for functions/classes; otherwise use line ranges.
|
|
103284
|
+
- Deduplicate files across groups.
|
|
103285
|
+
</output-rules>`;
|
|
103189
103286
|
}
|
|
103190
103287
|
var import_ai5, import_fs12, CODE_SEARCH_SCHEMA, searchTool, queryTool, extractTool, delegateTool, analyzeAllTool;
|
|
103191
103288
|
var init_vercel = __esm({
|
|
@@ -103201,17 +103298,54 @@ var init_vercel = __esm({
|
|
|
103201
103298
|
import_fs12 = require("fs");
|
|
103202
103299
|
init_error_types();
|
|
103203
103300
|
init_hashline();
|
|
103301
|
+
init_provider();
|
|
103204
103302
|
init_simpleTelemetry();
|
|
103205
103303
|
CODE_SEARCH_SCHEMA = {
|
|
103206
103304
|
type: "object",
|
|
103207
103305
|
properties: {
|
|
103208
|
-
|
|
103306
|
+
confidence: {
|
|
103307
|
+
type: "string",
|
|
103308
|
+
enum: ["high", "medium", "low"],
|
|
103309
|
+
description: "How confident you are that these locations answer the question."
|
|
103310
|
+
},
|
|
103311
|
+
reason: {
|
|
103312
|
+
type: "string",
|
|
103313
|
+
description: "Brief explanation of confidence level \u2014 what was found, partially found, or not found."
|
|
103314
|
+
},
|
|
103315
|
+
groups: {
|
|
103209
103316
|
type: "array",
|
|
103210
|
-
items: {
|
|
103211
|
-
|
|
103317
|
+
items: {
|
|
103318
|
+
type: "object",
|
|
103319
|
+
properties: {
|
|
103320
|
+
reason: {
|
|
103321
|
+
type: "string",
|
|
103322
|
+
description: "Why these files are relevant \u2014 what aspect of the question they address (not how the code works)."
|
|
103323
|
+
},
|
|
103324
|
+
files: {
|
|
103325
|
+
type: "array",
|
|
103326
|
+
items: { type: "string" },
|
|
103327
|
+
description: 'File targets like "path/to/file.ext#Symbol" or "path/to/file.ext:10-20".'
|
|
103328
|
+
}
|
|
103329
|
+
},
|
|
103330
|
+
required: ["reason", "files"]
|
|
103331
|
+
},
|
|
103332
|
+
description: "Groups of related files, each with a reason explaining why they matter."
|
|
103333
|
+
},
|
|
103334
|
+
searches: {
|
|
103335
|
+
type: "array",
|
|
103336
|
+
items: {
|
|
103337
|
+
type: "object",
|
|
103338
|
+
properties: {
|
|
103339
|
+
query: { type: "string", description: "The search query used." },
|
|
103340
|
+
path: { type: "string", description: "The path searched in." },
|
|
103341
|
+
had_results: { type: "boolean", description: "Whether the search returned any results." }
|
|
103342
|
+
},
|
|
103343
|
+
required: ["query", "path", "had_results"]
|
|
103344
|
+
},
|
|
103345
|
+
description: "All search queries executed during this session, with their paths and outcomes."
|
|
103212
103346
|
}
|
|
103213
103347
|
},
|
|
103214
|
-
required: ["
|
|
103348
|
+
required: ["confidence", "reason", "groups", "searches"],
|
|
103215
103349
|
additionalProperties: false
|
|
103216
103350
|
};
|
|
103217
103351
|
searchTool = (options = {}) => {
|
|
@@ -103232,11 +103366,20 @@ var init_vercel = __esm({
|
|
|
103232
103366
|
const previousSearches = /* @__PURE__ */ new Map();
|
|
103233
103367
|
const dupBlockCounts = /* @__PURE__ */ new Map();
|
|
103234
103368
|
const paginationCounts = /* @__PURE__ */ new Map();
|
|
103369
|
+
let consecutiveNoResults = 0;
|
|
103370
|
+
const MAX_CONSECUTIVE_NO_RESULTS = 4;
|
|
103371
|
+
const failedConcepts = /* @__PURE__ */ new Map();
|
|
103235
103372
|
const MAX_PAGES_PER_QUERY = 3;
|
|
103373
|
+
const previousDelegations = [];
|
|
103374
|
+
let cachedDedupModel = void 0;
|
|
103375
|
+
function normalizeQueryConcept(query2) {
|
|
103376
|
+
if (!query2) return "";
|
|
103377
|
+
return query2.replace(/^["']|["']$/g, "").replace(/^(definition\s+of|implementation\s+of|usage\s+of|find|where\s+is|how\s+does|locate|show\s+me|get|look\s+for)\s+/i, "").replace(/^["']|["']$/g, "").replace(/\./g, "").replace(/[_\-\s]+/g, "").toLowerCase().trim();
|
|
103378
|
+
}
|
|
103236
103379
|
return (0, import_ai5.tool)({
|
|
103237
103380
|
name: "search",
|
|
103238
103381
|
description: searchDelegate ? searchDelegateDescription : searchDescription,
|
|
103239
|
-
inputSchema: searchSchema,
|
|
103382
|
+
inputSchema: searchDelegate ? searchDelegateSchema : searchSchema,
|
|
103240
103383
|
execute: async ({ query: searchQuery, path: path9, allow_tests, exact, maxTokens: paramMaxTokens, language, session, nextPage, workingDirectory }) => {
|
|
103241
103384
|
if (!exact && searchQuery) {
|
|
103242
103385
|
const originalQuery = searchQuery;
|
|
@@ -103281,7 +103424,8 @@ var init_vercel = __esm({
|
|
|
103281
103424
|
return await search(searchOptions);
|
|
103282
103425
|
};
|
|
103283
103426
|
if (!searchDelegate) {
|
|
103284
|
-
const searchKey = `${searchPath}::${searchQuery}::${exact || false}`;
|
|
103427
|
+
const searchKey = `${searchPath}::${searchQuery}::${exact || false}::${language || ""}`;
|
|
103428
|
+
let circuitBreakerWarning = "";
|
|
103285
103429
|
if (!nextPage) {
|
|
103286
103430
|
if (previousSearches.has(searchKey)) {
|
|
103287
103431
|
const blockCount = (dupBlockCounts.get(searchKey) || 0) + 1;
|
|
@@ -103301,6 +103445,35 @@ var init_vercel = __esm({
|
|
|
103301
103445
|
}
|
|
103302
103446
|
previousSearches.set(searchKey, { hadResults: false });
|
|
103303
103447
|
paginationCounts.set(searchKey, 0);
|
|
103448
|
+
const normalizedKey = `${searchPath}::${normalizeQueryConcept(searchQuery)}`;
|
|
103449
|
+
if (failedConcepts.has(normalizedKey) && failedConcepts.get(normalizedKey) >= 2) {
|
|
103450
|
+
const conceptCount = failedConcepts.get(normalizedKey) + 1;
|
|
103451
|
+
failedConcepts.set(normalizedKey, conceptCount);
|
|
103452
|
+
if (debug) {
|
|
103453
|
+
console.error(`[CONCEPT-DEDUP] Blocked variation of failed concept (${conceptCount}x): "${searchQuery}" normalized to "${normalizeQueryConcept(searchQuery)}"`);
|
|
103454
|
+
}
|
|
103455
|
+
const isSubfolder = path9 && path9 !== effectiveSearchCwd && path9 !== ".";
|
|
103456
|
+
const scopeHint = isSubfolder ? `
|
|
103457
|
+
- Try searching from the workspace root (omit the path parameter) \u2014 the term may exist in a different directory` : `
|
|
103458
|
+
- The term does not exist in this codebase at any path`;
|
|
103459
|
+
return `CONCEPT ALREADY FAILED (${conceptCount} variations tried). You already searched for "${normalizeQueryConcept(searchQuery)}" with different quoting/syntax in this path and got NO results each time. Changing quotes, adding "func" prefix, or switching to method syntax will NOT change the results.
|
|
103460
|
+
|
|
103461
|
+
Change your strategy:${scopeHint}
|
|
103462
|
+
- Use extract on a file you ALREADY found to read actual code and discover real function/type names
|
|
103463
|
+
- Use listFiles to browse directories and find what functions actually exist
|
|
103464
|
+
- Search for a BROADER concept (e.g., instead of "ctxGetData", try "context" or "middleware data access")
|
|
103465
|
+
- If you have enough information from prior searches, provide your final answer NOW`;
|
|
103466
|
+
}
|
|
103467
|
+
if (consecutiveNoResults >= MAX_CONSECUTIVE_NO_RESULTS) {
|
|
103468
|
+
if (debug) {
|
|
103469
|
+
console.error(`[CIRCUIT-BREAKER] ${consecutiveNoResults} consecutive no-result searches, warning: "${searchQuery}"`);
|
|
103470
|
+
}
|
|
103471
|
+
const isSubfolderCB = path9 && path9 !== effectiveSearchCwd && path9 !== ".";
|
|
103472
|
+
const cbScopeHint = isSubfolderCB ? ` You have been searching in "${path9}" \u2014 consider searching from the workspace root or a different directory.` : "";
|
|
103473
|
+
circuitBreakerWarning = `
|
|
103474
|
+
|
|
103475
|
+
\u26A0\uFE0F CIRCUIT BREAKER: Your last ${consecutiveNoResults} searches ALL returned no results.${cbScopeHint} You MUST change your approach: use extract on files you already found, use listFiles to browse directories, or provide your final answer. Guessing names will not help.`;
|
|
103476
|
+
}
|
|
103304
103477
|
} else {
|
|
103305
103478
|
const pageCount = (paginationCounts.get(searchKey) || 0) + 1;
|
|
103306
103479
|
paginationCounts.set(searchKey, pageCount);
|
|
@@ -103314,10 +103487,24 @@ var init_vercel = __esm({
|
|
|
103314
103487
|
try {
|
|
103315
103488
|
const result = maybeAnnotate(await runRawSearch());
|
|
103316
103489
|
if (typeof result === "string" && result.includes("No results found")) {
|
|
103490
|
+
consecutiveNoResults++;
|
|
103491
|
+
const normalizedKey = `${searchPath}::${normalizeQueryConcept(searchQuery)}`;
|
|
103492
|
+
failedConcepts.set(normalizedKey, (failedConcepts.get(normalizedKey) || 0) + 1);
|
|
103493
|
+
if (debug) {
|
|
103494
|
+
console.error(`[NO-RESULTS] consecutiveNoResults=${consecutiveNoResults}, concept "${normalizeQueryConcept(searchQuery)}" failed ${failedConcepts.get(normalizedKey)}x`);
|
|
103495
|
+
}
|
|
103317
103496
|
if (/^[A-Z]+-\d+$/.test(searchQuery.trim()) || /^[A-Z]+-\d+$/.test(searchQuery.replace(/"/g, "").trim())) {
|
|
103318
|
-
return result + "\n\n\u26A0\uFE0F Your query looks like a ticket/issue ID (e.g., JIRA-1234). Ticket IDs are rarely present in source code. Search for the technical concepts described in the ticket instead (e.g., function names, error messages, variable names).";
|
|
103497
|
+
return result + "\n\n\u26A0\uFE0F Your query looks like a ticket/issue ID (e.g., JIRA-1234). Ticket IDs are rarely present in source code. Search for the technical concepts described in the ticket instead (e.g., function names, error messages, variable names)." + circuitBreakerWarning;
|
|
103498
|
+
}
|
|
103499
|
+
if (consecutiveNoResults >= MAX_CONSECUTIVE_NO_RESULTS - 1 && !circuitBreakerWarning) {
|
|
103500
|
+
const isSubfolderWarn = path9 && path9 !== effectiveSearchCwd && path9 !== ".";
|
|
103501
|
+
const warnScopeHint = isSubfolderWarn ? ` You are searching in "${path9}" \u2014 consider searching from the workspace root or a different directory.` : "";
|
|
103502
|
+
return result + `
|
|
103503
|
+
|
|
103504
|
+
\u26A0\uFE0F WARNING: ${consecutiveNoResults} consecutive searches returned no results.${warnScopeHint} Before your next action: use extract on a file you already found to read actual code, or use listFiles to discover what functions really exist. One more failed search will trigger the circuit breaker.`;
|
|
103319
103505
|
}
|
|
103320
103506
|
} else if (typeof result === "string") {
|
|
103507
|
+
consecutiveNoResults = 0;
|
|
103321
103508
|
const entry = previousSearches.get(searchKey);
|
|
103322
103509
|
if (entry) entry.hadResults = true;
|
|
103323
103510
|
}
|
|
@@ -103325,7 +103512,7 @@ var init_vercel = __esm({
|
|
|
103325
103512
|
options.fileTracker.trackFilesFromOutput(result, effectiveSearchCwd).catch(() => {
|
|
103326
103513
|
});
|
|
103327
103514
|
}
|
|
103328
|
-
return result;
|
|
103515
|
+
return typeof result === "string" ? result + circuitBreakerWarning : result;
|
|
103329
103516
|
} catch (error40) {
|
|
103330
103517
|
console.error("Error executing search command:", error40);
|
|
103331
103518
|
const formatted = formatErrorForAI(error40);
|
|
@@ -103335,12 +103522,58 @@ var init_vercel = __esm({
|
|
|
103335
103522
|
return formatted;
|
|
103336
103523
|
}
|
|
103337
103524
|
}
|
|
103525
|
+
const delegatePath = searchPath || "";
|
|
103526
|
+
let effectiveQuery = searchQuery;
|
|
103527
|
+
if (previousDelegations.length > 0) {
|
|
103528
|
+
if (cachedDedupModel === void 0) {
|
|
103529
|
+
const dedupProvider = options.searchDelegateProvider || process.env.PROBE_SEARCH_DELEGATE_PROVIDER || options.provider || process.env.FORCE_PROVIDER || null;
|
|
103530
|
+
const dedupModelName = options.searchDelegateModel || process.env.PROBE_SEARCH_DELEGATE_MODEL || options.model || process.env.MODEL_NAME || null;
|
|
103531
|
+
if (debug) {
|
|
103532
|
+
console.error(`[DEDUP-LLM] Creating model: provider=${dedupProvider}, model=${dedupModelName}`);
|
|
103533
|
+
}
|
|
103534
|
+
cachedDedupModel = await createLanguageModel(dedupProvider, dedupModelName);
|
|
103535
|
+
if (debug) {
|
|
103536
|
+
console.error(`[DEDUP-LLM] Model created: ${cachedDedupModel ? "success" : "null"}`);
|
|
103537
|
+
}
|
|
103538
|
+
}
|
|
103539
|
+
const dedupSpanAttrs = {
|
|
103540
|
+
"dedup.query": searchQuery,
|
|
103541
|
+
"dedup.previous_count": String(previousDelegations.length),
|
|
103542
|
+
"dedup.previous_queries": previousDelegations.map((d) => d.query).join(" | ")
|
|
103543
|
+
};
|
|
103544
|
+
const dedup = options.tracer?.withSpan ? await options.tracer.withSpan("search.delegate.dedup", async () => {
|
|
103545
|
+
return await checkDelegateDedup(searchQuery, previousDelegations, cachedDedupModel, debug);
|
|
103546
|
+
}, dedupSpanAttrs, (span, result) => {
|
|
103547
|
+
span.setAttributes({
|
|
103548
|
+
"dedup.action": result.action,
|
|
103549
|
+
"dedup.reason": result.reason || "",
|
|
103550
|
+
"dedup.rewritten": result.rewritten || ""
|
|
103551
|
+
});
|
|
103552
|
+
}) : await checkDelegateDedup(searchQuery, previousDelegations, cachedDedupModel, debug);
|
|
103553
|
+
if (debug) {
|
|
103554
|
+
console.error(`[DEDUP-LLM] Query: "${searchQuery}" \u2192 ${dedup.action}: ${dedup.reason}${dedup.rewritten ? ` \u2192 "${dedup.rewritten}"` : ""}`);
|
|
103555
|
+
}
|
|
103556
|
+
if (dedup.action === "block") {
|
|
103557
|
+
const prevQueries = previousDelegations.map((d) => `"${d.query}"`).join(", ");
|
|
103558
|
+
return `DELEGATE BLOCKED: "${searchQuery}" is semantically duplicate of previous delegation(s) [${prevQueries}]. ${dedup.reason}
|
|
103559
|
+
|
|
103560
|
+
Do NOT re-delegate the same concept. Use extract() on files already found, or synthesize your answer from existing results.`;
|
|
103561
|
+
}
|
|
103562
|
+
if (dedup.action === "rewrite" && dedup.rewritten) {
|
|
103563
|
+
effectiveQuery = dedup.rewritten;
|
|
103564
|
+
if (debug) {
|
|
103565
|
+
console.error(`[DEDUP-LLM] Rewritten query: "${searchQuery}" \u2192 "${effectiveQuery}"`);
|
|
103566
|
+
}
|
|
103567
|
+
}
|
|
103568
|
+
}
|
|
103569
|
+
const delegationRecord = { query: effectiveQuery, path: delegatePath, hadResults: false };
|
|
103570
|
+
previousDelegations.push(delegationRecord);
|
|
103338
103571
|
try {
|
|
103339
103572
|
if (debug) {
|
|
103340
|
-
console.error(`Delegating search with query: "${
|
|
103573
|
+
console.error(`Delegating search with query: "${effectiveQuery}", path: "${searchPath}"${effectiveQuery !== searchQuery ? ` (rewritten from: "${searchQuery}")` : ""}`);
|
|
103341
103574
|
}
|
|
103342
103575
|
const delegateTask = buildSearchDelegateTask({
|
|
103343
|
-
searchQuery,
|
|
103576
|
+
searchQuery: effectiveQuery,
|
|
103344
103577
|
searchPath,
|
|
103345
103578
|
exact,
|
|
103346
103579
|
language,
|
|
@@ -103367,18 +103600,33 @@ var init_vercel = __esm({
|
|
|
103367
103600
|
});
|
|
103368
103601
|
const delegateResult = options.tracer?.withSpan ? await options.tracer.withSpan("search.delegate", runDelegation, {
|
|
103369
103602
|
"search.query": searchQuery,
|
|
103370
|
-
"search.path": searchPath
|
|
103603
|
+
"search.path": searchPath,
|
|
103604
|
+
...effectiveQuery !== searchQuery ? { "search.query.rewritten": effectiveQuery } : {}
|
|
103371
103605
|
}, (span, result) => {
|
|
103372
|
-
const text = typeof result === "string" ? result : "";
|
|
103606
|
+
const text = typeof result === "string" ? result : JSON.stringify(result) || "";
|
|
103607
|
+
if (debug) console.error(`[search-delegate] onResult: type=${typeof result}, length=${text.length}`);
|
|
103373
103608
|
span.setAttributes({
|
|
103374
103609
|
"search.delegate.output": truncateForSpan(text),
|
|
103375
|
-
"search.delegate.output_length": text.length
|
|
103610
|
+
"search.delegate.output_length": String(text.length)
|
|
103376
103611
|
});
|
|
103377
103612
|
}) : await runDelegation();
|
|
103378
|
-
const
|
|
103379
|
-
if (
|
|
103613
|
+
const structured = parseDelegatedResponse(delegateResult);
|
|
103614
|
+
if (delegationRecord && structured) {
|
|
103615
|
+
delegationRecord.hadResults = structured.groups.length > 0;
|
|
103616
|
+
delegationRecord.reason = structured.reason || "";
|
|
103617
|
+
delegationRecord.groups = structured.groups.map((g) => ({ reason: g.reason }));
|
|
103618
|
+
}
|
|
103619
|
+
if (!structured || structured.groups.length === 0) {
|
|
103620
|
+
if (structured && structured.confidence === "low" && structured.reason) {
|
|
103621
|
+
if (debug) {
|
|
103622
|
+
console.error(`Delegated search explicitly found nothing: ${structured.reason}`);
|
|
103623
|
+
}
|
|
103624
|
+
return `NOT FOUND: The search delegate thoroughly searched for "${searchQuery}" and concluded: ${structured.reason}
|
|
103625
|
+
|
|
103626
|
+
Do NOT search for analogies or loosely related concepts. If the feature does not exist in the codebase, say so in your final answer.`;
|
|
103627
|
+
}
|
|
103380
103628
|
if (debug) {
|
|
103381
|
-
console.error("Delegated search returned no
|
|
103629
|
+
console.error("Delegated search returned no results; falling back to raw search");
|
|
103382
103630
|
}
|
|
103383
103631
|
const fallbackResult = maybeAnnotate(await runRawSearch());
|
|
103384
103632
|
if (options.fileTracker && typeof fallbackResult === "string") {
|
|
@@ -103389,57 +103637,35 @@ var init_vercel = __esm({
|
|
|
103389
103637
|
}
|
|
103390
103638
|
const delegateBase = options.allowedFolders?.[0] || options.cwd || ".";
|
|
103391
103639
|
const resolutionBase = searchPaths[0] || options.cwd || ".";
|
|
103392
|
-
const
|
|
103393
|
-
const
|
|
103394
|
-
|
|
103395
|
-
|
|
103396
|
-
|
|
103397
|
-
|
|
103398
|
-
|
|
103399
|
-
|
|
103400
|
-
|
|
103401
|
-
|
|
103402
|
-
|
|
103403
|
-
|
|
103404
|
-
|
|
103405
|
-
if ((0, import_fs12.existsSync)(candidate)) {
|
|
103406
|
-
validatedTargets.push(candidate + suffix);
|
|
103407
|
-
if (debug) console.error(`[search-delegate] Fixed doubled path segment: ${filePart} \u2192 ${candidate}`);
|
|
103408
|
-
fixed = true;
|
|
103409
|
-
break;
|
|
103640
|
+
const wsPrefix = resolutionBase.endsWith("/") ? resolutionBase : resolutionBase + "/";
|
|
103641
|
+
for (const group of structured.groups) {
|
|
103642
|
+
group.files = group.files.map((target) => resolveTargetPath(target, delegateBase)).map((target) => {
|
|
103643
|
+
const { filePart, suffix } = splitTargetSuffix(target);
|
|
103644
|
+
if ((0, import_fs12.existsSync)(filePart)) return target;
|
|
103645
|
+
const parts = filePart.split("/").filter(Boolean);
|
|
103646
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
103647
|
+
if (parts[i] === parts[i + 1]) {
|
|
103648
|
+
const candidate = "/" + [...parts.slice(0, i), ...parts.slice(i + 1)].join("/");
|
|
103649
|
+
if ((0, import_fs12.existsSync)(candidate)) {
|
|
103650
|
+
if (debug) console.error(`[search-delegate] Fixed doubled path: ${filePart} \u2192 ${candidate}`);
|
|
103651
|
+
return candidate + suffix;
|
|
103652
|
+
}
|
|
103410
103653
|
}
|
|
103411
103654
|
}
|
|
103412
|
-
|
|
103413
|
-
|
|
103414
|
-
|
|
103415
|
-
|
|
103416
|
-
|
|
103417
|
-
|
|
103418
|
-
|
|
103419
|
-
|
|
103420
|
-
if (debug) console.error(`[search-delegate] Resolved with alt base: ${filePart} \u2192 ${altFile}`);
|
|
103421
|
-
fixed = true;
|
|
103422
|
-
break;
|
|
103655
|
+
for (const altBase of [resolutionBase, options.cwd].filter(Boolean)) {
|
|
103656
|
+
if (altBase === delegateBase) continue;
|
|
103657
|
+
const altResolved = resolveTargetPath(target, altBase);
|
|
103658
|
+
const { filePart: altFile } = splitTargetSuffix(altResolved);
|
|
103659
|
+
if ((0, import_fs12.existsSync)(altFile)) {
|
|
103660
|
+
if (debug) console.error(`[search-delegate] Resolved with alt base: ${filePart} \u2192 ${altFile}`);
|
|
103661
|
+
return altResolved;
|
|
103662
|
+
}
|
|
103423
103663
|
}
|
|
103424
|
-
|
|
103425
|
-
|
|
103426
|
-
|
|
103427
|
-
validatedTargets.push(target);
|
|
103428
|
-
}
|
|
103429
|
-
const extractOptions = {
|
|
103430
|
-
files: validatedTargets,
|
|
103431
|
-
cwd: resolutionBase,
|
|
103432
|
-
allowTests: allow_tests ?? true
|
|
103433
|
-
};
|
|
103434
|
-
if (outline) {
|
|
103435
|
-
extractOptions.format = "xml";
|
|
103436
|
-
}
|
|
103437
|
-
const extractResult = await extract(extractOptions);
|
|
103438
|
-
if (resolutionBase && typeof extractResult === "string") {
|
|
103439
|
-
const wsPrefix = resolutionBase.endsWith("/") ? resolutionBase : resolutionBase + "/";
|
|
103440
|
-
return maybeAnnotate(extractResult.split(wsPrefix).join(""));
|
|
103664
|
+
if (debug) console.error(`[search-delegate] Warning: target may not exist: ${filePart}`);
|
|
103665
|
+
return target;
|
|
103666
|
+
}).map((target) => target.split(wsPrefix).join(""));
|
|
103441
103667
|
}
|
|
103442
|
-
return
|
|
103668
|
+
return JSON.stringify(structured, null, 2);
|
|
103443
103669
|
} catch (error40) {
|
|
103444
103670
|
console.error("Delegated search failed, falling back to raw search:", error40);
|
|
103445
103671
|
try {
|