@probelabs/probe 0.6.0-rc288 → 0.6.0-rc291
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-rc291-aarch64-apple-darwin.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc291-aarch64-unknown-linux-musl.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc291-x86_64-apple-darwin.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc291-x86_64-pc-windows-msvc.zip +0 -0
- package/bin/binaries/probe-v0.6.0-rc291-x86_64-unknown-linux-musl.tar.gz +0 -0
- package/build/agent/ProbeAgent.js +95 -14
- package/build/agent/index.js +401 -86261
- package/build/agent/shared/prompts.js +27 -6
- package/build/extract.js +4 -2
- package/build/mcp/index.js +122 -9
- package/build/mcp/index.ts +162 -17
- package/build/search.js +6 -5
- package/build/tools/vercel.js +56 -23
- package/build/utils/error-types.js +2 -2
- package/build/utils/path-validation.js +1 -1
- package/cjs/agent/ProbeAgent.cjs +193 -45
- package/cjs/index.cjs +193 -45
- package/package.json +2 -1
- package/src/agent/ProbeAgent.js +95 -14
- package/src/agent/shared/prompts.js +27 -6
- package/src/extract.js +4 -2
- package/src/mcp/index.ts +162 -17
- package/src/search.js +6 -5
- package/src/tools/vercel.js +56 -23
- package/src/utils/error-types.js +2 -2
- package/src/utils/path-validation.js +1 -1
- package/bin/binaries/probe-v0.6.0-rc288-aarch64-apple-darwin.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc288-aarch64-unknown-linux-musl.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc288-x86_64-apple-darwin.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc288-x86_64-pc-windows-msvc.zip +0 -0
- package/bin/binaries/probe-v0.6.0-rc288-x86_64-unknown-linux-musl.tar.gz +0 -0
package/cjs/agent/ProbeAgent.cjs
CHANGED
|
@@ -25324,13 +25324,13 @@ function categorizeError(error40) {
|
|
|
25324
25324
|
if (lowerMessage.includes("path does not exist") || lowerMessage.includes("no such file or directory") || errorCode === "enoent") {
|
|
25325
25325
|
return new PathError(message, {
|
|
25326
25326
|
originalError: error40,
|
|
25327
|
-
suggestion: "The specified path does not exist.
|
|
25327
|
+
suggestion: "The specified path does not exist. Use the listFiles tool to check the correct directory structure, then retry with a valid path."
|
|
25328
25328
|
});
|
|
25329
25329
|
}
|
|
25330
25330
|
if (lowerMessage.includes("not a directory") || errorCode === "enotdir") {
|
|
25331
25331
|
return new PathError(message, {
|
|
25332
25332
|
originalError: error40,
|
|
25333
|
-
suggestion: "The path is not a directory.
|
|
25333
|
+
suggestion: "The path is not a directory. Use the listFiles tool to find the correct directory, then retry."
|
|
25334
25334
|
});
|
|
25335
25335
|
}
|
|
25336
25336
|
if (lowerMessage.includes("permission denied") || errorCode === "eacces") {
|
|
@@ -25586,7 +25586,7 @@ async function validateCwdPath(inputPath, defaultPath = process.cwd()) {
|
|
|
25586
25586
|
}
|
|
25587
25587
|
if (error40.code === "ENOENT") {
|
|
25588
25588
|
throw new PathError(`Path does not exist: ${normalizedPath}`, {
|
|
25589
|
-
suggestion: "The specified path does not exist.
|
|
25589
|
+
suggestion: "The specified path does not exist. Use the listFiles tool to check the correct directory structure, then retry with a valid path.",
|
|
25590
25590
|
details: { path: normalizedPath }
|
|
25591
25591
|
});
|
|
25592
25592
|
}
|
|
@@ -25821,7 +25821,8 @@ var init_search = __esm({
|
|
|
25821
25821
|
session: "--session",
|
|
25822
25822
|
timeout: "--timeout",
|
|
25823
25823
|
language: "--language",
|
|
25824
|
-
format: "--format"
|
|
25824
|
+
format: "--format",
|
|
25825
|
+
lsp: "--lsp"
|
|
25825
25826
|
};
|
|
25826
25827
|
}
|
|
25827
25828
|
});
|
|
@@ -26057,7 +26058,8 @@ var init_extract = __esm({
|
|
|
26057
26058
|
allowTests: "--allow-tests",
|
|
26058
26059
|
contextLines: "--context",
|
|
26059
26060
|
format: "--format",
|
|
26060
|
-
inputFile: "--input-file"
|
|
26061
|
+
inputFile: "--input-file",
|
|
26062
|
+
lsp: "--lsp"
|
|
26061
26063
|
};
|
|
26062
26064
|
}
|
|
26063
26065
|
});
|
|
@@ -27291,11 +27293,9 @@ function autoQuoteSearchTerms(query2) {
|
|
|
27291
27293
|
const result = tokens.map((token) => {
|
|
27292
27294
|
if (token.startsWith('"')) return token;
|
|
27293
27295
|
if (operators.has(token)) return token;
|
|
27294
|
-
const hasUpper = /[A-Z]/.test(token);
|
|
27295
|
-
const hasLower = /[a-z]/.test(token);
|
|
27296
27296
|
const hasUnderscore = token.includes("_");
|
|
27297
|
-
const
|
|
27298
|
-
if (
|
|
27297
|
+
const hasCaseTransition = /[a-z][A-Z]/.test(token) || /[A-Z]{2,}[a-z]/.test(token);
|
|
27298
|
+
if (hasCaseTransition || hasUnderscore) {
|
|
27299
27299
|
return `"${token}"`;
|
|
27300
27300
|
}
|
|
27301
27301
|
return token;
|
|
@@ -27410,7 +27410,7 @@ function buildSearchDelegateTask({ searchQuery, searchPath, exact, language, all
|
|
|
27410
27410
|
"Break down complex queries into multiple searches to cover all aspects.",
|
|
27411
27411
|
"",
|
|
27412
27412
|
"Available tools:",
|
|
27413
|
-
"- search: Find code matching keywords or patterns. Run multiple searches for different aspects
|
|
27413
|
+
"- search: Find code matching keywords or patterns. Results are paginated \u2014 use nextPage=true when results are relevant to get more. Run multiple searches for different aspects.",
|
|
27414
27414
|
"- extract: Verify code snippets to ensure targets are actually relevant before including them.",
|
|
27415
27415
|
"- listFiles: Understand directory structure to find where relevant code might live.",
|
|
27416
27416
|
"",
|
|
@@ -27431,13 +27431,14 @@ function buildSearchDelegateTask({ searchQuery, searchPath, exact, language, all
|
|
|
27431
27431
|
"",
|
|
27432
27432
|
"Combining searches with OR:",
|
|
27433
27433
|
'- Multiple unquoted words use OR logic: rate limit matches files containing EITHER "rate" OR "limit".',
|
|
27434
|
-
`-
|
|
27434
|
+
`- IMPORTANT: Multiple quoted terms use AND logic by default: '"RateLimit" "middleware"' requires BOTH in the same file.`,
|
|
27435
|
+
`- To search for ANY of several quoted symbols, use the explicit OR operator: '"ForwardMessage" OR "SessionLimiter"'.`,
|
|
27435
27436
|
'- Without quotes, camelCase like limitDRL gets split into "limit" + "DRL" \u2014 not what you want for symbol lookup.',
|
|
27436
27437
|
"- Use OR to search for multiple related symbols in ONE search instead of separate searches.",
|
|
27437
27438
|
"- This is much faster than running separate searches sequentially.",
|
|
27438
|
-
`- Example: search '"ForwardMessage" "SessionLimiter"' finds files with either exact symbol in one call.`,
|
|
27439
|
-
`- Example: search '"limitDRL" "doRollingWindowWrite"' finds both rate limiting functions at once.`,
|
|
27440
|
-
|
|
27439
|
+
`- Example: search '"ForwardMessage" OR "SessionLimiter"' finds files with either exact symbol in one call.`,
|
|
27440
|
+
`- Example: search '"limitDRL" OR "doRollingWindowWrite"' finds both rate limiting functions at once.`,
|
|
27441
|
+
"- Use AND (or just put quoted terms together) when you need both terms in the same file.",
|
|
27441
27442
|
"",
|
|
27442
27443
|
"Parallel tool calls:",
|
|
27443
27444
|
"- When you need to search for INDEPENDENT concepts, call multiple search tools IN PARALLEL (same response).",
|
|
@@ -27451,10 +27452,10 @@ function buildSearchDelegateTask({ searchQuery, searchPath, exact, language, all
|
|
|
27451
27452
|
' Query: "Find the IP allowlist middleware"',
|
|
27452
27453
|
' \u2192 search "allowlist middleware" (one search, probe handles IP/ip/Ip variations)',
|
|
27453
27454
|
' Query: "Find ForwardMessage and SessionLimiter"',
|
|
27454
|
-
` \u2192 search '"ForwardMessage" "SessionLimiter"' (one OR search finds both exact symbols)`,
|
|
27455
|
+
` \u2192 search '"ForwardMessage" OR "SessionLimiter"' (one OR search finds both exact symbols)`,
|
|
27455
27456
|
' OR: search exact=true "ForwardMessage" + search exact=true "SessionLimiter" IN PARALLEL',
|
|
27456
27457
|
' Query: "Find limitDRL and limitRedis functions"',
|
|
27457
|
-
` \u2192 search '"limitDRL" "limitRedis"' (one OR search, quoted to prevent camelCase splitting)`,
|
|
27458
|
+
` \u2192 search '"limitDRL" OR "limitRedis"' (one OR search, quoted to prevent camelCase splitting)`,
|
|
27458
27459
|
' Query: "Find ThrottleRetryLimit usage"',
|
|
27459
27460
|
' \u2192 search exact=true "ThrottleRetryLimit" (one search, if no results the symbol does not exist \u2014 stop)',
|
|
27460
27461
|
' Query: "How does BM25 scoring work with SIMD optimization?"',
|
|
@@ -27462,7 +27463,7 @@ function buildSearchDelegateTask({ searchQuery, searchPath, exact, language, all
|
|
|
27462
27463
|
"",
|
|
27463
27464
|
"BAD search strategy (never do this):",
|
|
27464
27465
|
' \u2192 search "AllowedIPs" \u2192 search "allowedIps" \u2192 search "allowed_ips" (WRONG: case/style variations, probe handles them)',
|
|
27465
|
-
` \u2192 search "limitDRL" \u2192 search "LimitDRL" (WRONG: case variation \u2014 combine with OR: '"limitDRL" "limitRedis"')`,
|
|
27466
|
+
` \u2192 search "limitDRL" \u2192 search "LimitDRL" (WRONG: case variation \u2014 combine with OR: '"limitDRL" OR "limitRedis"')`,
|
|
27466
27467
|
' \u2192 search "throttle_retry_limit" after searching "ThrottleRetryLimit" (WRONG: snake_case variation, probe handles it)',
|
|
27467
27468
|
' \u2192 search "ThrottleRetryLimit" path=tyk \u2192 search "ThrottleRetryLimit" path=gateway \u2192 search "ThrottleRetryLimit" path=apidef (WRONG: same query on different paths \u2014 probe searches recursively)',
|
|
27468
27469
|
' \u2192 search "func (k *RateLimitAndQuotaCheck) handleRateLimitFailure" (WRONG: do not search full function signatures, just use exact=true "handleRateLimitFailure")',
|
|
@@ -27475,15 +27476,34 @@ function buildSearchDelegateTask({ searchQuery, searchPath, exact, language, all
|
|
|
27475
27476
|
'- To bypass stopword filtering: wrap terms in quotes ("return", "struct") or set exact=true. Both disable stemming and splitting too.',
|
|
27476
27477
|
'- camelCase terms are split: getUserData becomes "get", "user", "data" \u2014 so one search covers all naming styles.',
|
|
27477
27478
|
'- Do NOT search for full function signatures like "func (r *Type) Method(args)". Just search for the method name with exact=true.',
|
|
27479
|
+
'- Do NOT search for file names (e.g., "sliding_log.go"). Use listFiles to discover files by name.',
|
|
27480
|
+
"",
|
|
27481
|
+
"PAGINATION:",
|
|
27482
|
+
"- Search results are paginated (~20k tokens per page).",
|
|
27483
|
+
"- If your search returned relevant files, call the same query with nextPage=true to check for more.",
|
|
27484
|
+
'- Keep paginating while results stay relevant. Stop when results are off-topic or "All results retrieved".',
|
|
27485
|
+
"",
|
|
27486
|
+
"WHEN TO STOP:",
|
|
27487
|
+
"- After you have explored the main concept AND related subsystems.",
|
|
27488
|
+
"- Once you have 5-15 targets covering different aspects of the query.",
|
|
27489
|
+
'- If you get a "DUPLICATE SEARCH BLOCKED" message, move on.',
|
|
27478
27490
|
"",
|
|
27479
27491
|
"Strategy:",
|
|
27480
|
-
"1. Analyze the query
|
|
27481
|
-
|
|
27482
|
-
"
|
|
27492
|
+
"1. Analyze the query \u2014 identify key concepts, then brainstorm SYNONYMS and alternative terms for each.",
|
|
27493
|
+
' Code naming often differs from the concept: "authentication" \u2192 verify, credentials, login, auth;',
|
|
27494
|
+
' "rate limiting" \u2192 throttle, quota, limiter, bucket; "error handling" \u2192 catch, recover, panic.',
|
|
27495
|
+
" Think about what a developer would NAME the function/struct/variable, not just the concept.",
|
|
27496
|
+
"2. Run INDEPENDENT searches in PARALLEL \u2014 search for the main concept AND synonyms simultaneously.",
|
|
27497
|
+
" After each search, check if results are relevant. If yes, call nextPage=true for more results.",
|
|
27498
|
+
`3. Combine related symbols into OR searches: '"symbolA" OR "symbolB"' finds files with either.`,
|
|
27483
27499
|
"4. For known symbol names use exact=true. For concepts use default (exact=false).",
|
|
27484
|
-
"5.
|
|
27485
|
-
"
|
|
27486
|
-
"
|
|
27500
|
+
"5. After your first round of searches, READ the extracted code and look for connected code:",
|
|
27501
|
+
" - Function calls to other important functions \u2192 include those targets.",
|
|
27502
|
+
" - Type references and imports \u2192 include type definitions.",
|
|
27503
|
+
" - Registered handlers/middleware \u2192 include all registered items.",
|
|
27504
|
+
"6. If a search returns results, use extract to verify relevance. Run multiple extracts in parallel too.",
|
|
27505
|
+
"7. If a search returns NO results, the term does not exist. Do NOT retry with variations. Move on.",
|
|
27506
|
+
"8. Once you have enough targets (typically 5-15), output your final JSON answer immediately.",
|
|
27487
27507
|
"",
|
|
27488
27508
|
`Query: ${searchQuery}`,
|
|
27489
27509
|
`Search path(s): ${searchPath}`,
|
|
@@ -27492,7 +27512,9 @@ function buildSearchDelegateTask({ searchQuery, searchPath, exact, language, all
|
|
|
27492
27512
|
'Return ONLY valid JSON: {"targets": ["path/to/file.ext#Symbol", "path/to/file.ext:line", "path/to/file.ext:start-end"]}',
|
|
27493
27513
|
'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.',
|
|
27494
27514
|
"Prefer #Symbol when a function/class name is clear; otherwise use line numbers.",
|
|
27495
|
-
"Deduplicate targets. Do NOT explain or answer - ONLY return the JSON targets."
|
|
27515
|
+
"Deduplicate targets. Do NOT explain or answer - ONLY return the JSON targets.",
|
|
27516
|
+
"",
|
|
27517
|
+
"Remember: if your search returned relevant results, use nextPage=true to check for more before outputting."
|
|
27496
27518
|
].join("\n");
|
|
27497
27519
|
}
|
|
27498
27520
|
var import_ai, import_fs4, CODE_SEARCH_SCHEMA, searchTool, queryTool, extractTool, delegateTool, analyzeAllTool;
|
|
@@ -27537,6 +27559,7 @@ var init_vercel = __esm({
|
|
|
27537
27559
|
return result;
|
|
27538
27560
|
};
|
|
27539
27561
|
const previousSearches = /* @__PURE__ */ new Set();
|
|
27562
|
+
let consecutiveDupBlocks = 0;
|
|
27540
27563
|
const paginationCounts = /* @__PURE__ */ new Map();
|
|
27541
27564
|
const MAX_PAGES_PER_QUERY = 3;
|
|
27542
27565
|
return (0, import_ai.tool)({
|
|
@@ -27589,12 +27612,17 @@ var init_vercel = __esm({
|
|
|
27589
27612
|
const searchKey = `${searchQuery}::${exact || false}`;
|
|
27590
27613
|
if (!nextPage) {
|
|
27591
27614
|
if (previousSearches.has(searchKey)) {
|
|
27615
|
+
consecutiveDupBlocks++;
|
|
27592
27616
|
if (debug) {
|
|
27593
|
-
console.error(`[DEDUP] Blocked duplicate search: "${searchQuery}" (path: "${searchPath}")`);
|
|
27617
|
+
console.error(`[DEDUP] Blocked duplicate search (${consecutiveDupBlocks}x): "${searchQuery}" (path: "${searchPath}")`);
|
|
27594
27618
|
}
|
|
27595
|
-
|
|
27619
|
+
if (consecutiveDupBlocks >= 3) {
|
|
27620
|
+
return "STOP. You have been blocked " + consecutiveDupBlocks + " times for repeating searches. You MUST output your final JSON answer NOW with whatever targets you have found. Do NOT call any more tools.";
|
|
27621
|
+
}
|
|
27622
|
+
return "DUPLICATE SEARCH BLOCKED (" + consecutiveDupBlocks + "x). You already searched for this. Do NOT repeat \u2014 probe searches recursively across all paths. Either: (1) use extract on results you already found, (2) try a COMPLETELY different keyword, or (3) output your final answer NOW.";
|
|
27596
27623
|
}
|
|
27597
27624
|
previousSearches.add(searchKey);
|
|
27625
|
+
consecutiveDupBlocks = 0;
|
|
27598
27626
|
paginationCounts.set(searchKey, 0);
|
|
27599
27627
|
} else {
|
|
27600
27628
|
const pageCount = (paginationCounts.get(searchKey) || 0) + 1;
|
|
@@ -27615,7 +27643,11 @@ var init_vercel = __esm({
|
|
|
27615
27643
|
return result;
|
|
27616
27644
|
} catch (error40) {
|
|
27617
27645
|
console.error("Error executing search command:", error40);
|
|
27618
|
-
|
|
27646
|
+
const formatted = formatErrorForAI(error40);
|
|
27647
|
+
if (error40.category === "path_error" || error40.message?.includes("does not exist")) {
|
|
27648
|
+
return formatted + "\n\nThe path does not exist. Use the listFiles tool to verify the correct directory structure before retrying. If the workspace itself is gone, output your final answer with whatever information you have.";
|
|
27649
|
+
}
|
|
27650
|
+
return formatted;
|
|
27619
27651
|
}
|
|
27620
27652
|
}
|
|
27621
27653
|
try {
|
|
@@ -30980,6 +31012,9 @@ var require_utils = __commonJS({
|
|
|
30980
31012
|
sandboxGlobal
|
|
30981
31013
|
};
|
|
30982
31014
|
context.prototypeWhitelist.set(Object.getPrototypeOf([][Symbol.iterator]()), /* @__PURE__ */ new Set());
|
|
31015
|
+
context.prototypeWhitelist.set(Object.getPrototypeOf(""[Symbol.iterator]()), /* @__PURE__ */ new Set());
|
|
31016
|
+
context.prototypeWhitelist.set(Object.getPrototypeOf((/* @__PURE__ */ new Set())[Symbol.iterator]()), /* @__PURE__ */ new Set());
|
|
31017
|
+
context.prototypeWhitelist.set(Object.getPrototypeOf((/* @__PURE__ */ new Map())[Symbol.iterator]()), /* @__PURE__ */ new Set());
|
|
30983
31018
|
return context;
|
|
30984
31019
|
}
|
|
30985
31020
|
function createExecContext(sandbox, executionTree, evalContext) {
|
|
@@ -32118,6 +32153,18 @@ var require_executor = __commonJS({
|
|
|
32118
32153
|
a = void 0;
|
|
32119
32154
|
}
|
|
32120
32155
|
}
|
|
32156
|
+
if (op === 29 && !a) {
|
|
32157
|
+
done(void 0, a);
|
|
32158
|
+
return;
|
|
32159
|
+
}
|
|
32160
|
+
if (op === 30 && a) {
|
|
32161
|
+
done(void 0, a);
|
|
32162
|
+
return;
|
|
32163
|
+
}
|
|
32164
|
+
if (op === 85 && a !== null && a !== void 0) {
|
|
32165
|
+
done(void 0, a);
|
|
32166
|
+
return;
|
|
32167
|
+
}
|
|
32121
32168
|
let bobj;
|
|
32122
32169
|
try {
|
|
32123
32170
|
let ad;
|
|
@@ -32180,6 +32227,18 @@ var require_executor = __commonJS({
|
|
|
32180
32227
|
a = void 0;
|
|
32181
32228
|
}
|
|
32182
32229
|
}
|
|
32230
|
+
if (op === 29 && !a) {
|
|
32231
|
+
done(void 0, a);
|
|
32232
|
+
return;
|
|
32233
|
+
}
|
|
32234
|
+
if (op === 30 && a) {
|
|
32235
|
+
done(void 0, a);
|
|
32236
|
+
return;
|
|
32237
|
+
}
|
|
32238
|
+
if (op === 85 && a !== null && a !== void 0) {
|
|
32239
|
+
done(void 0, a);
|
|
32240
|
+
return;
|
|
32241
|
+
}
|
|
32183
32242
|
let bobj;
|
|
32184
32243
|
try {
|
|
32185
32244
|
bobj = syncDone((d) => execSync(ticks, tree[2], scope, context, d, inLoopOrSwitch)).result;
|
|
@@ -88375,26 +88434,46 @@ var init_prompts = __esm({
|
|
|
88375
88434
|
CRITICAL - You are READ-ONLY:
|
|
88376
88435
|
You must NEVER create, modify, delete, or write files. You are strictly an exploration and analysis tool. If asked to make changes, implement features, fix bugs, or modify a PR, refuse and explain that file modifications must be done by the engineer tool \u2014 your role is only to investigate code and answer questions. Do not attempt workarounds using bash commands (echo, cat, tee, sed, etc.) to write files.
|
|
88377
88436
|
|
|
88437
|
+
CRITICAL - ALWAYS search before answering:
|
|
88438
|
+
You must NEVER answer questions about the codebase from memory or general knowledge. ALWAYS use the search and extract tools first to find the actual code, then base your answer ONLY on what you found. Even if you think you know the answer, you MUST verify it against the actual code. Your answers must be grounded in code evidence, not assumptions.
|
|
88439
|
+
|
|
88378
88440
|
When exploring code:
|
|
88379
88441
|
- Provide clear, concise explanations based on user request
|
|
88380
88442
|
- Find and highlight the most relevant code snippets, if required
|
|
88381
|
-
- Trace function calls and data flow through the system
|
|
88443
|
+
- Trace function calls and data flow through the system \u2014 follow the FULL call chain, not just the entry point
|
|
88382
88444
|
- Try to understand the user's intent and provide relevant information
|
|
88383
88445
|
- Understand high level picture
|
|
88384
88446
|
- Balance detail with clarity in your explanations
|
|
88447
|
+
- Search using SYNONYMS and alternative terms \u2014 code naming often differs from the concept name (e.g., "authentication" might be named verify_credentials, check_token, validate_session)
|
|
88448
|
+
- When you find a key function, look at what it CALLS and what CALLS it to discover the complete picture
|
|
88449
|
+
- Before answering, ask yourself: "Did I cover all the major components? Are there related subsystems I missed?" If yes, do one more search round.
|
|
88385
88450
|
|
|
88386
88451
|
When providing answers:
|
|
88452
|
+
- Be EXHAUSTIVE: cover ALL components you discovered, not just the main ones. If you found 10 related files, discuss all 10, not just the top 3. Users want the complete picture.
|
|
88453
|
+
- After drafting your answer, do a self-check: "What did I find in my searches that I haven't mentioned yet?" Add any missing components.
|
|
88454
|
+
- Include data structures, configuration options, and error handling \u2014 not just the happy path.
|
|
88387
88455
|
- Always include a "References" section at the end of your response
|
|
88388
88456
|
- List all relevant source code locations you found during exploration
|
|
88389
88457
|
- Use the format: file_path:line_number or file_path#symbol_name
|
|
88390
88458
|
- Group references by file when multiple locations are from the same file
|
|
88391
88459
|
- Include brief descriptions of what each reference contains`,
|
|
88392
|
-
"code-searcher": `You are ProbeChat Code
|
|
88460
|
+
"code-searcher": `You are ProbeChat Code Explorer & Searcher. Your job is to EXPLORE the codebase to find ALL relevant code locations for the query, then return them as JSON targets.
|
|
88461
|
+
|
|
88462
|
+
You think like a code explorer \u2014 you understand that codebases have layers:
|
|
88463
|
+
- Core implementations (algorithms, data structures)
|
|
88464
|
+
- Middleware/integration layers (request handlers, interceptors)
|
|
88465
|
+
- Configuration and storage backends
|
|
88466
|
+
- Scoping mechanisms (per-user, per-org, per-tenant, global)
|
|
88467
|
+
- Supporting utilities and helpers
|
|
88393
88468
|
|
|
88394
88469
|
When searching:
|
|
88395
|
-
-
|
|
88396
|
-
-
|
|
88397
|
-
-
|
|
88470
|
+
- Search for the MAIN concept first, then think: "what RELATED subsystems would a real codebase have?"
|
|
88471
|
+
- Use extract to READ the code you find \u2014 look for function calls, type references, and imports that point to OTHER relevant code
|
|
88472
|
+
- If you find middleware, check: are there org-level or tenant-level variants?
|
|
88473
|
+
- If you find algorithms, check: are there different storage backends?
|
|
88474
|
+
- Search results are paginated \u2014 if results look relevant, call nextPage=true to check for more files
|
|
88475
|
+
- Stop paginating when results become irrelevant or you see "All results retrieved"
|
|
88476
|
+
- Search using SYNONYMS \u2014 code naming differs from concepts (e.g., "rate limiting" \u2192 throttle, quota, limiter, bucket)
|
|
88398
88477
|
|
|
88399
88478
|
Output format (MANDATORY):
|
|
88400
88479
|
- Return ONLY valid JSON with a single top-level key: "targets"
|
|
@@ -88404,7 +88483,8 @@ Output format (MANDATORY):
|
|
|
88404
88483
|
- "path/to/file.ext:line"
|
|
88405
88484
|
- "path/to/file.ext:start-end"
|
|
88406
88485
|
- Prefer #SymbolName when a function/class name is clear; otherwise use line numbers
|
|
88407
|
-
- Deduplicate targets and keep them concise
|
|
88486
|
+
- Deduplicate targets and keep them concise
|
|
88487
|
+
- Aim for 5-15 targets covering ALL aspects of the query`,
|
|
88408
88488
|
"architect": `You are ProbeChat Architect, a specialized AI assistant focused on software architecture and design. Your primary function is to help users understand, analyze, and design software systems using the provided code analysis tools.
|
|
88409
88489
|
|
|
88410
88490
|
When analyzing code:
|
|
@@ -100539,7 +100619,7 @@ var init_ProbeAgent = __esm({
|
|
|
100539
100619
|
if (limiter && result.textStream) {
|
|
100540
100620
|
const originalStream = result.textStream;
|
|
100541
100621
|
const debug = this.debug;
|
|
100542
|
-
|
|
100622
|
+
const wrappedStream = (async function* () {
|
|
100543
100623
|
try {
|
|
100544
100624
|
for await (const chunk of originalStream) {
|
|
100545
100625
|
yield chunk;
|
|
@@ -100552,6 +100632,13 @@ var init_ProbeAgent = __esm({
|
|
|
100552
100632
|
}
|
|
100553
100633
|
}
|
|
100554
100634
|
})();
|
|
100635
|
+
return new Proxy(result, {
|
|
100636
|
+
get(target, prop) {
|
|
100637
|
+
if (prop === "textStream") return wrappedStream;
|
|
100638
|
+
const value = target[prop];
|
|
100639
|
+
return typeof value === "function" ? value.bind(target) : value;
|
|
100640
|
+
}
|
|
100641
|
+
});
|
|
100555
100642
|
} else if (limiter) {
|
|
100556
100643
|
limiter.release(null);
|
|
100557
100644
|
}
|
|
@@ -101777,9 +101864,9 @@ Workspace: ${this.allowedFolders.join(", ")}`;
|
|
|
101777
101864
|
Follow these instructions carefully:
|
|
101778
101865
|
1. Analyze the user's request.
|
|
101779
101866
|
2. Use the available tools step-by-step to fulfill the request.
|
|
101780
|
-
3. You
|
|
101781
|
-
4. Ensure to get really deep and understand the full picture before answering.
|
|
101782
|
-
5. Once the task is fully completed, provide your final answer directly as text.
|
|
101867
|
+
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 extracted code blocks. Use extract only to expand context or read full files." : " Search handles stemming and case variations automatically \u2014 do NOT try keyword variations manually. Read full files only if really necessary."}
|
|
101868
|
+
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).
|
|
101869
|
+
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.
|
|
101783
101870
|
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."}
|
|
101784
101871
|
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 ? `
|
|
101785
101872
|
7. When modifying files, choose the appropriate tool:
|
|
@@ -102165,6 +102252,22 @@ You are working with a workspace. Available paths: ${workspaceDesc}
|
|
|
102165
102252
|
if (recentTexts.every((t) => t && t === recentTexts[0])) return true;
|
|
102166
102253
|
if (recentTexts.every((t) => detectStuckResponse(t))) return true;
|
|
102167
102254
|
}
|
|
102255
|
+
if (steps.length >= 3) {
|
|
102256
|
+
const last3 = steps.slice(-3);
|
|
102257
|
+
const allHaveTools = last3.every((s) => s.toolCalls?.length === 1);
|
|
102258
|
+
if (allHaveTools) {
|
|
102259
|
+
const signatures = last3.map((s) => {
|
|
102260
|
+
const tc = s.toolCalls[0];
|
|
102261
|
+
return `${tc.toolName}::${JSON.stringify(tc.args ?? tc.input)}`;
|
|
102262
|
+
});
|
|
102263
|
+
if (signatures[0] === signatures[1] && signatures[1] === signatures[2]) {
|
|
102264
|
+
if (this.debug) {
|
|
102265
|
+
console.log(`[DEBUG] Circuit breaker: 3 consecutive identical tool calls detected (${last3[0].toolCalls[0].toolName}), forcing stop`);
|
|
102266
|
+
}
|
|
102267
|
+
return true;
|
|
102268
|
+
}
|
|
102269
|
+
}
|
|
102270
|
+
}
|
|
102168
102271
|
return false;
|
|
102169
102272
|
},
|
|
102170
102273
|
prepareStep: ({ steps, stepNumber }) => {
|
|
@@ -102173,6 +102276,37 @@ You are working with a workspace. Available paths: ${workspaceDesc}
|
|
|
102173
102276
|
toolChoice: "none"
|
|
102174
102277
|
};
|
|
102175
102278
|
}
|
|
102279
|
+
if (steps.length >= 2) {
|
|
102280
|
+
const last2 = steps.slice(-2);
|
|
102281
|
+
if (last2.every((s) => s.toolCalls?.length === 1)) {
|
|
102282
|
+
const tc1 = last2[0].toolCalls[0];
|
|
102283
|
+
const tc2 = last2[1].toolCalls[0];
|
|
102284
|
+
const sig1 = `${tc1.toolName}::${JSON.stringify(tc1.args ?? tc1.input)}`;
|
|
102285
|
+
const sig2 = `${tc2.toolName}::${JSON.stringify(tc2.args ?? tc2.input)}`;
|
|
102286
|
+
if (sig1 === sig2) {
|
|
102287
|
+
if (this.debug) {
|
|
102288
|
+
console.log(`[DEBUG] prepareStep: 2 consecutive identical tool calls (${tc1.toolName}), forcing toolChoice=none`);
|
|
102289
|
+
console.log(`[DEBUG] sig: ${sig1.substring(0, 200)}`);
|
|
102290
|
+
}
|
|
102291
|
+
return { toolChoice: "none" };
|
|
102292
|
+
}
|
|
102293
|
+
}
|
|
102294
|
+
}
|
|
102295
|
+
if (steps.length >= 3) {
|
|
102296
|
+
const last3 = steps.slice(-3);
|
|
102297
|
+
const allErrors = last3.every(
|
|
102298
|
+
(s) => s.toolResults?.length > 0 && s.toolResults.every((tr) => {
|
|
102299
|
+
const r = typeof tr.result === "string" ? tr.result : "";
|
|
102300
|
+
return r.includes("<error ") || r.includes("does not exist");
|
|
102301
|
+
})
|
|
102302
|
+
);
|
|
102303
|
+
if (allErrors) {
|
|
102304
|
+
if (this.debug) {
|
|
102305
|
+
console.log(`[DEBUG] prepareStep: 3 consecutive tool errors, forcing toolChoice=none`);
|
|
102306
|
+
}
|
|
102307
|
+
return { toolChoice: "none" };
|
|
102308
|
+
}
|
|
102309
|
+
}
|
|
102176
102310
|
const lastStep = steps[steps.length - 1];
|
|
102177
102311
|
const modelJustStopped = lastStep?.finishReason === "stop" && (!lastStep?.toolCalls || lastStep.toolCalls.length === 0);
|
|
102178
102312
|
if (modelJustStopped) {
|
|
@@ -102201,9 +102335,12 @@ Here is the result to review:
|
|
|
102201
102335
|
${resultToReview}
|
|
102202
102336
|
</result>
|
|
102203
102337
|
|
|
102204
|
-
|
|
102338
|
+
IMPORTANT: First review ALL completed work in the conversation above before taking any action.
|
|
102339
|
+
Double-check your response based on the criteria above. If everything looks good, respond with your previous answer exactly as-is. If your text has inaccuracies, fix the text. Only call a tool if you find a genuinely MISSING action \u2014 NEVER redo work that was already completed successfully. Respond with the COMPLETE corrected answer.`;
|
|
102205
102340
|
return {
|
|
102206
|
-
userMessage: completionPromptMessage
|
|
102341
|
+
userMessage: completionPromptMessage,
|
|
102342
|
+
toolChoice: "none"
|
|
102343
|
+
// Force text-only review — no tool calls
|
|
102207
102344
|
};
|
|
102208
102345
|
}
|
|
102209
102346
|
}
|
|
@@ -102245,7 +102382,11 @@ Double-check your response based on the criteria above. If everything looks good
|
|
|
102245
102382
|
options.onStream(text);
|
|
102246
102383
|
}
|
|
102247
102384
|
if (this.debug) {
|
|
102248
|
-
|
|
102385
|
+
const toolSummary = toolCalls?.length ? toolCalls.map((tc) => {
|
|
102386
|
+
const args = tc.args ? JSON.stringify(tc.args) : "";
|
|
102387
|
+
return args ? `${tc.toolName}(${debugTruncate(args, 120)})` : tc.toolName;
|
|
102388
|
+
}).join(", ") : "none";
|
|
102389
|
+
console.log(`[DEBUG] Step ${currentIteration}/${maxIterations} finished (reason: ${finishReason}, tools: [${toolSummary}])`);
|
|
102249
102390
|
if (text) {
|
|
102250
102391
|
console.log(`[DEBUG] model text: ${debugTruncate(text)}`);
|
|
102251
102392
|
}
|
|
@@ -102278,9 +102419,15 @@ Double-check your response based on the criteria above. If everything looks good
|
|
|
102278
102419
|
}
|
|
102279
102420
|
const executeAIRequest = async () => {
|
|
102280
102421
|
const result = await this.streamTextWithRetryAndFallback(streamOptions);
|
|
102281
|
-
const
|
|
102422
|
+
const steps = await result.steps;
|
|
102423
|
+
let finalText;
|
|
102424
|
+
if (steps && steps.length > 1) {
|
|
102425
|
+
const lastStepText = steps[steps.length - 1].text;
|
|
102426
|
+
finalText = lastStepText || await result.text;
|
|
102427
|
+
} else {
|
|
102428
|
+
finalText = await result.text;
|
|
102429
|
+
}
|
|
102282
102430
|
if (this.debug) {
|
|
102283
|
-
const steps = await result.steps;
|
|
102284
102431
|
console.log(`[DEBUG] streamText completed: ${steps?.length || 0} steps, finalText=${finalText?.length || 0} chars`);
|
|
102285
102432
|
}
|
|
102286
102433
|
const usage = await result.usage;
|
|
@@ -102348,14 +102495,15 @@ Here is the result to review:
|
|
|
102348
102495
|
${finalResult}
|
|
102349
102496
|
</result>
|
|
102350
102497
|
|
|
102351
|
-
|
|
102498
|
+
IMPORTANT: First review ALL completed work in the conversation above before taking any action.
|
|
102499
|
+
Double-check your response based on the criteria above. If everything looks good, respond with your previous answer exactly as-is. If your text has inaccuracies, fix the text. Only call a tool if you find a genuinely MISSING action \u2014 NEVER redo work that was already completed successfully. Respond with the COMPLETE corrected answer.`;
|
|
102352
102500
|
currentMessages.push({ role: "user", content: completionPromptMessage });
|
|
102353
|
-
const completionMaxIterations = 5;
|
|
102354
102501
|
const completionStreamOptions = {
|
|
102355
102502
|
model: this.provider ? this.provider(this.model) : this.model,
|
|
102356
102503
|
messages: this.prepareMessagesWithImages(currentMessages),
|
|
102357
102504
|
tools: tools2,
|
|
102358
|
-
|
|
102505
|
+
toolChoice: "none",
|
|
102506
|
+
// Force text-only response — no tool calls during review
|
|
102359
102507
|
maxTokens: maxResponseTokens,
|
|
102360
102508
|
temperature: 0.3,
|
|
102361
102509
|
onStepFinish: ({ toolResults, text, finishReason, usage }) => {
|