@probelabs/probe 0.6.0-rc281 → 0.6.0-rc282

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.
@@ -9185,13 +9185,19 @@ function buildSearchDelegateTask({ searchQuery, searchPath, exact, language, all
9185
9185
  "- listFiles: Understand directory structure to find where relevant code might live.",
9186
9186
  "",
9187
9187
  "CRITICAL - How probe search works (do NOT ignore):",
9188
- "- By default (exact=false), probe ALREADY handles stemming, case-insensitive matching, and camelCase/snake_case splitting.",
9188
+ "- By default (exact=false), probe ALREADY handles stemming, case-insensitive matching, and camelCase/snake_case splitting automatically.",
9189
9189
  '- Searching "allowed_ips" ALREADY matches "AllowedIPs", "allowedIps", "allowed_ips", etc. Do NOT manually try case/style variations.',
9190
9190
  '- Searching "getUserData" ALREADY matches "get", "user", "data" and their variations.',
9191
- "- NEVER repeat the same search query \u2014 you will get the same results.",
9191
+ "- NEVER repeat the same search query \u2014 you will get the same results. Changing the path does NOT change this.",
9192
9192
  "- NEVER search trivial variations of the same keyword (e.g., AllowedIPs then allowedIps then allowed_ips). This is wasteful \u2014 probe handles it.",
9193
- "- If a search returns no results, the term likely does not exist in that path. Try a genuinely DIFFERENT keyword or concept, not a variation.",
9194
- "- If 2-3 consecutive searches return no results for a concept, STOP searching for it and move on.",
9193
+ "- If a search returns no results, the term likely does not exist. Try a genuinely DIFFERENT keyword or concept, not a variation.",
9194
+ "- If 2-3 searches return no results for a concept, STOP searching for it and move on. Do NOT keep retrying.",
9195
+ "",
9196
+ "When to use exact=true:",
9197
+ "- Use exact=true when searching for a KNOWN symbol name (function, type, variable, struct).",
9198
+ "- exact=true matches the literal string only \u2014 no stemming, no splitting.",
9199
+ '- This is ideal for precise lookups: exact=true "ForwardMessage", exact=true "SessionLimiter", exact=true "ThrottleRetryLimit".',
9200
+ "- Do NOT use exact=true for exploratory/conceptual queries \u2014 use the default for those.",
9195
9201
  "",
9196
9202
  "GOOD search strategy (do this):",
9197
9203
  ' Query: "How does authentication work and how are sessions managed?"',
@@ -9200,10 +9206,18 @@ function buildSearchDelegateTask({ searchQuery, searchPath, exact, language, all
9200
9206
  ' \u2192 search "allowlist middleware" (one search, probe handles IP/ip/Ip variations)',
9201
9207
  ' Query: "How does BM25 scoring work with SIMD optimization?"',
9202
9208
  ' \u2192 search "BM25 scoring" \u2192 search "SIMD optimization" (two different concepts)',
9209
+ ' Query: "Find ForwardMessage and SessionLimiter functions"',
9210
+ ' \u2192 search exact=true "ForwardMessage" \u2192 search exact=true "SessionLimiter" (known symbols, use exact)',
9211
+ ' Query: "Find ThrottleRetryLimit usage"',
9212
+ ' \u2192 search exact=true "ThrottleRetryLimit" (one search, if no results the symbol does not exist \u2014 stop)',
9203
9213
  "",
9204
9214
  "BAD search strategy (never do this):",
9205
- ' \u2192 search "AllowedIPs" \u2192 search "allowedIps" \u2192 search "allowed_ips" (WRONG: these are trivial case variations, probe handles them)',
9206
- ' \u2192 search "CIDR" \u2192 search "cidr" \u2192 search "Cidr" \u2192 search "*cidr*" (WRONG: same keyword repeated with variations)',
9215
+ ' \u2192 search "AllowedIPs" \u2192 search "allowedIps" \u2192 search "allowed_ips" (WRONG: case/style variations, probe handles them)',
9216
+ ' \u2192 search "limitDRL" \u2192 search "LimitDRL" (WRONG: case variation of same term)',
9217
+ ' \u2192 search "throttle_retry_limit" after searching "ThrottleRetryLimit" (WRONG: snake_case variation, probe handles it)',
9218
+ ' \u2192 search "ThrottleRetryLimit" path=tyk \u2192 search "ThrottleRetryLimit" path=gateway \u2192 search "ThrottleRetryLimit" path=apidef (WRONG: same query on different paths hoping for different results)',
9219
+ ' \u2192 search "func (k *RateLimitAndQuotaCheck) handleRateLimitFailure" (WRONG: do not search full function signatures, just use exact=true "handleRateLimitFailure")',
9220
+ ' \u2192 search "ForwardMessage" \u2192 search "ForwardMessage" \u2192 search "ForwardMessage" (WRONG: repeating the exact same query)',
9207
9221
  ' \u2192 search "error handling" \u2192 search "error handling" \u2192 search "error handling" (WRONG: repeating exact same query)',
9208
9222
  "",
9209
9223
  "Keyword tips:",
@@ -9212,12 +9226,13 @@ function buildSearchDelegateTask({ searchQuery, searchPath, exact, language, all
9212
9226
  '- To bypass stopword filtering: wrap terms in quotes ("return", "struct") or set exact=true. Both disable stemming and splitting too.',
9213
9227
  "- Multiple words without operators use OR logic: foo bar = foo OR bar. Use AND explicitly if you need both: foo AND bar.",
9214
9228
  '- camelCase terms are split: getUserData becomes "get", "user", "data" \u2014 so one search covers all naming styles.',
9229
+ '- Do NOT search for full function signatures like "func (r *Type) Method(args)". Just search for the method name with exact=true.',
9215
9230
  "",
9216
9231
  "Strategy:",
9217
9232
  "1. Analyze the query - identify key concepts, entities, and relationships",
9218
- "2. Run ONE focused search per concept with the most natural keyword. Trust probe to handle variations.",
9233
+ "2. Run ONE focused search per concept. For known symbol names use exact=true. For concepts use default (exact=false).",
9219
9234
  "3. If a search returns results, use extract to verify relevance",
9220
- "4. Only try a different keyword if the first one returned irrelevant results (not if it returned no results \u2014 that means the concept is absent)",
9235
+ "4. If a search returns NO results, the term does not exist in the codebase. Do NOT retry with variations, different paths, or longer strings. Move on.",
9221
9236
  "5. Combine all relevant targets in your final response",
9222
9237
  "",
9223
9238
  `Query: ${searchQuery}`,
@@ -9538,6 +9553,36 @@ var init_vercel = __esm({
9538
9553
  } else if (targets) {
9539
9554
  const parsedTargets = parseTargets(targets);
9540
9555
  extractFiles = parsedTargets.map((target) => resolveTargetPath(target, effectiveCwd));
9556
+ if (options.allowedFolders && options.allowedFolders.length > 0) {
9557
+ extractFiles = extractFiles.map((target) => {
9558
+ const { filePart, suffix } = splitTargetSuffix(target);
9559
+ if (existsSync(filePart)) return target;
9560
+ const cwdPrefix = effectiveCwd.endsWith("/") ? effectiveCwd : effectiveCwd + "/";
9561
+ const relativePart = filePart.startsWith(cwdPrefix) ? filePart.slice(cwdPrefix.length) : null;
9562
+ if (relativePart) {
9563
+ for (const folder of options.allowedFolders) {
9564
+ const candidate = folder + "/" + relativePart;
9565
+ if (existsSync(candidate)) {
9566
+ if (debug) console.error(`[extract] Auto-fixed path: ${filePart} \u2192 ${candidate}`);
9567
+ return candidate + suffix;
9568
+ }
9569
+ }
9570
+ }
9571
+ for (const folder of options.allowedFolders) {
9572
+ const folderPrefix = folder.endsWith("/") ? folder : folder + "/";
9573
+ const wsParent = folderPrefix.replace(/[^/]+\/$/, "");
9574
+ if (filePart.startsWith(wsParent)) {
9575
+ const tail = filePart.slice(wsParent.length);
9576
+ const candidate = folderPrefix + tail;
9577
+ if (candidate !== filePart && existsSync(candidate)) {
9578
+ if (debug) console.error(`[extract] Auto-fixed path via workspace: ${filePart} \u2192 ${candidate}`);
9579
+ return candidate + suffix;
9580
+ }
9581
+ }
9582
+ }
9583
+ return target;
9584
+ });
9585
+ }
9541
9586
  let effectiveFormat = format;
9542
9587
  if (outline && format === "outline-xml") {
9543
9588
  effectiveFormat = "xml";
@@ -144,13 +144,19 @@ function buildSearchDelegateTask({ searchQuery, searchPath, exact, language, all
144
144
  '- listFiles: Understand directory structure to find where relevant code might live.',
145
145
  '',
146
146
  'CRITICAL - How probe search works (do NOT ignore):',
147
- '- By default (exact=false), probe ALREADY handles stemming, case-insensitive matching, and camelCase/snake_case splitting.',
147
+ '- By default (exact=false), probe ALREADY handles stemming, case-insensitive matching, and camelCase/snake_case splitting automatically.',
148
148
  '- Searching "allowed_ips" ALREADY matches "AllowedIPs", "allowedIps", "allowed_ips", etc. Do NOT manually try case/style variations.',
149
149
  '- Searching "getUserData" ALREADY matches "get", "user", "data" and their variations.',
150
- '- NEVER repeat the same search query — you will get the same results.',
150
+ '- NEVER repeat the same search query — you will get the same results. Changing the path does NOT change this.',
151
151
  '- NEVER search trivial variations of the same keyword (e.g., AllowedIPs then allowedIps then allowed_ips). This is wasteful — probe handles it.',
152
- '- If a search returns no results, the term likely does not exist in that path. Try a genuinely DIFFERENT keyword or concept, not a variation.',
153
- '- If 2-3 consecutive searches return no results for a concept, STOP searching for it and move on.',
152
+ '- If a search returns no results, the term likely does not exist. Try a genuinely DIFFERENT keyword or concept, not a variation.',
153
+ '- If 2-3 searches return no results for a concept, STOP searching for it and move on. Do NOT keep retrying.',
154
+ '',
155
+ 'When to use exact=true:',
156
+ '- Use exact=true when searching for a KNOWN symbol name (function, type, variable, struct).',
157
+ '- exact=true matches the literal string only — no stemming, no splitting.',
158
+ '- This is ideal for precise lookups: exact=true "ForwardMessage", exact=true "SessionLimiter", exact=true "ThrottleRetryLimit".',
159
+ '- Do NOT use exact=true for exploratory/conceptual queries — use the default for those.',
154
160
  '',
155
161
  'GOOD search strategy (do this):',
156
162
  ' Query: "How does authentication work and how are sessions managed?"',
@@ -159,10 +165,18 @@ function buildSearchDelegateTask({ searchQuery, searchPath, exact, language, all
159
165
  ' → search "allowlist middleware" (one search, probe handles IP/ip/Ip variations)',
160
166
  ' Query: "How does BM25 scoring work with SIMD optimization?"',
161
167
  ' → search "BM25 scoring" → search "SIMD optimization" (two different concepts)',
168
+ ' Query: "Find ForwardMessage and SessionLimiter functions"',
169
+ ' → search exact=true "ForwardMessage" → search exact=true "SessionLimiter" (known symbols, use exact)',
170
+ ' Query: "Find ThrottleRetryLimit usage"',
171
+ ' → search exact=true "ThrottleRetryLimit" (one search, if no results the symbol does not exist — stop)',
162
172
  '',
163
173
  'BAD search strategy (never do this):',
164
- ' → search "AllowedIPs" → search "allowedIps" → search "allowed_ips" (WRONG: these are trivial case variations, probe handles them)',
165
- ' → search "CIDR" → search "cidr" → search "Cidr" → search "*cidr*" (WRONG: same keyword repeated with variations)',
174
+ ' → search "AllowedIPs" → search "allowedIps" → search "allowed_ips" (WRONG: case/style variations, probe handles them)',
175
+ ' → search "limitDRL" → search "LimitDRL" (WRONG: case variation of same term)',
176
+ ' → search "throttle_retry_limit" after searching "ThrottleRetryLimit" (WRONG: snake_case variation, probe handles it)',
177
+ ' → search "ThrottleRetryLimit" path=tyk → search "ThrottleRetryLimit" path=gateway → search "ThrottleRetryLimit" path=apidef (WRONG: same query on different paths hoping for different results)',
178
+ ' → search "func (k *RateLimitAndQuotaCheck) handleRateLimitFailure" (WRONG: do not search full function signatures, just use exact=true "handleRateLimitFailure")',
179
+ ' → search "ForwardMessage" → search "ForwardMessage" → search "ForwardMessage" (WRONG: repeating the exact same query)',
166
180
  ' → search "error handling" → search "error handling" → search "error handling" (WRONG: repeating exact same query)',
167
181
  '',
168
182
  'Keyword tips:',
@@ -171,12 +185,13 @@ function buildSearchDelegateTask({ searchQuery, searchPath, exact, language, all
171
185
  '- To bypass stopword filtering: wrap terms in quotes ("return", "struct") or set exact=true. Both disable stemming and splitting too.',
172
186
  '- Multiple words without operators use OR logic: foo bar = foo OR bar. Use AND explicitly if you need both: foo AND bar.',
173
187
  '- camelCase terms are split: getUserData becomes "get", "user", "data" — so one search covers all naming styles.',
188
+ '- Do NOT search for full function signatures like "func (r *Type) Method(args)". Just search for the method name with exact=true.',
174
189
  '',
175
190
  'Strategy:',
176
191
  '1. Analyze the query - identify key concepts, entities, and relationships',
177
- '2. Run ONE focused search per concept with the most natural keyword. Trust probe to handle variations.',
192
+ '2. Run ONE focused search per concept. For known symbol names use exact=true. For concepts use default (exact=false).',
178
193
  '3. If a search returns results, use extract to verify relevance',
179
- '4. Only try a different keyword if the first one returned irrelevant results (not if it returned no results that means the concept is absent)',
194
+ '4. If a search returns NO results, the term does not exist in the codebase. Do NOT retry with variations, different paths, or longer strings. Move on.',
180
195
  '5. Combine all relevant targets in your final response',
181
196
  '',
182
197
  `Query: ${searchQuery}`,
@@ -570,6 +585,50 @@ export const extractTool = (options = {}) => {
570
585
  // Resolve relative paths in targets against cwd
571
586
  extractFiles = parsedTargets.map(target => resolveTargetPath(target, effectiveCwd));
572
587
 
588
+ // Auto-fix: if resolved paths don't exist, try allowedFolders subdirs
589
+ // Handles when search returns relative paths (e.g., "gateway/file.go") and
590
+ // model constructs wrong absolute paths (e.g., /workspace/gateway/file.go
591
+ // instead of /workspace/tyk/gateway/file.go)
592
+ if (options.allowedFolders && options.allowedFolders.length > 0) {
593
+ extractFiles = extractFiles.map(target => {
594
+ const { filePart, suffix } = splitTargetSuffix(target);
595
+ if (existsSync(filePart)) return target;
596
+
597
+ // Try resolving the relative tail against each allowedFolder
598
+ const cwdPrefix = (effectiveCwd.endsWith('/') ? effectiveCwd : effectiveCwd + '/');
599
+ const relativePart = filePart.startsWith(cwdPrefix)
600
+ ? filePart.slice(cwdPrefix.length)
601
+ : null;
602
+
603
+ if (relativePart) {
604
+ for (const folder of options.allowedFolders) {
605
+ const candidate = folder + '/' + relativePart;
606
+ if (existsSync(candidate)) {
607
+ if (debug) console.error(`[extract] Auto-fixed path: ${filePart} → ${candidate}`);
608
+ return candidate + suffix;
609
+ }
610
+ }
611
+ }
612
+
613
+ // Try stripping workspace prefix and resolving against allowedFolders
614
+ // e.g., /tmp/visor-workspaces/abc/gateway/file.go → try each folder + gateway/file.go
615
+ for (const folder of options.allowedFolders) {
616
+ const folderPrefix = folder.endsWith('/') ? folder : folder + '/';
617
+ const wsParent = folderPrefix.replace(/[^/]+\/$/, '');
618
+ if (filePart.startsWith(wsParent)) {
619
+ const tail = filePart.slice(wsParent.length);
620
+ const candidate = folderPrefix + tail;
621
+ if (candidate !== filePart && existsSync(candidate)) {
622
+ if (debug) console.error(`[extract] Auto-fixed path via workspace: ${filePart} → ${candidate}`);
623
+ return candidate + suffix;
624
+ }
625
+ }
626
+ }
627
+
628
+ return target;
629
+ });
630
+ }
631
+
573
632
  // Apply format mapping for outline-xml to xml
574
633
  let effectiveFormat = format;
575
634
  if (outline && format === 'outline-xml') {
@@ -4205,7 +4205,7 @@ var init_NormalizedSchema = __esm({
4205
4205
  if (this.isDocumentSchema()) {
4206
4206
  return member([15, 0], memberName);
4207
4207
  }
4208
- throw new Error(`@smithy/core/schema - ${this.getName(true)} has no no member=${memberName}.`);
4208
+ throw new Error(`@smithy/core/schema - ${this.getName(true)} has no member=${memberName}.`);
4209
4209
  }
4210
4210
  getMemberSchemas() {
4211
4211
  const buffer = {};
@@ -5996,7 +5996,7 @@ var init_EventStreamSerde = __esm({
5996
5996
  throw new Error("@smithy/core/event-streams - non-struct member not supported in event stream union.");
5997
5997
  }
5998
5998
  }
5999
- const messageSerialization = serializer.flush();
5999
+ const messageSerialization = serializer.flush() ?? new Uint8Array();
6000
6000
  const body = typeof messageSerialization === "string" ? (this.serdeContext?.utf8Decoder ?? import_util_utf8.fromUtf8)(messageSerialization) : messageSerialization;
6001
6001
  return {
6002
6002
  body,
@@ -36563,13 +36563,19 @@ function buildSearchDelegateTask({ searchQuery, searchPath, exact, language, all
36563
36563
  "- listFiles: Understand directory structure to find where relevant code might live.",
36564
36564
  "",
36565
36565
  "CRITICAL - How probe search works (do NOT ignore):",
36566
- "- By default (exact=false), probe ALREADY handles stemming, case-insensitive matching, and camelCase/snake_case splitting.",
36566
+ "- By default (exact=false), probe ALREADY handles stemming, case-insensitive matching, and camelCase/snake_case splitting automatically.",
36567
36567
  '- Searching "allowed_ips" ALREADY matches "AllowedIPs", "allowedIps", "allowed_ips", etc. Do NOT manually try case/style variations.',
36568
36568
  '- Searching "getUserData" ALREADY matches "get", "user", "data" and their variations.',
36569
- "- NEVER repeat the same search query \u2014 you will get the same results.",
36569
+ "- NEVER repeat the same search query \u2014 you will get the same results. Changing the path does NOT change this.",
36570
36570
  "- NEVER search trivial variations of the same keyword (e.g., AllowedIPs then allowedIps then allowed_ips). This is wasteful \u2014 probe handles it.",
36571
- "- If a search returns no results, the term likely does not exist in that path. Try a genuinely DIFFERENT keyword or concept, not a variation.",
36572
- "- If 2-3 consecutive searches return no results for a concept, STOP searching for it and move on.",
36571
+ "- If a search returns no results, the term likely does not exist. Try a genuinely DIFFERENT keyword or concept, not a variation.",
36572
+ "- If 2-3 searches return no results for a concept, STOP searching for it and move on. Do NOT keep retrying.",
36573
+ "",
36574
+ "When to use exact=true:",
36575
+ "- Use exact=true when searching for a KNOWN symbol name (function, type, variable, struct).",
36576
+ "- exact=true matches the literal string only \u2014 no stemming, no splitting.",
36577
+ '- This is ideal for precise lookups: exact=true "ForwardMessage", exact=true "SessionLimiter", exact=true "ThrottleRetryLimit".',
36578
+ "- Do NOT use exact=true for exploratory/conceptual queries \u2014 use the default for those.",
36573
36579
  "",
36574
36580
  "GOOD search strategy (do this):",
36575
36581
  ' Query: "How does authentication work and how are sessions managed?"',
@@ -36578,10 +36584,18 @@ function buildSearchDelegateTask({ searchQuery, searchPath, exact, language, all
36578
36584
  ' \u2192 search "allowlist middleware" (one search, probe handles IP/ip/Ip variations)',
36579
36585
  ' Query: "How does BM25 scoring work with SIMD optimization?"',
36580
36586
  ' \u2192 search "BM25 scoring" \u2192 search "SIMD optimization" (two different concepts)',
36587
+ ' Query: "Find ForwardMessage and SessionLimiter functions"',
36588
+ ' \u2192 search exact=true "ForwardMessage" \u2192 search exact=true "SessionLimiter" (known symbols, use exact)',
36589
+ ' Query: "Find ThrottleRetryLimit usage"',
36590
+ ' \u2192 search exact=true "ThrottleRetryLimit" (one search, if no results the symbol does not exist \u2014 stop)',
36581
36591
  "",
36582
36592
  "BAD search strategy (never do this):",
36583
- ' \u2192 search "AllowedIPs" \u2192 search "allowedIps" \u2192 search "allowed_ips" (WRONG: these are trivial case variations, probe handles them)',
36584
- ' \u2192 search "CIDR" \u2192 search "cidr" \u2192 search "Cidr" \u2192 search "*cidr*" (WRONG: same keyword repeated with variations)',
36593
+ ' \u2192 search "AllowedIPs" \u2192 search "allowedIps" \u2192 search "allowed_ips" (WRONG: case/style variations, probe handles them)',
36594
+ ' \u2192 search "limitDRL" \u2192 search "LimitDRL" (WRONG: case variation of same term)',
36595
+ ' \u2192 search "throttle_retry_limit" after searching "ThrottleRetryLimit" (WRONG: snake_case variation, probe handles it)',
36596
+ ' \u2192 search "ThrottleRetryLimit" path=tyk \u2192 search "ThrottleRetryLimit" path=gateway \u2192 search "ThrottleRetryLimit" path=apidef (WRONG: same query on different paths hoping for different results)',
36597
+ ' \u2192 search "func (k *RateLimitAndQuotaCheck) handleRateLimitFailure" (WRONG: do not search full function signatures, just use exact=true "handleRateLimitFailure")',
36598
+ ' \u2192 search "ForwardMessage" \u2192 search "ForwardMessage" \u2192 search "ForwardMessage" (WRONG: repeating the exact same query)',
36585
36599
  ' \u2192 search "error handling" \u2192 search "error handling" \u2192 search "error handling" (WRONG: repeating exact same query)',
36586
36600
  "",
36587
36601
  "Keyword tips:",
@@ -36590,12 +36604,13 @@ function buildSearchDelegateTask({ searchQuery, searchPath, exact, language, all
36590
36604
  '- To bypass stopword filtering: wrap terms in quotes ("return", "struct") or set exact=true. Both disable stemming and splitting too.',
36591
36605
  "- Multiple words without operators use OR logic: foo bar = foo OR bar. Use AND explicitly if you need both: foo AND bar.",
36592
36606
  '- camelCase terms are split: getUserData becomes "get", "user", "data" \u2014 so one search covers all naming styles.',
36607
+ '- Do NOT search for full function signatures like "func (r *Type) Method(args)". Just search for the method name with exact=true.',
36593
36608
  "",
36594
36609
  "Strategy:",
36595
36610
  "1. Analyze the query - identify key concepts, entities, and relationships",
36596
- "2. Run ONE focused search per concept with the most natural keyword. Trust probe to handle variations.",
36611
+ "2. Run ONE focused search per concept. For known symbol names use exact=true. For concepts use default (exact=false).",
36597
36612
  "3. If a search returns results, use extract to verify relevance",
36598
- "4. Only try a different keyword if the first one returned irrelevant results (not if it returned no results \u2014 that means the concept is absent)",
36613
+ "4. If a search returns NO results, the term does not exist in the codebase. Do NOT retry with variations, different paths, or longer strings. Move on.",
36599
36614
  "5. Combine all relevant targets in your final response",
36600
36615
  "",
36601
36616
  `Query: ${searchQuery}`,
@@ -36918,6 +36933,36 @@ var init_vercel = __esm({
36918
36933
  } else if (targets) {
36919
36934
  const parsedTargets = parseTargets(targets);
36920
36935
  extractFiles = parsedTargets.map((target) => resolveTargetPath(target, effectiveCwd));
36936
+ if (options.allowedFolders && options.allowedFolders.length > 0) {
36937
+ extractFiles = extractFiles.map((target) => {
36938
+ const { filePart, suffix } = splitTargetSuffix(target);
36939
+ if ((0, import_fs4.existsSync)(filePart)) return target;
36940
+ const cwdPrefix = effectiveCwd.endsWith("/") ? effectiveCwd : effectiveCwd + "/";
36941
+ const relativePart = filePart.startsWith(cwdPrefix) ? filePart.slice(cwdPrefix.length) : null;
36942
+ if (relativePart) {
36943
+ for (const folder of options.allowedFolders) {
36944
+ const candidate = folder + "/" + relativePart;
36945
+ if ((0, import_fs4.existsSync)(candidate)) {
36946
+ if (debug) console.error(`[extract] Auto-fixed path: ${filePart} \u2192 ${candidate}`);
36947
+ return candidate + suffix;
36948
+ }
36949
+ }
36950
+ }
36951
+ for (const folder of options.allowedFolders) {
36952
+ const folderPrefix = folder.endsWith("/") ? folder : folder + "/";
36953
+ const wsParent = folderPrefix.replace(/[^/]+\/$/, "");
36954
+ if (filePart.startsWith(wsParent)) {
36955
+ const tail = filePart.slice(wsParent.length);
36956
+ const candidate = folderPrefix + tail;
36957
+ if (candidate !== filePart && (0, import_fs4.existsSync)(candidate)) {
36958
+ if (debug) console.error(`[extract] Auto-fixed path via workspace: ${filePart} \u2192 ${candidate}`);
36959
+ return candidate + suffix;
36960
+ }
36961
+ }
36962
+ }
36963
+ return target;
36964
+ });
36965
+ }
36921
36966
  let effectiveFormat = format2;
36922
36967
  if (outline && format2 === "outline-xml") {
36923
36968
  effectiveFormat = "xml";
package/cjs/index.cjs CHANGED
@@ -6063,7 +6063,7 @@ var init_NormalizedSchema = __esm({
6063
6063
  if (this.isDocumentSchema()) {
6064
6064
  return member([15, 0], memberName);
6065
6065
  }
6066
- throw new Error(`@smithy/core/schema - ${this.getName(true)} has no no member=${memberName}.`);
6066
+ throw new Error(`@smithy/core/schema - ${this.getName(true)} has no member=${memberName}.`);
6067
6067
  }
6068
6068
  getMemberSchemas() {
6069
6069
  const buffer = {};
@@ -7854,7 +7854,7 @@ var init_EventStreamSerde = __esm({
7854
7854
  throw new Error("@smithy/core/event-streams - non-struct member not supported in event stream union.");
7855
7855
  }
7856
7856
  }
7857
- const messageSerialization = serializer.flush();
7857
+ const messageSerialization = serializer.flush() ?? new Uint8Array();
7858
7858
  const body = typeof messageSerialization === "string" ? (this.serdeContext?.utf8Decoder ?? import_util_utf8.fromUtf8)(messageSerialization) : messageSerialization;
7859
7859
  return {
7860
7860
  body,
@@ -110725,13 +110725,19 @@ function buildSearchDelegateTask({ searchQuery, searchPath, exact, language, all
110725
110725
  "- listFiles: Understand directory structure to find where relevant code might live.",
110726
110726
  "",
110727
110727
  "CRITICAL - How probe search works (do NOT ignore):",
110728
- "- By default (exact=false), probe ALREADY handles stemming, case-insensitive matching, and camelCase/snake_case splitting.",
110728
+ "- By default (exact=false), probe ALREADY handles stemming, case-insensitive matching, and camelCase/snake_case splitting automatically.",
110729
110729
  '- Searching "allowed_ips" ALREADY matches "AllowedIPs", "allowedIps", "allowed_ips", etc. Do NOT manually try case/style variations.',
110730
110730
  '- Searching "getUserData" ALREADY matches "get", "user", "data" and their variations.',
110731
- "- NEVER repeat the same search query \u2014 you will get the same results.",
110731
+ "- NEVER repeat the same search query \u2014 you will get the same results. Changing the path does NOT change this.",
110732
110732
  "- NEVER search trivial variations of the same keyword (e.g., AllowedIPs then allowedIps then allowed_ips). This is wasteful \u2014 probe handles it.",
110733
- "- If a search returns no results, the term likely does not exist in that path. Try a genuinely DIFFERENT keyword or concept, not a variation.",
110734
- "- If 2-3 consecutive searches return no results for a concept, STOP searching for it and move on.",
110733
+ "- If a search returns no results, the term likely does not exist. Try a genuinely DIFFERENT keyword or concept, not a variation.",
110734
+ "- If 2-3 searches return no results for a concept, STOP searching for it and move on. Do NOT keep retrying.",
110735
+ "",
110736
+ "When to use exact=true:",
110737
+ "- Use exact=true when searching for a KNOWN symbol name (function, type, variable, struct).",
110738
+ "- exact=true matches the literal string only \u2014 no stemming, no splitting.",
110739
+ '- This is ideal for precise lookups: exact=true "ForwardMessage", exact=true "SessionLimiter", exact=true "ThrottleRetryLimit".',
110740
+ "- Do NOT use exact=true for exploratory/conceptual queries \u2014 use the default for those.",
110735
110741
  "",
110736
110742
  "GOOD search strategy (do this):",
110737
110743
  ' Query: "How does authentication work and how are sessions managed?"',
@@ -110740,10 +110746,18 @@ function buildSearchDelegateTask({ searchQuery, searchPath, exact, language, all
110740
110746
  ' \u2192 search "allowlist middleware" (one search, probe handles IP/ip/Ip variations)',
110741
110747
  ' Query: "How does BM25 scoring work with SIMD optimization?"',
110742
110748
  ' \u2192 search "BM25 scoring" \u2192 search "SIMD optimization" (two different concepts)',
110749
+ ' Query: "Find ForwardMessage and SessionLimiter functions"',
110750
+ ' \u2192 search exact=true "ForwardMessage" \u2192 search exact=true "SessionLimiter" (known symbols, use exact)',
110751
+ ' Query: "Find ThrottleRetryLimit usage"',
110752
+ ' \u2192 search exact=true "ThrottleRetryLimit" (one search, if no results the symbol does not exist \u2014 stop)',
110743
110753
  "",
110744
110754
  "BAD search strategy (never do this):",
110745
- ' \u2192 search "AllowedIPs" \u2192 search "allowedIps" \u2192 search "allowed_ips" (WRONG: these are trivial case variations, probe handles them)',
110746
- ' \u2192 search "CIDR" \u2192 search "cidr" \u2192 search "Cidr" \u2192 search "*cidr*" (WRONG: same keyword repeated with variations)',
110755
+ ' \u2192 search "AllowedIPs" \u2192 search "allowedIps" \u2192 search "allowed_ips" (WRONG: case/style variations, probe handles them)',
110756
+ ' \u2192 search "limitDRL" \u2192 search "LimitDRL" (WRONG: case variation of same term)',
110757
+ ' \u2192 search "throttle_retry_limit" after searching "ThrottleRetryLimit" (WRONG: snake_case variation, probe handles it)',
110758
+ ' \u2192 search "ThrottleRetryLimit" path=tyk \u2192 search "ThrottleRetryLimit" path=gateway \u2192 search "ThrottleRetryLimit" path=apidef (WRONG: same query on different paths hoping for different results)',
110759
+ ' \u2192 search "func (k *RateLimitAndQuotaCheck) handleRateLimitFailure" (WRONG: do not search full function signatures, just use exact=true "handleRateLimitFailure")',
110760
+ ' \u2192 search "ForwardMessage" \u2192 search "ForwardMessage" \u2192 search "ForwardMessage" (WRONG: repeating the exact same query)',
110747
110761
  ' \u2192 search "error handling" \u2192 search "error handling" \u2192 search "error handling" (WRONG: repeating exact same query)',
110748
110762
  "",
110749
110763
  "Keyword tips:",
@@ -110752,12 +110766,13 @@ function buildSearchDelegateTask({ searchQuery, searchPath, exact, language, all
110752
110766
  '- To bypass stopword filtering: wrap terms in quotes ("return", "struct") or set exact=true. Both disable stemming and splitting too.',
110753
110767
  "- Multiple words without operators use OR logic: foo bar = foo OR bar. Use AND explicitly if you need both: foo AND bar.",
110754
110768
  '- camelCase terms are split: getUserData becomes "get", "user", "data" \u2014 so one search covers all naming styles.',
110769
+ '- Do NOT search for full function signatures like "func (r *Type) Method(args)". Just search for the method name with exact=true.',
110755
110770
  "",
110756
110771
  "Strategy:",
110757
110772
  "1. Analyze the query - identify key concepts, entities, and relationships",
110758
- "2. Run ONE focused search per concept with the most natural keyword. Trust probe to handle variations.",
110773
+ "2. Run ONE focused search per concept. For known symbol names use exact=true. For concepts use default (exact=false).",
110759
110774
  "3. If a search returns results, use extract to verify relevance",
110760
- "4. Only try a different keyword if the first one returned irrelevant results (not if it returned no results \u2014 that means the concept is absent)",
110775
+ "4. If a search returns NO results, the term does not exist in the codebase. Do NOT retry with variations, different paths, or longer strings. Move on.",
110761
110776
  "5. Combine all relevant targets in your final response",
110762
110777
  "",
110763
110778
  `Query: ${searchQuery}`,
@@ -111080,6 +111095,36 @@ var init_vercel = __esm({
111080
111095
  } else if (targets) {
111081
111096
  const parsedTargets = parseTargets(targets);
111082
111097
  extractFiles = parsedTargets.map((target) => resolveTargetPath(target, effectiveCwd));
111098
+ if (options.allowedFolders && options.allowedFolders.length > 0) {
111099
+ extractFiles = extractFiles.map((target) => {
111100
+ const { filePart, suffix } = splitTargetSuffix(target);
111101
+ if ((0, import_fs11.existsSync)(filePart)) return target;
111102
+ const cwdPrefix = effectiveCwd.endsWith("/") ? effectiveCwd : effectiveCwd + "/";
111103
+ const relativePart = filePart.startsWith(cwdPrefix) ? filePart.slice(cwdPrefix.length) : null;
111104
+ if (relativePart) {
111105
+ for (const folder of options.allowedFolders) {
111106
+ const candidate = folder + "/" + relativePart;
111107
+ if ((0, import_fs11.existsSync)(candidate)) {
111108
+ if (debug) console.error(`[extract] Auto-fixed path: ${filePart} \u2192 ${candidate}`);
111109
+ return candidate + suffix;
111110
+ }
111111
+ }
111112
+ }
111113
+ for (const folder of options.allowedFolders) {
111114
+ const folderPrefix = folder.endsWith("/") ? folder : folder + "/";
111115
+ const wsParent = folderPrefix.replace(/[^/]+\/$/, "");
111116
+ if (filePart.startsWith(wsParent)) {
111117
+ const tail = filePart.slice(wsParent.length);
111118
+ const candidate = folderPrefix + tail;
111119
+ if (candidate !== filePart && (0, import_fs11.existsSync)(candidate)) {
111120
+ if (debug) console.error(`[extract] Auto-fixed path via workspace: ${filePart} \u2192 ${candidate}`);
111121
+ return candidate + suffix;
111122
+ }
111123
+ }
111124
+ }
111125
+ return target;
111126
+ });
111127
+ }
111083
111128
  let effectiveFormat = format2;
111084
111129
  if (outline && format2 === "outline-xml") {
111085
111130
  effectiveFormat = "xml";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@probelabs/probe",
3
- "version": "0.6.0-rc281",
3
+ "version": "0.6.0-rc282",
4
4
  "description": "Node.js wrapper for the probe code search tool",
5
5
  "main": "src/index.js",
6
6
  "module": "src/index.js",
@@ -75,7 +75,7 @@
75
75
  "dependencies": {
76
76
  "@ai-sdk/amazon-bedrock": "^1.0.8",
77
77
  "@ai-sdk/anthropic": "^2.0.8",
78
- "@ai-sdk/google": "^2.0.14",
78
+ "@ai-sdk/google": "^3.0.37",
79
79
  "@ai-sdk/openai": "^2.0.10",
80
80
  "@anthropic-ai/claude-agent-sdk": "^0.1.46",
81
81
  "@modelcontextprotocol/sdk": "^1.0.0",
@@ -144,13 +144,19 @@ function buildSearchDelegateTask({ searchQuery, searchPath, exact, language, all
144
144
  '- listFiles: Understand directory structure to find where relevant code might live.',
145
145
  '',
146
146
  'CRITICAL - How probe search works (do NOT ignore):',
147
- '- By default (exact=false), probe ALREADY handles stemming, case-insensitive matching, and camelCase/snake_case splitting.',
147
+ '- By default (exact=false), probe ALREADY handles stemming, case-insensitive matching, and camelCase/snake_case splitting automatically.',
148
148
  '- Searching "allowed_ips" ALREADY matches "AllowedIPs", "allowedIps", "allowed_ips", etc. Do NOT manually try case/style variations.',
149
149
  '- Searching "getUserData" ALREADY matches "get", "user", "data" and their variations.',
150
- '- NEVER repeat the same search query — you will get the same results.',
150
+ '- NEVER repeat the same search query — you will get the same results. Changing the path does NOT change this.',
151
151
  '- NEVER search trivial variations of the same keyword (e.g., AllowedIPs then allowedIps then allowed_ips). This is wasteful — probe handles it.',
152
- '- If a search returns no results, the term likely does not exist in that path. Try a genuinely DIFFERENT keyword or concept, not a variation.',
153
- '- If 2-3 consecutive searches return no results for a concept, STOP searching for it and move on.',
152
+ '- If a search returns no results, the term likely does not exist. Try a genuinely DIFFERENT keyword or concept, not a variation.',
153
+ '- If 2-3 searches return no results for a concept, STOP searching for it and move on. Do NOT keep retrying.',
154
+ '',
155
+ 'When to use exact=true:',
156
+ '- Use exact=true when searching for a KNOWN symbol name (function, type, variable, struct).',
157
+ '- exact=true matches the literal string only — no stemming, no splitting.',
158
+ '- This is ideal for precise lookups: exact=true "ForwardMessage", exact=true "SessionLimiter", exact=true "ThrottleRetryLimit".',
159
+ '- Do NOT use exact=true for exploratory/conceptual queries — use the default for those.',
154
160
  '',
155
161
  'GOOD search strategy (do this):',
156
162
  ' Query: "How does authentication work and how are sessions managed?"',
@@ -159,10 +165,18 @@ function buildSearchDelegateTask({ searchQuery, searchPath, exact, language, all
159
165
  ' → search "allowlist middleware" (one search, probe handles IP/ip/Ip variations)',
160
166
  ' Query: "How does BM25 scoring work with SIMD optimization?"',
161
167
  ' → search "BM25 scoring" → search "SIMD optimization" (two different concepts)',
168
+ ' Query: "Find ForwardMessage and SessionLimiter functions"',
169
+ ' → search exact=true "ForwardMessage" → search exact=true "SessionLimiter" (known symbols, use exact)',
170
+ ' Query: "Find ThrottleRetryLimit usage"',
171
+ ' → search exact=true "ThrottleRetryLimit" (one search, if no results the symbol does not exist — stop)',
162
172
  '',
163
173
  'BAD search strategy (never do this):',
164
- ' → search "AllowedIPs" → search "allowedIps" → search "allowed_ips" (WRONG: these are trivial case variations, probe handles them)',
165
- ' → search "CIDR" → search "cidr" → search "Cidr" → search "*cidr*" (WRONG: same keyword repeated with variations)',
174
+ ' → search "AllowedIPs" → search "allowedIps" → search "allowed_ips" (WRONG: case/style variations, probe handles them)',
175
+ ' → search "limitDRL" → search "LimitDRL" (WRONG: case variation of same term)',
176
+ ' → search "throttle_retry_limit" after searching "ThrottleRetryLimit" (WRONG: snake_case variation, probe handles it)',
177
+ ' → search "ThrottleRetryLimit" path=tyk → search "ThrottleRetryLimit" path=gateway → search "ThrottleRetryLimit" path=apidef (WRONG: same query on different paths hoping for different results)',
178
+ ' → search "func (k *RateLimitAndQuotaCheck) handleRateLimitFailure" (WRONG: do not search full function signatures, just use exact=true "handleRateLimitFailure")',
179
+ ' → search "ForwardMessage" → search "ForwardMessage" → search "ForwardMessage" (WRONG: repeating the exact same query)',
166
180
  ' → search "error handling" → search "error handling" → search "error handling" (WRONG: repeating exact same query)',
167
181
  '',
168
182
  'Keyword tips:',
@@ -171,12 +185,13 @@ function buildSearchDelegateTask({ searchQuery, searchPath, exact, language, all
171
185
  '- To bypass stopword filtering: wrap terms in quotes ("return", "struct") or set exact=true. Both disable stemming and splitting too.',
172
186
  '- Multiple words without operators use OR logic: foo bar = foo OR bar. Use AND explicitly if you need both: foo AND bar.',
173
187
  '- camelCase terms are split: getUserData becomes "get", "user", "data" — so one search covers all naming styles.',
188
+ '- Do NOT search for full function signatures like "func (r *Type) Method(args)". Just search for the method name with exact=true.',
174
189
  '',
175
190
  'Strategy:',
176
191
  '1. Analyze the query - identify key concepts, entities, and relationships',
177
- '2. Run ONE focused search per concept with the most natural keyword. Trust probe to handle variations.',
192
+ '2. Run ONE focused search per concept. For known symbol names use exact=true. For concepts use default (exact=false).',
178
193
  '3. If a search returns results, use extract to verify relevance',
179
- '4. Only try a different keyword if the first one returned irrelevant results (not if it returned no results that means the concept is absent)',
194
+ '4. If a search returns NO results, the term does not exist in the codebase. Do NOT retry with variations, different paths, or longer strings. Move on.',
180
195
  '5. Combine all relevant targets in your final response',
181
196
  '',
182
197
  `Query: ${searchQuery}`,
@@ -570,6 +585,50 @@ export const extractTool = (options = {}) => {
570
585
  // Resolve relative paths in targets against cwd
571
586
  extractFiles = parsedTargets.map(target => resolveTargetPath(target, effectiveCwd));
572
587
 
588
+ // Auto-fix: if resolved paths don't exist, try allowedFolders subdirs
589
+ // Handles when search returns relative paths (e.g., "gateway/file.go") and
590
+ // model constructs wrong absolute paths (e.g., /workspace/gateway/file.go
591
+ // instead of /workspace/tyk/gateway/file.go)
592
+ if (options.allowedFolders && options.allowedFolders.length > 0) {
593
+ extractFiles = extractFiles.map(target => {
594
+ const { filePart, suffix } = splitTargetSuffix(target);
595
+ if (existsSync(filePart)) return target;
596
+
597
+ // Try resolving the relative tail against each allowedFolder
598
+ const cwdPrefix = (effectiveCwd.endsWith('/') ? effectiveCwd : effectiveCwd + '/');
599
+ const relativePart = filePart.startsWith(cwdPrefix)
600
+ ? filePart.slice(cwdPrefix.length)
601
+ : null;
602
+
603
+ if (relativePart) {
604
+ for (const folder of options.allowedFolders) {
605
+ const candidate = folder + '/' + relativePart;
606
+ if (existsSync(candidate)) {
607
+ if (debug) console.error(`[extract] Auto-fixed path: ${filePart} → ${candidate}`);
608
+ return candidate + suffix;
609
+ }
610
+ }
611
+ }
612
+
613
+ // Try stripping workspace prefix and resolving against allowedFolders
614
+ // e.g., /tmp/visor-workspaces/abc/gateway/file.go → try each folder + gateway/file.go
615
+ for (const folder of options.allowedFolders) {
616
+ const folderPrefix = folder.endsWith('/') ? folder : folder + '/';
617
+ const wsParent = folderPrefix.replace(/[^/]+\/$/, '');
618
+ if (filePart.startsWith(wsParent)) {
619
+ const tail = filePart.slice(wsParent.length);
620
+ const candidate = folderPrefix + tail;
621
+ if (candidate !== filePart && existsSync(candidate)) {
622
+ if (debug) console.error(`[extract] Auto-fixed path via workspace: ${filePart} → ${candidate}`);
623
+ return candidate + suffix;
624
+ }
625
+ }
626
+ }
627
+
628
+ return target;
629
+ });
630
+ }
631
+
573
632
  // Apply format mapping for outline-xml to xml
574
633
  let effectiveFormat = format;
575
634
  if (outline && format === 'outline-xml') {