@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.
@@ -8,10 +8,7 @@
8
8
  * - Custom fallback chains with full configuration
9
9
  */
10
10
 
11
- import { createAnthropic } from '@ai-sdk/anthropic';
12
- import { createOpenAI } from '@ai-sdk/openai';
13
- import { createGoogleGenerativeAI } from '@ai-sdk/google';
14
- import { createAmazonBedrock } from '@ai-sdk/amazon-bedrock';
11
+ import { createProviderInstance, DEFAULT_MODELS as SHARED_DEFAULT_MODELS } from '../utils/provider.js';
15
12
 
16
13
  /**
17
14
  * Fallback strategies
@@ -40,12 +37,7 @@ export const FALLBACK_STRATEGIES = {
40
37
  /**
41
38
  * Default model mappings for each provider
42
39
  */
43
- const DEFAULT_MODELS = {
44
- anthropic: 'claude-sonnet-4-6',
45
- openai: 'gpt-5.2',
46
- google: 'gemini-2.5-flash',
47
- bedrock: 'anthropic.claude-sonnet-4-6'
48
- };
40
+ const DEFAULT_MODELS = SHARED_DEFAULT_MODELS;
49
41
 
50
42
  /**
51
43
  * FallbackManager class for handling provider and model fallback
@@ -138,53 +130,7 @@ export class FallbackManager {
138
130
  */
