@probelabs/probe 0.6.0-rc282 → 0.6.0-rc284
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-rc284-aarch64-apple-darwin.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc284-aarch64-unknown-linux-musl.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc284-x86_64-apple-darwin.tar.gz +0 -0
- package/bin/binaries/{probe-v0.6.0-rc282-x86_64-pc-windows-msvc.zip → probe-v0.6.0-rc284-x86_64-pc-windows-msvc.zip} +0 -0
- package/bin/binaries/probe-v0.6.0-rc284-x86_64-unknown-linux-musl.tar.gz +0 -0
- package/build/agent/index.js +86 -17
- package/build/tools/vercel.js +118 -17
- package/cjs/agent/ProbeAgent.cjs +33005 -42172
- package/cjs/index.cjs +31096 -40263
- package/package.json +8 -8
- package/src/tools/vercel.js +118 -17
- package/bin/binaries/probe-v0.6.0-rc282-aarch64-apple-darwin.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc282-aarch64-unknown-linux-musl.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc282-x86_64-apple-darwin.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc282-x86_64-unknown-linux-musl.tar.gz +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@probelabs/probe",
|
|
3
|
-
"version": "0.6.0-
|
|
3
|
+
"version": "0.6.0-rc284",
|
|
4
4
|
"description": "Node.js wrapper for the probe code search tool",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"module": "src/index.js",
|
|
@@ -73,10 +73,10 @@
|
|
|
73
73
|
"author": "probelabs",
|
|
74
74
|
"license": "ISC",
|
|
75
75
|
"dependencies": {
|
|
76
|
-
"@ai-sdk/amazon-bedrock": "^
|
|
77
|
-
"@ai-sdk/anthropic": "^
|
|
78
|
-
"@ai-sdk/google": "^3.0.
|
|
79
|
-
"@ai-sdk/openai": "^
|
|
76
|
+
"@ai-sdk/amazon-bedrock": "^4.0.77",
|
|
77
|
+
"@ai-sdk/anthropic": "^3.0.58",
|
|
78
|
+
"@ai-sdk/google": "^3.0.43",
|
|
79
|
+
"@ai-sdk/openai": "^3.0.41",
|
|
80
80
|
"@anthropic-ai/claude-agent-sdk": "^0.1.46",
|
|
81
81
|
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
82
82
|
"@nyariv/sandboxjs": "github:probelabs/SandboxJS",
|
|
@@ -84,7 +84,7 @@
|
|
|
84
84
|
"acorn": "^8.15.0",
|
|
85
85
|
"acorn-walk": "^8.3.4",
|
|
86
86
|
"adm-zip": "^0.5.16",
|
|
87
|
-
"ai": "^
|
|
87
|
+
"ai": "^6.0.121",
|
|
88
88
|
"ajv": "^8.17.1",
|
|
89
89
|
"astring": "^1.9.0",
|
|
90
90
|
"axios": "^1.8.3",
|
|
@@ -94,7 +94,7 @@
|
|
|
94
94
|
"gpt-tokenizer": "^3.0.1",
|
|
95
95
|
"tar": "^6.2.0",
|
|
96
96
|
"yaml": "^2.6.1",
|
|
97
|
-
"zod": "^3.
|
|
97
|
+
"zod": "^3.25.76"
|
|
98
98
|
},
|
|
99
99
|
"devDependencies": {
|
|
100
100
|
"@jest/globals": "^29.7.0",
|
|
@@ -106,7 +106,7 @@
|
|
|
106
106
|
"typescript": "^5.9.2"
|
|
107
107
|
},
|
|
108
108
|
"peerDependencies": {
|
|
109
|
-
"ai": "^
|
|
109
|
+
"ai": "^6.0.0"
|
|
110
110
|
},
|
|
111
111
|
"engines": {
|
|
112
112
|
"node": ">=14.16.0"
|
package/src/tools/vercel.js
CHANGED
|
@@ -14,6 +14,75 @@ import { existsSync } from 'fs';
|
|
|
14
14
|
import { formatErrorForAI } from '../utils/error-types.js';
|
|
15
15
|
import { annotateOutputWithHashes } from './hashline.js';
|
|
16
16
|
|
|
17
|
+
/**
|
|
18
|
+
* Auto-quote search query terms that contain mixed case or underscores.
|
|
19
|
+
* Unquoted camelCase like "limitDRL" gets split by stemming into "limit" + "DRL".
|
|
20
|
+
* This wraps such terms in quotes so they match as literal strings.
|
|
21
|
+
*
|
|
22
|
+
* Examples:
|
|
23
|
+
* "limitDRL limitRedis" → '"limitDRL" "limitRedis"'
|
|
24
|
+
* "ThrottleRetryLimit" → '"ThrottleRetryLimit"'
|
|
25
|
+
* "allowed_ips" → '"allowed_ips"'
|
|
26
|
+
* "rate limit" → 'rate limit' (no change, all lowercase)
|
|
27
|
+
* '"already quoted"' → '"already quoted"' (no change)
|
|
28
|
+
* 'foo AND bar' → 'foo AND bar' (operators preserved)
|
|
29
|
+
*/
|
|
30
|
+
function autoQuoteSearchTerms(query) {
|
|
31
|
+
if (!query || typeof query !== 'string') return query;
|
|
32
|
+
|
|
33
|
+
// Split on whitespace, preserving quoted strings and operators
|
|
34
|
+
const tokens = [];
|
|
35
|
+
let i = 0;
|
|
36
|
+
while (i < query.length) {
|
|
37
|
+
// Skip whitespace
|
|
38
|
+
if (/\s/.test(query[i])) {
|
|
39
|
+
i++;
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
// Quoted string — keep as-is
|
|
43
|
+
if (query[i] === '"') {
|
|
44
|
+
const end = query.indexOf('"', i + 1);
|
|
45
|
+
if (end !== -1) {
|
|
46
|
+
tokens.push(query.substring(i, end + 1));
|
|
47
|
+
i = end + 1;
|
|
48
|
+
} else {
|
|
49
|
+
// Unclosed quote — take rest
|
|
50
|
+
tokens.push(query.substring(i));
|
|
51
|
+
break;
|
|
52
|
+
}
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
// Unquoted token
|
|
56
|
+
let j = i;
|
|
57
|
+
while (j < query.length && !/\s/.test(query[j]) && query[j] !== '"') {
|
|
58
|
+
j++;
|
|
59
|
+
}
|
|
60
|
+
tokens.push(query.substring(i, j));
|
|
61
|
+
i = j;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Boolean operators that should not be quoted
|
|
65
|
+
const operators = new Set(['AND', 'OR', 'NOT']);
|
|
66
|
+
|
|
67
|
+
const result = tokens.map(token => {
|
|
68
|
+
// Already quoted
|
|
69
|
+
if (token.startsWith('"')) return token;
|
|
70
|
+
// Boolean operator
|
|
71
|
+
if (operators.has(token)) return token;
|
|
72
|
+
// Check if token needs quoting: has mixed case (upper+lower) or underscores
|
|
73
|
+
const hasUpper = /[A-Z]/.test(token);
|
|
74
|
+
const hasLower = /[a-z]/.test(token);
|
|
75
|
+
const hasUnderscore = token.includes('_');
|
|
76
|
+
const hasMixedCase = hasUpper && hasLower;
|
|
77
|
+
if (hasMixedCase || hasUnderscore) {
|
|
78
|
+
return `"${token}"`;
|
|
79
|
+
}
|
|
80
|
+
return token;
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
return result.join(' ');
|
|
84
|
+
}
|
|
85
|
+
|
|
17
86
|
const CODE_SEARCH_SCHEMA = {
|
|
18
87
|
type: 'object',
|
|
19
88
|
properties: {
|
|
@@ -158,41 +227,61 @@ function buildSearchDelegateTask({ searchQuery, searchPath, exact, language, all
|
|
|
158
227
|
'- This is ideal for precise lookups: exact=true "ForwardMessage", exact=true "SessionLimiter", exact=true "ThrottleRetryLimit".',
|
|
159
228
|
'- Do NOT use exact=true for exploratory/conceptual queries — use the default for those.',
|
|
160
229
|
'',
|
|
230
|
+
'Combining searches with OR:',
|
|
231
|
+
'- Multiple unquoted words use OR logic: rate limit matches files containing EITHER "rate" OR "limit".',
|
|
232
|
+
'- For known symbol names, quote each term to prevent splitting: \'"limitDRL" "limitRedis"\' matches either exact symbol.',
|
|
233
|
+
'- Without quotes, camelCase like limitDRL gets split into "limit" + "DRL" — not what you want for symbol lookup.',
|
|
234
|
+
'- Use OR to search for multiple related symbols in ONE search instead of separate searches.',
|
|
235
|
+
'- This is much faster than running separate searches sequentially.',
|
|
236
|
+
'- Example: search \'"ForwardMessage" "SessionLimiter"\' finds files with either exact symbol in one call.',
|
|
237
|
+
'- Example: search \'"limitDRL" "doRollingWindowWrite"\' finds both rate limiting functions at once.',
|
|
238
|
+
'- Use AND only when you need both terms to appear in the same file: "rate AND limit".',
|
|
239
|
+
'',
|
|
240
|
+
'Parallel tool calls:',
|
|
241
|
+
'- When you need to search for INDEPENDENT concepts, call multiple search tools IN PARALLEL (same response).',
|
|
242
|
+
'- Do NOT wait for one search to finish before starting the next if they are independent.',
|
|
243
|
+
'- Example: for "rate limiting and session management", call search "rate limiting" AND search "session management" in parallel.',
|
|
244
|
+
'- Similarly, call multiple extract tools in parallel when verifying different files.',
|
|
245
|
+
'',
|
|
161
246
|
'GOOD search strategy (do this):',
|
|
162
247
|
' Query: "How does authentication work and how are sessions managed?"',
|
|
163
|
-
' → search "authentication"
|
|
248
|
+
' → search "authentication" + search "session management" IN PARALLEL (two independent concepts)',
|
|
164
249
|
' Query: "Find the IP allowlist middleware"',
|
|
165
250
|
' → search "allowlist middleware" (one search, probe handles IP/ip/Ip variations)',
|
|
166
|
-
' Query: "
|
|
167
|
-
' → search "
|
|
168
|
-
'
|
|
169
|
-
'
|
|
251
|
+
' Query: "Find ForwardMessage and SessionLimiter"',
|
|
252
|
+
' → search \'"ForwardMessage" "SessionLimiter"\' (one OR search finds both exact symbols)',
|
|
253
|
+
' OR: search exact=true "ForwardMessage" + search exact=true "SessionLimiter" IN PARALLEL',
|
|
254
|
+
' Query: "Find limitDRL and limitRedis functions"',
|
|
255
|
+
' → search \'"limitDRL" "limitRedis"\' (one OR search, quoted to prevent camelCase splitting)',
|
|
170
256
|
' Query: "Find ThrottleRetryLimit usage"',
|
|
171
257
|
' → search exact=true "ThrottleRetryLimit" (one search, if no results the symbol does not exist — stop)',
|
|
258
|
+
' Query: "How does BM25 scoring work with SIMD optimization?"',
|
|
259
|
+
' → search "BM25 scoring" + search "SIMD optimization" IN PARALLEL (two different concepts)',
|
|
172
260
|
'',
|
|
173
261
|
'BAD search strategy (never do this):',
|
|
174
262
|
' → search "AllowedIPs" → search "allowedIps" → search "allowed_ips" (WRONG: case/style variations, probe handles them)',
|
|
175
|
-
' → search "limitDRL" → search "LimitDRL" (WRONG: case variation
|
|
263
|
+
' → search "limitDRL" → search "LimitDRL" (WRONG: case variation — combine with OR: \'"limitDRL" "limitRedis"\')',
|
|
176
264
|
' → 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
|
|
265
|
+
' → search "ThrottleRetryLimit" path=tyk → search "ThrottleRetryLimit" path=gateway → search "ThrottleRetryLimit" path=apidef (WRONG: same query on different paths — probe searches recursively)',
|
|
178
266
|
' → search "func (k *RateLimitAndQuotaCheck) handleRateLimitFailure" (WRONG: do not search full function signatures, just use exact=true "handleRateLimitFailure")',
|
|
179
267
|
' → search "ForwardMessage" → search "ForwardMessage" → search "ForwardMessage" (WRONG: repeating the exact same query)',
|
|
180
|
-
' → search "
|
|
268
|
+
' → search "authentication" → wait → search "session management" → wait (WRONG: these are independent, run them in parallel)',
|
|
181
269
|
'',
|
|
182
270
|
'Keyword tips:',
|
|
183
271
|
'- Common programming keywords are filtered as stopwords when unquoted: function, class, return, new, struct, impl, var, let, const, etc.',
|
|
184
272
|
'- Avoid searching for these alone — combine with a specific term (e.g., "middleware function" is fine, "function" alone is too generic).',
|
|
185
273
|
'- To bypass stopword filtering: wrap terms in quotes ("return", "struct") or set exact=true. Both disable stemming and splitting too.',
|
|
186
|
-
'- Multiple words without operators use OR logic: foo bar = foo OR bar. Use AND explicitly if you need both: foo AND bar.',
|
|
187
274
|
'- camelCase terms are split: getUserData becomes "get", "user", "data" — so one search covers all naming styles.',
|
|
188
275
|
'- Do NOT search for full function signatures like "func (r *Type) Method(args)". Just search for the method name with exact=true.',
|
|
189
276
|
'',
|
|
190
277
|
'Strategy:',
|
|
191
|
-
'1. Analyze the query - identify key concepts
|
|
192
|
-
'2.
|
|
193
|
-
'3.
|
|
194
|
-
'4.
|
|
195
|
-
'5.
|
|
278
|
+
'1. Analyze the query - identify key concepts and group related symbols',
|
|
279
|
+
'2. Combine related symbols into OR searches: \'"symbolA" "symbolB"\' finds files with either (quote to prevent splitting)',
|
|
280
|
+
'3. Run INDEPENDENT searches in PARALLEL — do not wait for one to finish before starting another',
|
|
281
|
+
'4. For known symbol names use exact=true. For concepts use default (exact=false).',
|
|
282
|
+
'5. If a search returns results, use extract to verify relevance. Run multiple extracts in parallel too.',
|
|
283
|
+
'6. If a search returns NO results, the term does not exist. Do NOT retry with variations, different paths, or longer strings. Move on.',
|
|
284
|
+
'7. Combine all relevant targets in your final response',
|
|
196
285
|
'',
|
|
197
286
|
`Query: ${searchQuery}`,
|
|
198
287
|
`Search path(s): ${searchPath}`,
|
|
@@ -244,6 +333,16 @@ export const searchTool = (options = {}) => {
|
|
|
244
333
|
: searchDescription,
|
|
245
334
|
inputSchema: searchSchema,
|
|
246
335
|
execute: async ({ query: searchQuery, path, allow_tests, exact, maxTokens: paramMaxTokens, language, session, nextPage }) => {
|
|
336
|
+
// Auto-quote mixed-case and underscore terms to prevent unwanted stemming/splitting
|
|
337
|
+
// Skip when exact=true since that already preserves the literal string
|
|
338
|
+
if (!exact && searchQuery) {
|
|
339
|
+
const originalQuery = searchQuery;
|
|
340
|
+
searchQuery = autoQuoteSearchTerms(searchQuery);
|
|
341
|
+
if (debug && searchQuery !== originalQuery) {
|
|
342
|
+
console.error(`[search] Auto-quoted query: "${originalQuery}" → "${searchQuery}"`);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
247
346
|
// Use parameter maxTokens if provided, otherwise use the default
|
|
248
347
|
const effectiveMaxTokens = paramMaxTokens || maxTokens;
|
|
249
348
|
|
|
@@ -289,13 +388,15 @@ export const searchTool = (options = {}) => {
|
|
|
289
388
|
if (!searchDelegate) {
|
|
290
389
|
// Block duplicate non-paginated searches (models sometimes repeat the exact same call)
|
|
291
390
|
// Allow pagination: only nextPage=true is a legitimate repeat of the same query
|
|
292
|
-
|
|
391
|
+
// Use query+exact as the key (ignore path) to prevent path-hopping evasion
|
|
392
|
+
// where model searches same term on different subpaths hoping for different results
|
|
393
|
+
const searchKey = `${searchQuery}::${exact || false}`;
|
|
293
394
|
if (!nextPage) {
|
|
294
395
|
if (previousSearches.has(searchKey)) {
|
|
295
396
|
if (debug) {
|
|
296
|
-
console.error(`[DEDUP] Blocked duplicate search: "${searchQuery}"
|
|
397
|
+
console.error(`[DEDUP] Blocked duplicate search: "${searchQuery}" (path: "${searchPath}")`);
|
|
297
398
|
}
|
|
298
|
-
return 'DUPLICATE SEARCH BLOCKED: You already searched for this exact query
|
|
399
|
+
return 'DUPLICATE SEARCH BLOCKED: You already searched for this exact query. Changing the path does NOT give different results — probe searches recursively. Do NOT repeat the same search. Try a genuinely different keyword, use extract to examine results you already found, or use attempt_completion if you have enough information.';
|
|
299
400
|
}
|
|
300
401
|
previousSearches.add(searchKey);
|
|
301
402
|
paginationCounts.set(searchKey, 0);
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|