139
131
  _createProviderInstance(config) {
140
132
  try {
141
- switch (config.provider) {
142
- case 'anthropic':
143
- return createAnthropic({
144
- apiKey: config.apiKey,
145
- ...(config.baseURL && { baseURL: config.baseURL })
146
- });
147
-
148
- case 'openai':
149
- return createOpenAI({
150
- compatibility: 'strict',
151
- apiKey: config.apiKey,
152
- ...(config.baseURL && { baseURL: config.baseURL })
153
- });
154
-
155
- case 'google':
156
- return createGoogleGenerativeAI({
157
- apiKey: config.apiKey,
158
- ...(config.baseURL && { baseURL: config.baseURL })
159
- });
160
-
161
- case 'bedrock': {
162
- const bedrockConfig = {};
163
-
164
- if (config.apiKey) {
165
- bedrockConfig.apiKey = config.apiKey;
166
- } else if (config.accessKeyId && config.secretAccessKey) {
167
- bedrockConfig.accessKeyId = config.accessKeyId;
168
- bedrockConfig.secretAccessKey = config.secretAccessKey;
169
- if (config.sessionToken) {
170
- bedrockConfig.sessionToken = config.sessionToken;
171
- }
172
- }
173
-
174
- if (config.region) {
175
- bedrockConfig.region = config.region;
176
- }
177
-
178
- if (config.baseURL) {
179
- bedrockConfig.baseURL = config.baseURL;
180
- }
181
-
182
- return createAmazonBedrock(bedrockConfig);
183
- }
184
-
185
- default:
186
- throw new Error(`FallbackManager: Unknown provider "${config.provider}"`);
187
- }
133
+ return createProviderInstance(config);
188
134
  } catch (error) {
189
135
  // Re-throw with more context
190
136
  const providerName = this._getProviderDisplayName(config);
@@ -27,10 +27,7 @@ export const ENGINE_ACTIVITY_TIMEOUT_MIN = 5000;
27
27
  */
28
28
  export const ENGINE_ACTIVITY_TIMEOUT_MAX = 600000;
29
29
 
30
- import { createAnthropic } from '@ai-sdk/anthropic';
31
- import { createOpenAI } from '@ai-sdk/openai';
32
- import { createGoogleGenerativeAI } from '@ai-sdk/google';
33
- import { createAmazonBedrock } from '@ai-sdk/amazon-bedrock';
30
+ import { createProviderInstance, DEFAULT_MODELS } from '../utils/provider.js';
34
31
  import { streamText, generateText, tool, stepCountIs, jsonSchema, Output } from 'ai';
35
32
  import { randomUUID } from 'crypto';
36
33
  import { EventEmitter } from 'events';
@@ -1673,13 +1670,10 @@ export class ProbeAgent {
1673
1670
  * Initialize Anthropic model
1674
1671
  */
1675
1672
  initializeAnthropicModel(apiKey, apiUrl, modelName) {
1676
- this.provider = createAnthropic({
1677
- apiKey: apiKey,
1678
- ...(apiUrl && { baseURL: apiUrl }),
1679
- });
1680
- this.model = modelName || 'claude-sonnet-4-6';
1673
+ this.provider = createProviderInstance({ provider: 'anthropic', apiKey, ...(apiUrl && { baseURL: apiUrl }) });
1674
+ this.model = modelName || DEFAULT_MODELS.anthropic;
1681
1675
  this.apiType = 'anthropic';
1682
-
1676
+
1683
1677
  if (this.debug) {
1684
1678
  console.log(`Using Anthropic API with model: ${this.model}${apiUrl ? ` (URL: ${apiUrl})` : ''}`);
1685
1679
  }
@@ -1689,14 +1683,10 @@ export class ProbeAgent {
1689
1683
  * Initialize OpenAI model
1690
1684
  */
1691
1685
  initializeOpenAIModel(apiKey, apiUrl, modelName) {
1692
- this.provider = createOpenAI({
1693
- compatibility: 'strict',
1694
- apiKey: apiKey,
1695
- ...(apiUrl && { baseURL: apiUrl }),
1696
- });
1697
- this.model = modelName || 'gpt-5.2';
1686
+ this.provider = createProviderInstance({ provider: 'openai', apiKey, ...(apiUrl && { baseURL: apiUrl }) });
1687
+ this.model = modelName || DEFAULT_MODELS.openai;
1698
1688
  this.apiType = 'openai';
1699
-
1689
+
1700
1690
  if (this.debug) {
1701
1691
  console.log(`Using OpenAI API with model: ${this.model}${apiUrl ? ` (URL: ${apiUrl})` : ''}`);
1702
1692
  }
@@ -1706,10 +1696,7 @@ export class ProbeAgent {
1706
1696
  * Initialize Google model
1707
1697
  */
1708
1698
  initializeGoogleModel(apiKey, apiUrl, modelName) {
1709
- this.provider = createGoogleGenerativeAI({
1710
- apiKey: apiKey,
1711
- ...(apiUrl && { baseURL: apiUrl }),
1712
- });
1699
+ this.provider = createProviderInstance({ provider: 'google', apiKey, ...(apiUrl && { baseURL: apiUrl }) });
1713
1700
  this.model = modelName || 'gemini-2.5-pro';
1714
1701
  this.apiType = 'google';
1715
1702
 
@@ -2245,32 +2232,10 @@ export class ProbeAgent {
2245
2232
  * Initialize AWS Bedrock model
2246
2233
  */
2247
2234
  initializeBedrockModel(accessKeyId, secretAccessKey, region, sessionToken, apiKey, baseURL, modelName) {
2248
- // Build configuration object, only including defined values
2249
- const config = {};
2250
-
2251
- // Authentication - prefer API key if provided, otherwise use AWS credentials
2252
- if (apiKey) {
2253
- config.apiKey = apiKey;
2254
- } else if (accessKeyId && secretAccessKey) {
2255
- config.accessKeyId = accessKeyId;
2256
- config.secretAccessKey = secretAccessKey;
2257
- if (sessionToken) {
2258
- config.sessionToken = sessionToken;
2259
- }
2260
- }
2261
-
2262
- // Region is required for AWS credentials but optional for API key
2263
- if (region) {
2264
- config.region = region;
2265
- }
2266
-
2267
- // Optional base URL
2268
- if (baseURL) {
2269
- config.baseURL = baseURL;
2270
- }
2271
-
2272
- this.provider = createAmazonBedrock(config);
2273
- this.model = modelName || 'anthropic.claude-sonnet-4-6';
2235
+ this.provider = createProviderInstance({
2236
+ provider: 'bedrock', apiKey, accessKeyId, secretAccessKey, sessionToken, region, baseURL
2237
+ });
2238
+ this.model = modelName || DEFAULT_MODELS.bedrock;
2274
2239
  this.apiType = 'bedrock';
2275
2240
 
2276
2241
  if (this.debug) {
@@ -3012,7 +2977,7 @@ export class ProbeAgent {
3012
2977
 
3013
2978
  // Add high-level instructions about when to use tools
3014
2979
  const searchToolDesc1 = this.searchDelegate
3015
- ? '- 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 — just ask questions.'
2980
+ ? '- 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 — just ask questions.'
3016
2981
  : '- search: Find code patterns using keyword queries with Elasticsearch syntax. Handles stemming and case variations automatically — do NOT try manual keyword variations.';
3017
2982
  systemPrompt += `You have access to powerful code search and analysis tools through MCP:
3018
2983
  ${searchToolDesc1}
@@ -3025,10 +2990,10 @@ ${searchToolDesc1}
3025
2990
  }
3026
2991
 
3027
2992
  const searchGuidance1 = this.searchDelegate
3028
- ? '1. Start with search — ask a question about what you want to understand. It returns extracted code blocks directly.'
2993
+ ? '1. Start with search — ask a question about what you want to understand. It returns file locations grouped by relevance (JSON with confidence and groups).'
3029
2994
  : '1. Start with search to find relevant code patterns. One search per concept is usually enough — probe handles stemming and case variations.';
3030
2995
  const extractGuidance1 = this.searchDelegate
3031
- ? '2. Use extract only if you need more context or a full file'
2996
+ ? '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.'
3032
2997
  : '2. Use extract to get detailed context when needed';
3033
2998
 
3034
2999
  systemPrompt += `\n
@@ -3078,7 +3043,7 @@ ${extractGuidance1}
3078
3043
 
3079
3044
  // Add high-level instructions about when to use tools
3080
3045
  const searchToolDesc2 = this.searchDelegate
3081
- ? '- 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 — just ask questions.'
3046
+ ? '- 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 — just ask questions.'
3082
3047
  : '- search: Find code patterns using keyword queries with Elasticsearch syntax. Handles stemming and case variations automatically — do NOT try manual keyword variations.';
3083
3048
  systemPrompt += `You have access to powerful code search and analysis tools through MCP:
3084
3049
  ${searchToolDesc2}
@@ -3091,10 +3056,10 @@ ${searchToolDesc2}
3091
3056
  }
3092
3057
 
3093
3058
  const searchGuidance2 = this.searchDelegate
3094
- ? '1. Start with search — ask a question about what you want to understand. It returns extracted code blocks directly.'
3059
+ ? '1. Start with search — ask a question about what you want to understand. It returns file locations grouped by relevance (JSON with confidence and groups).'
3095
3060
  : '1. Start with search to find relevant code patterns. One search per concept is usually enough — probe handles stemming and case variations.';
3096
3061
  const extractGuidance2 = this.searchDelegate
3097
- ? '2. Use extract only if you need more context or a full file'
3062
+ ? '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.'
3098
3063
  : '2. Use extract to get detailed context when needed';
3099
3064
 
3100
3065
  systemPrompt += `\n
@@ -3160,10 +3125,10 @@ ${extractGuidance2}
3160
3125
  Follow these instructions carefully:
3161
3126
  1. Analyze the user's request.
3162
3127
  2. Use the available tools step-by-step to fulfill the request.
3163
- 3. You MUST use the search tool before answering ANY code-related question. NEVER answer from memory or general knowledge — your answers must be grounded in actual code found via search/extract.${this.searchDelegate ? ' Ask natural language questions — 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 — do NOT try keyword variations manually. Read full files only if really necessary.'}
3128
+ 3. You MUST use the search tool before answering ANY code-related question. NEVER answer from memory or general knowledge — your answers must be grounded in actual code found via search/extract.${this.searchDelegate ? ' Ask natural language questions — 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 — do NOT try keyword variations manually. Read full files only if really necessary.'}
3164
3129
  4. Ensure to get really deep and understand the full picture before answering. Follow call chains — 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).
3165
3130
  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 — go straight to the answer.
3166
- 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.'}
3131
+ 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 — 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 — 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.'}
3167
3132
  7. NEVER use bash for code exploration (no grep, cat, find, head, tail, awk, sed) — always use search and extract tools instead. Bash is only for system operations like building, running tests, or git commands.${this.allowEdit ? `
3168
3133
  7. When modifying files, choose the appropriate tool:
3169
3134
  - Use 'edit' for all code modifications:
@@ -4088,9 +4053,16 @@ or
4088
4053
  const searchSummary = searchesTried.length > 0
4089
4054
  ? `\nSearches attempted: ${searchesTried.join(', ')}`
4090
4055
  : '';
4056
+
4057
+ // For code-searcher subagents: instruct to output structured JSON even on partial results
4058
+ const isCodeSearcher = this.promptType === 'code-searcher';
4059
+ const lastIterMessage = isCodeSearcher
4060
+ ? `⚠️ LAST ITERATION — 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}`
4061
+ : `⚠️ LAST ITERATION — 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}`;
4062
+
4091
4063
  return {
4092
4064
  toolChoice: 'none',
4093
- userMessage: `⚠️ LAST ITERATION — 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}`
4065
+ userMessage: lastIterMessage
4094
4066
  };
4095
4067
  }
4096
4068
 
@@ -4766,13 +4738,16 @@ Double-check your response based on the criteria above. If everything looks good
4766
4738
  if (!finalResult || finalResult === DEFAULT_MAX_ITER_MSG) {
4767
4739
  try {
4768
4740
  const searchQueries = [];
4741
+ const searchDetails = [];
4769
4742
  const toolCounts = {};
4770
4743
  for (const tc of _toolCallLog) {
4771
4744
  toolCounts[tc.name] = (toolCounts[tc.name] || 0) + 1;
4772
4745
  if (tc.name === 'search') {
4773
4746
  const q = tc.args.query || '';
4747
+ const p = tc.args.path || '.';
4774
4748
  const exact = tc.args.exact ? ' (exact)' : '';
4775
4749
  searchQueries.push(`"${q}"${exact}`);
4750
+ searchDetails.push({ query: q, path: p, had_results: false });
4776
4751
  }
4777
4752
  }
4778
4753
  const toolBreakdown = Object.entries(toolCounts)
@@ -4780,13 +4755,24 @@ Double-check your response based on the criteria above. If everything looks good
4780
4755
  .join(', ');
4781
4756
  const uniqueSearches = [...new Set(searchQueries)];
4782
4757
 
4783
- let summary = `I was unable to complete your request after ${currentIteration} tool iterations.\n\n`;
4784
- summary += `Tool calls made: ${toolBreakdown || 'none'}\n`;
4785
- if (uniqueSearches.length > 0) {
4786
- summary += `Search queries tried: ${uniqueSearches.join(', ')}\n`;
4758
+ // For code-searcher subagents: produce structured JSON so the parent
4759
+ // can still use partial results instead of getting a plain error string.
4760
+ if (this.promptType === 'code-searcher') {
4761
+ finalResult = JSON.stringify({
4762
+ confidence: 'low',
4763
+ reason: 'Search incomplete — iteration limit reached',
4764
+ groups: [],
4765
+ searches: searchDetails
4766
+ });
4767
+ } else {
4768
+ let summary = `I was unable to complete your request after ${currentIteration} tool iterations.\n\n`;
4769
+ summary += `Tool calls made: ${toolBreakdown || 'none'}\n`;
4770
+ if (uniqueSearches.length > 0) {
4771
+ summary += `Search queries tried: ${uniqueSearches.join(', ')}\n`;
4772
+ }
4773
+ summary += `\nThe 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.`;
4774
+ finalResult = summary;
4787
4775
  }
4788
- summary += `\nThe 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.`;
4789
- finalResult = summary;
4790
4776
  } catch {
4791
4777
  finalResult = DEFAULT_MAX_ITER_MSG;
4792
4778
  }
package/build/delegate.js CHANGED
@@ -20,13 +20,14 @@ import { ProbeAgent } from './agent/ProbeAgent.js';
20
20
  */
21
21
  class DelegationManager {
22
22
  constructor(options = {}) {
23
+ const parseSafe = (val, fallback) => { const n = parseInt(val, 10); return Number.isNaN(n) ? fallback : n; };
23
24
  this.maxConcurrent = options.maxConcurrent
24
- ?? parseInt(process.env.MAX_CONCURRENT_DELEGATIONS || '3', 10);
25
+ ?? parseSafe(process.env.MAX_CONCURRENT_DELEGATIONS, 3);
25
26
  this.maxPerSession = options.maxPerSession
26
- ?? parseInt(process.env.MAX_DELEGATIONS_PER_SESSION || '10', 10);
27
+ ?? parseSafe(process.env.MAX_DELEGATIONS_PER_SESSION, 10);
27
28
  // Default queue timeout: 60 seconds. Set DELEGATION_QUEUE_TIMEOUT=0 to disable.
28
29
  this.defaultQueueTimeout = options.queueTimeout
29
- ?? parseInt(process.env.DELEGATION_QUEUE_TIMEOUT || '60000', 10);
30
+ ?? parseSafe(process.env.DELEGATION_QUEUE_TIMEOUT, 60000);
30
31
 
31
32
  // Track delegations per session with timestamp for potential TTL cleanup
32
33
  // Map<string, { count: number, lastUpdated: number }>
@@ -545,6 +546,9 @@ export async function delegate({
545
546
  // Phase 2: Hard cancel after deadline if subagent hasn't finished
546
547
  let parentAbortHandler;
547
548
  let parentAbortHardCancelId = null;
549
+ // Track whether the race has settled so late abort signals don't create
550
+ // unhandled promise rejections.
551
+ let raceSettled = false;
548
552
  const parentAbortPromise = new Promise((_, reject) => {
549
553
  if (parentAbortSignal) {
550
554
  if (parentAbortSignal.aborted) {
@@ -553,6 +557,8 @@ export async function delegate({
553
557
  return;
554
558
  }
555
559
  parentAbortHandler = () => {
560
+ // If the race already settled, the answer won — don't reject.
561
+ if (raceSettled) return;
556
562
  // Phase 1: graceful wind-down — let subagent finish its current step
557
563
  subagent.triggerGracefulWindDown();
558
564
  if (debug) {
@@ -567,6 +573,7 @@ export async function delegate({
567
573
  }
568
574
  // Phase 2: hard cancel after 30s if subagent hasn't finished
569
575
  parentAbortHardCancelId = setTimeout(() => {
576
+ if (raceSettled) return;
570
577
  if (debug) {
571
578
  console.error(`[DELEGATE] Graceful wind-down deadline expired — hard cancelling subagent ${sessionId}`);
572
579
  }
@@ -594,6 +601,8 @@ export async function delegate({
594
601
  try {
595
602
  response = await Promise.race(racers);
596
603
  } finally {
604
+ // Mark race as settled so late abort signals are ignored
605
+ raceSettled = true;
597
606
  // Clean up parent abort listener and hard cancel timer to prevent memory leaks
598
607
  if (parentAbortHandler && parentAbortSignal) {
599
608
  parentAbortSignal.removeEventListener('abort', parentAbortHandler);
@@ -650,10 +659,12 @@ export async function delegate({
650
659
  });
651
660
 
652
661
  if (delegationSpan) {
662
+ const { truncateForSpan } = await import('./agent/simpleTelemetry.js');
653
663
  delegationSpan.setAttributes({
654
664
  'delegation.result.success': true,
655
665
  'delegation.result.response_length': response.length,
656
- 'delegation.result.duration_ms': duration
666
+ 'delegation.result.duration_ms': duration,
667
+ 'delegation.result': truncateForSpan(response, 4096)
657
668
  });
658
669
  delegationSpan.setStatus({ code: 1 }); // OK
659
670
  delegationSpan.end();
@@ -7,6 +7,11 @@ import { z } from 'zod';
7
7
  import { resolve, isAbsolute } from 'path';
8
8
 
9
9
  // Common schemas for tool parameters (used for internal execution after XML parsing)
10
+ export const searchDelegateSchema = z.object({
11
+ query: z.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 — just describe what you are looking for in plain English. A subagent will handle keyword searches for you.'),
12
+ path: z.string().optional().default('.').describe('Path to search in.'),
13
+ });
14
+
10
15
  export const searchSchema = z.object({
11
16
  query: z.string().describe('Search query — 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.'),
12
17
  path: z.string().optional().default('.').describe('Path to search in. For dependencies use "go:github.com/owner/repo", "js:package_name", or "rust:cargo_name" etc.'),
@@ -100,7 +105,17 @@ export const attemptCompletionSchema = {
100
105
  // Tool descriptions (used by Vercel tool() definitions)
101
106
 
102
107
  export const searchDescription = 'Search code in the repository. Free-form questions are accepted, but Elasticsearch-style keyword queries work best. Use this tool first for any code-related questions. NOTE: By default, search handles stemming, case-insensitive matching, and camelCase/snake_case splitting automatically — do NOT manually try keyword variations like "getAllUsers" then "get_all_users" then "GetAllUsers". One search covers all variations.';
103
- export const 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 just ask the question naturally.';
108
+ export const 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.
109
+
110
+ Returns JSON: { "confidence": "high|medium|low", "groups": [{ "reason": "why these files matter", "files": ["path#Symbol", ...] }] }
111
+
112
+ IMPORTANT — each call spawns a subagent (expensive, takes minutes). Be deliberate:
113
+ - Ask plain English questions about WHERE code is, NOT keyword queries. Good: "How are user sessions extracted from cookies?" Bad: "ctxGetSession OR GetSession"
114
+ - Each call should explore a DIFFERENT ANGLE of the problem. Don't rephrase — reframe:
115
+ 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?"
116
+ Bad: 1) "How does session extraction work?" 2) "Where is the session extracted?" 3) "Find session extraction code" ← same question reworded
117
+ - If a search returned no useful results, ask about a DIFFERENT part of the system. Think: what upstream/downstream component touches this?
118
+ - After getting results, use extract() to read the files you need — search only locates, extract reads.`;
104
119
  export const queryDescription = 'Search code using ast-grep structural pattern matching. Use this tool to find specific code structures like functions, classes, or methods.';
105
120
  export const extractDescription = 'Extract code blocks from files based on file paths and optional line numbers. Use this tool to see complete context after finding relevant files. Line numbers from output can be used with edit start_line/end_line for precise editing.';
106
121
  export const delegateDescription = 'Automatically delegate big distinct tasks to specialized probe subagents within the agentic loop. Used by AI agents to break down complex requests into focused, parallel tasks.';