@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/index.cjs
CHANGED
|
@@ -1447,13 +1447,13 @@ function categorizeError(error40) {
|
|
|
1447
1447
|
if (lowerMessage.includes("path does not exist") || lowerMessage.includes("no such file or directory") || errorCode === "enoent") {
|
|
1448
1448
|
return new PathError(message, {
|
|
1449
1449
|
originalError: error40,
|
|
1450
|
-
suggestion: "The specified path does not exist.
|
|
1450
|
+
suggestion: "The specified path does not exist. Use the listFiles tool to check the correct directory structure, then retry with a valid path."
|
|
1451
1451
|
});
|
|
1452
1452
|
}
|
|
1453
1453
|
if (lowerMessage.includes("not a directory") || errorCode === "enotdir") {
|
|
1454
1454
|
return new PathError(message, {
|
|
1455
1455
|
originalError: error40,
|
|
1456
|
-
suggestion: "The path is not a directory.
|
|
1456
|
+
suggestion: "The path is not a directory. Use the listFiles tool to find the correct directory, then retry."
|
|
1457
1457
|
});
|
|
1458
1458
|
}
|
|
1459
1459
|
if (lowerMessage.includes("permission denied") || errorCode === "eacces") {
|
|
@@ -1709,7 +1709,7 @@ async function validateCwdPath(inputPath, defaultPath = process.cwd()) {
|
|
|
1709
1709
|
}
|
|
1710
1710
|
if (error40.code === "ENOENT") {
|
|
1711
1711
|
throw new PathError(`Path does not exist: ${normalizedPath}`, {
|
|
1712
|
-
suggestion: "The specified path does not exist.
|
|
1712
|
+
suggestion: "The specified path does not exist. Use the listFiles tool to check the correct directory structure, then retry with a valid path.",
|
|
1713
1713
|
details: { path: normalizedPath }
|
|
1714
1714
|
});
|
|
1715
1715
|
}
|
|
@@ -1944,7 +1944,8 @@ var init_search = __esm({
|
|
|
1944
1944
|
session: "--session",
|
|
1945
1945
|
timeout: "--timeout",
|
|
1946
1946
|
language: "--language",
|
|
1947
|
-
format: "--format"
|
|
1947
|
+
format: "--format",
|
|
1948
|
+
lsp: "--lsp"
|
|
1948
1949
|
};
|
|
1949
1950
|
}
|
|
1950
1951
|
});
|
|
@@ -2180,7 +2181,8 @@ var init_extract = __esm({
|
|
|
2180
2181
|
allowTests: "--allow-tests",
|
|
2181
2182
|
contextLines: "--context",
|
|
2182
2183
|
format: "--format",
|
|
2183
|
-
inputFile: "--input-file"
|
|
2184
|
+
inputFile: "--input-file",
|
|
2185
|
+
lsp: "--lsp"
|
|
2184
2186
|
};
|
|
2185
2187
|
}
|
|
2186
2188
|
});
|
|
@@ -73070,26 +73072,46 @@ var init_prompts = __esm({
|
|
|
73070
73072
|
CRITICAL - You are READ-ONLY:
|
|
73071
73073
|
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.
|
|
73072
73074
|
|
|
73075
|
+
CRITICAL - ALWAYS search before answering:
|
|
73076
|
+
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.
|
|
73077
|
+
|
|
73073
73078
|
When exploring code:
|
|
73074
73079
|
- Provide clear, concise explanations based on user request
|
|
73075
73080
|
- Find and highlight the most relevant code snippets, if required
|
|
73076
|
-
- Trace function calls and data flow through the system
|
|
73081
|
+
- Trace function calls and data flow through the system \u2014 follow the FULL call chain, not just the entry point
|
|
73077
73082
|
- Try to understand the user's intent and provide relevant information
|
|
73078
73083
|
- Understand high level picture
|
|
73079
73084
|
- Balance detail with clarity in your explanations
|
|
73085
|
+
- 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)
|
|
73086
|
+
- When you find a key function, look at what it CALLS and what CALLS it to discover the complete picture
|
|
73087
|
+
- Before answering, ask yourself: "Did I cover all the major components? Are there related subsystems I missed?" If yes, do one more search round.
|
|
73080
73088
|
|
|
73081
73089
|
When providing answers:
|
|
73090
|
+
- 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.
|
|
73091
|
+
- 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.
|
|
73092
|
+
- Include data structures, configuration options, and error handling \u2014 not just the happy path.
|
|
73082
73093
|
- Always include a "References" section at the end of your response
|
|
73083
73094
|
- List all relevant source code locations you found during exploration
|
|
73084
73095
|
- Use the format: file_path:line_number or file_path#symbol_name
|
|
73085
73096
|
- Group references by file when multiple locations are from the same file
|
|
73086
73097
|
- Include brief descriptions of what each reference contains`,
|
|
73087
|
-
"code-searcher": `You are ProbeChat Code
|
|
73098
|
+
"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.
|
|
73099
|
+
|
|
73100
|
+
You think like a code explorer \u2014 you understand that codebases have layers:
|
|
73101
|
+
- Core implementations (algorithms, data structures)
|
|
73102
|
+
- Middleware/integration layers (request handlers, interceptors)
|
|
73103
|
+
- Configuration and storage backends
|
|
73104
|
+
- Scoping mechanisms (per-user, per-org, per-tenant, global)
|
|
73105
|
+
- Supporting utilities and helpers
|
|
73088
73106
|
|
|
73089
73107
|
When searching:
|
|
73090
|
-
-
|
|
73091
|
-
-
|
|
73092
|
-
-
|
|
73108
|
+
- Search for the MAIN concept first, then think: "what RELATED subsystems would a real codebase have?"
|
|
73109
|
+
- Use extract to READ the code you find \u2014 look for function calls, type references, and imports that point to OTHER relevant code
|
|
73110
|
+
- If you find middleware, check: are there org-level or tenant-level variants?
|
|
73111
|
+
- If you find algorithms, check: are there different storage backends?
|
|
73112
|
+
- Search results are paginated \u2014 if results look relevant, call nextPage=true to check for more files
|
|
73113
|
+
- Stop paginating when results become irrelevant or you see "All results retrieved"
|
|
73114
|
+
- Search using SYNONYMS \u2014 code naming differs from concepts (e.g., "rate limiting" \u2192 throttle, quota, limiter, bucket)
|
|
73093
73115
|
|
|
73094
73116
|
Output format (MANDATORY):
|
|
73095
73117
|
- Return ONLY valid JSON with a single top-level key: "targets"
|
|
@@ -73099,7 +73121,8 @@ Output format (MANDATORY):
|
|
|
73099
73121
|
- "path/to/file.ext:line"
|
|
73100
73122
|
- "path/to/file.ext:start-end"
|
|
73101
73123
|
- Prefer #SymbolName when a function/class name is clear; otherwise use line numbers
|
|
73102
|
-
- Deduplicate targets and keep them concise
|
|
73124
|
+
- Deduplicate targets and keep them concise
|
|
73125
|
+
- Aim for 5-15 targets covering ALL aspects of the query`,
|
|
73103
73126
|
"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.
|
|
73104
73127
|
|
|
73105
73128
|
When analyzing code:
|
|
@@ -82677,6 +82700,9 @@ var require_utils2 = __commonJS({
|
|
|
82677
82700
|
sandboxGlobal
|
|
82678
82701
|
};
|
|
82679
82702
|
context.prototypeWhitelist.set(Object.getPrototypeOf([][Symbol.iterator]()), /* @__PURE__ */ new Set());
|
|
82703
|
+
context.prototypeWhitelist.set(Object.getPrototypeOf(""[Symbol.iterator]()), /* @__PURE__ */ new Set());
|
|
82704
|
+
context.prototypeWhitelist.set(Object.getPrototypeOf((/* @__PURE__ */ new Set())[Symbol.iterator]()), /* @__PURE__ */ new Set());
|
|
82705
|
+
context.prototypeWhitelist.set(Object.getPrototypeOf((/* @__PURE__ */ new Map())[Symbol.iterator]()), /* @__PURE__ */ new Set());
|
|
82680
82706
|
return context;
|
|
82681
82707
|
}
|
|
82682
82708
|
function createExecContext(sandbox, executionTree, evalContext) {
|
|
@@ -83815,6 +83841,18 @@ var require_executor = __commonJS({
|
|
|
83815
83841
|
a = void 0;
|
|
83816
83842
|
}
|
|
83817
83843
|
}
|
|
83844
|
+
if (op === 29 && !a) {
|
|
83845
|
+
done(void 0, a);
|
|
83846
|
+
return;
|
|
83847
|
+
}
|
|
83848
|
+
if (op === 30 && a) {
|
|
83849
|
+
done(void 0, a);
|
|
83850
|
+
return;
|
|
83851
|
+
}
|
|
83852
|
+
if (op === 85 && a !== null && a !== void 0) {
|
|
83853
|
+
done(void 0, a);
|
|
83854
|
+
return;
|
|
83855
|
+
}
|
|
83818
83856
|
let bobj;
|
|
83819
83857
|
try {
|
|
83820
83858
|
let ad;
|
|
@@ -83877,6 +83915,18 @@ var require_executor = __commonJS({
|
|
|
83877
83915
|
a = void 0;
|
|
83878
83916
|
}
|
|
83879
83917
|
}
|
|
83918
|
+
if (op === 29 && !a) {
|
|
83919
|
+
done(void 0, a);
|
|
83920
|
+
return;
|
|
83921
|
+
}
|
|
83922
|
+
if (op === 30 && a) {
|
|
83923
|
+
done(void 0, a);
|
|
83924
|
+
return;
|
|
83925
|
+
}
|
|
83926
|
+
if (op === 85 && a !== null && a !== void 0) {
|
|
83927
|
+
done(void 0, a);
|
|
83928
|
+
return;
|
|
83929
|
+
}
|
|
83880
83930
|
let bobj;
|
|
83881
83931
|
try {
|
|
83882
83932
|
bobj = syncDone((d) => execSync(ticks, tree[2], scope, context, d, inLoopOrSwitch)).result;
|
|
@@ -97515,7 +97565,7 @@ var init_ProbeAgent = __esm({
|
|
|
97515
97565
|
if (limiter && result.textStream) {
|
|
97516
97566
|
const originalStream = result.textStream;
|
|
97517
97567
|
const debug = this.debug;
|
|
97518
|
-
|
|
97568
|
+
const wrappedStream = (async function* () {
|
|
97519
97569
|
try {
|
|
97520
97570
|
for await (const chunk of originalStream) {
|
|
97521
97571
|
yield chunk;
|
|
@@ -97528,6 +97578,13 @@ var init_ProbeAgent = __esm({
|
|
|
97528
97578
|
}
|
|
97529
97579
|
}
|
|
97530
97580
|
})();
|
|
97581
|
+
return new Proxy(result, {
|
|
97582
|
+
get(target, prop) {
|
|
97583
|
+
if (prop === "textStream") return wrappedStream;
|
|
97584
|
+
const value = target[prop];
|
|
97585
|
+
return typeof value === "function" ? value.bind(target) : value;
|
|
97586
|
+
}
|
|
97587
|
+
});
|
|
97531
97588
|
} else if (limiter) {
|
|
97532
97589
|
limiter.release(null);
|
|
97533
97590
|
}
|
|
@@ -98753,9 +98810,9 @@ Workspace: ${this.allowedFolders.join(", ")}`;
|
|
|
98753
98810
|
Follow these instructions carefully:
|
|
98754
98811
|
1. Analyze the user's request.
|
|
98755
98812
|
2. Use the available tools step-by-step to fulfill the request.
|
|
98756
|
-
3. You
|
|
98757
|
-
4. Ensure to get really deep and understand the full picture before answering.
|
|
98758
|
-
5. Once the task is fully completed, provide your final answer directly as text.
|
|
98813
|
+
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."}
|
|
98814
|
+
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).
|
|
98815
|
+
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.
|
|
98759
98816
|
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."}
|
|
98760
98817
|
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 ? `
|
|
98761
98818
|
7. When modifying files, choose the appropriate tool:
|
|
@@ -99141,6 +99198,22 @@ You are working with a workspace. Available paths: ${workspaceDesc}
|
|
|
99141
99198
|
if (recentTexts.every((t) => t && t === recentTexts[0])) return true;
|
|
99142
99199
|
if (recentTexts.every((t) => detectStuckResponse(t))) return true;
|
|
99143
99200
|
}
|
|
99201
|
+
if (steps.length >= 3) {
|
|
99202
|
+
const last3 = steps.slice(-3);
|
|
99203
|
+
const allHaveTools = last3.every((s) => s.toolCalls?.length === 1);
|
|
99204
|
+
if (allHaveTools) {
|
|
99205
|
+
const signatures = last3.map((s) => {
|
|
99206
|
+
const tc = s.toolCalls[0];
|
|
99207
|
+
return `${tc.toolName}::${JSON.stringify(tc.args ?? tc.input)}`;
|
|
99208
|
+
});
|
|
99209
|
+
if (signatures[0] === signatures[1] && signatures[1] === signatures[2]) {
|
|
99210
|
+
if (this.debug) {
|
|
99211
|
+
console.log(`[DEBUG] Circuit breaker: 3 consecutive identical tool calls detected (${last3[0].toolCalls[0].toolName}), forcing stop`);
|
|
99212
|
+
}
|
|
99213
|
+
return true;
|
|
99214
|
+
}
|
|
99215
|
+
}
|
|
99216
|
+
}
|
|
99144
99217
|
return false;
|
|
99145
99218
|
},
|
|
99146
99219
|
prepareStep: ({ steps, stepNumber }) => {
|
|
@@ -99149,6 +99222,37 @@ You are working with a workspace. Available paths: ${workspaceDesc}
|
|
|
99149
99222
|
toolChoice: "none"
|
|
99150
99223
|
};
|
|
99151
99224
|
}
|
|
99225
|
+
if (steps.length >= 2) {
|
|
99226
|
+
const last2 = steps.slice(-2);
|
|
99227
|
+
if (last2.every((s) => s.toolCalls?.length === 1)) {
|
|
99228
|
+
const tc1 = last2[0].toolCalls[0];
|
|
99229
|
+
const tc2 = last2[1].toolCalls[0];
|
|
99230
|
+
const sig1 = `${tc1.toolName}::${JSON.stringify(tc1.args ?? tc1.input)}`;
|
|
99231
|
+
const sig2 = `${tc2.toolName}::${JSON.stringify(tc2.args ?? tc2.input)}`;
|
|
99232
|
+
if (sig1 === sig2) {
|
|
99233
|
+
if (this.debug) {
|
|
99234
|
+
console.log(`[DEBUG] prepareStep: 2 consecutive identical tool calls (${tc1.toolName}), forcing toolChoice=none`);
|
|
99235
|
+
console.log(`[DEBUG] sig: ${sig1.substring(0, 200)}`);
|
|
99236
|
+
}
|
|
99237
|
+
return { toolChoice: "none" };
|
|
99238
|
+
}
|
|
99239
|
+
}
|
|
99240
|
+
}
|
|
99241
|
+
if (steps.length >= 3) {
|
|
99242
|
+
const last3 = steps.slice(-3);
|
|
99243
|
+
const allErrors = last3.every(
|
|
99244
|
+
(s) => s.toolResults?.length > 0 && s.toolResults.every((tr) => {
|
|
99245
|
+
const r = typeof tr.result === "string" ? tr.result : "";
|
|
99246
|
+
return r.includes("<error ") || r.includes("does not exist");
|
|
99247
|
+
})
|
|
99248
|
+
);
|
|
99249
|
+
if (allErrors) {
|
|
99250
|
+
if (this.debug) {
|
|
99251
|
+
console.log(`[DEBUG] prepareStep: 3 consecutive tool errors, forcing toolChoice=none`);
|
|
99252
|
+
}
|
|
99253
|
+
return { toolChoice: "none" };
|
|
99254
|
+
}
|
|
99255
|
+
}
|
|
99152
99256
|
const lastStep = steps[steps.length - 1];
|
|
99153
99257
|
const modelJustStopped = lastStep?.finishReason === "stop" && (!lastStep?.toolCalls || lastStep.toolCalls.length === 0);
|
|
99154
99258
|
if (modelJustStopped) {
|
|
@@ -99177,9 +99281,12 @@ Here is the result to review:
|
|
|
99177
99281
|
${resultToReview}
|
|
99178
99282
|
</result>
|
|
99179
99283
|
|
|
99180
|
-
|
|
99284
|
+
IMPORTANT: First review ALL completed work in the conversation above before taking any action.
|
|
99285
|
+
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.`;
|
|
99181
99286
|
return {
|
|
99182
|
-
userMessage: completionPromptMessage
|
|
99287
|
+
userMessage: completionPromptMessage,
|
|
99288
|
+
toolChoice: "none"
|
|
99289
|
+
// Force text-only review — no tool calls
|
|
99183
99290
|
};
|
|
99184
99291
|
}
|
|
99185
99292
|
}
|
|
@@ -99221,7 +99328,11 @@ Double-check your response based on the criteria above. If everything looks good
|
|
|
99221
99328
|
options.onStream(text);
|
|
99222
99329
|
}
|
|
99223
99330
|
if (this.debug) {
|
|
99224
|
-
|
|
99331
|
+
const toolSummary = toolCalls?.length ? toolCalls.map((tc) => {
|
|
99332
|
+
const args = tc.args ? JSON.stringify(tc.args) : "";
|
|
99333
|
+
return args ? `${tc.toolName}(${debugTruncate(args, 120)})` : tc.toolName;
|
|
99334
|
+
}).join(", ") : "none";
|
|
99335
|
+
console.log(`[DEBUG] Step ${currentIteration}/${maxIterations} finished (reason: ${finishReason}, tools: [${toolSummary}])`);
|
|
99225
99336
|
if (text) {
|
|
99226
99337
|
console.log(`[DEBUG] model text: ${debugTruncate(text)}`);
|
|
99227
99338
|
}
|
|
@@ -99254,9 +99365,15 @@ Double-check your response based on the criteria above. If everything looks good
|
|
|
99254
99365
|
}
|
|
99255
99366
|
const executeAIRequest = async () => {
|
|
99256
99367
|
const result = await this.streamTextWithRetryAndFallback(streamOptions);
|
|
99257
|
-
const
|
|
99368
|
+
const steps = await result.steps;
|
|
99369
|
+
let finalText;
|
|
99370
|
+
if (steps && steps.length > 1) {
|
|
99371
|
+
const lastStepText = steps[steps.length - 1].text;
|
|
99372
|
+
finalText = lastStepText || await result.text;
|
|
99373
|
+
} else {
|
|
99374
|
+
finalText = await result.text;
|
|
99375
|
+
}
|
|
99258
99376
|
if (this.debug) {
|
|
99259
|
-
const steps = await result.steps;
|
|
99260
99377
|
console.log(`[DEBUG] streamText completed: ${steps?.length || 0} steps, finalText=${finalText?.length || 0} chars`);
|
|
99261
99378
|
}
|
|
99262
99379
|
const usage = await result.usage;
|
|
@@ -99324,14 +99441,15 @@ Here is the result to review:
|
|
|
99324
99441
|
${finalResult}
|
|
99325
99442
|
</result>
|
|
99326
99443
|
|
|
99327
|
-
|
|
99444
|
+
IMPORTANT: First review ALL completed work in the conversation above before taking any action.
|
|
99445
|
+
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.`;
|
|
99328
99446
|
currentMessages.push({ role: "user", content: completionPromptMessage });
|
|
99329
|
-
const completionMaxIterations = 5;
|
|
99330
99447
|
const completionStreamOptions = {
|
|
99331
99448
|
model: this.provider ? this.provider(this.model) : this.model,
|
|
99332
99449
|
messages: this.prepareMessagesWithImages(currentMessages),
|
|
99333
99450
|
tools: tools2,
|
|
99334
|
-
|
|
99451
|
+
toolChoice: "none",
|
|
99452
|
+
// Force text-only response — no tool calls during review
|
|
99335
99453
|
maxTokens: maxResponseTokens,
|
|
99336
99454
|
temperature: 0.3,
|
|
99337
99455
|
onStepFinish: ({ toolResults, text, finishReason, usage }) => {
|
|
@@ -101000,11 +101118,9 @@ function autoQuoteSearchTerms(query2) {
|
|
|
101000
101118
|
const result = tokens.map((token) => {
|
|
101001
101119
|
if (token.startsWith('"')) return token;
|
|
101002
101120
|
if (operators.has(token)) return token;
|
|
101003
|
-
const hasUpper = /[A-Z]/.test(token);
|
|
101004
|
-
const hasLower = /[a-z]/.test(token);
|
|
101005
101121
|
const hasUnderscore = token.includes("_");
|
|
101006
|
-
const
|
|
101007
|
-
if (
|
|
101122
|
+
const hasCaseTransition = /[a-z][A-Z]/.test(token) || /[A-Z]{2,}[a-z]/.test(token);
|
|
101123
|
+
if (hasCaseTransition || hasUnderscore) {
|
|
101008
101124
|
return `"${token}"`;
|
|
101009
101125
|
}
|
|
101010
101126
|
return token;
|
|
@@ -101119,7 +101235,7 @@ function buildSearchDelegateTask({ searchQuery, searchPath, exact, language, all
|
|
|
101119
101235
|
"Break down complex queries into multiple searches to cover all aspects.",
|
|
101120
101236
|
"",
|
|
101121
101237
|
"Available tools:",
|
|
101122
|
-
"- search: Find code matching keywords or patterns. Run multiple searches for different aspects
|
|
101238
|
+
"- 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.",
|
|
101123
101239
|
"- extract: Verify code snippets to ensure targets are actually relevant before including them.",
|
|
101124
101240
|
"- listFiles: Understand directory structure to find where relevant code might live.",
|
|
101125
101241
|
"",
|
|
@@ -101140,13 +101256,14 @@ function buildSearchDelegateTask({ searchQuery, searchPath, exact, language, all
|
|
|
101140
101256
|
"",
|
|
101141
101257
|
"Combining searches with OR:",
|
|
101142
101258
|
'- Multiple unquoted words use OR logic: rate limit matches files containing EITHER "rate" OR "limit".',
|
|
101143
|
-
`-
|
|
101259
|
+
`- IMPORTANT: Multiple quoted terms use AND logic by default: '"RateLimit" "middleware"' requires BOTH in the same file.`,
|
|
101260
|
+
`- To search for ANY of several quoted symbols, use the explicit OR operator: '"ForwardMessage" OR "SessionLimiter"'.`,
|
|
101144
101261
|
'- Without quotes, camelCase like limitDRL gets split into "limit" + "DRL" \u2014 not what you want for symbol lookup.',
|
|
101145
101262
|
"- Use OR to search for multiple related symbols in ONE search instead of separate searches.",
|
|
101146
101263
|
"- This is much faster than running separate searches sequentially.",
|
|
101147
|
-
`- Example: search '"ForwardMessage" "SessionLimiter"' finds files with either exact symbol in one call.`,
|
|
101148
|
-
`- Example: search '"limitDRL" "doRollingWindowWrite"' finds both rate limiting functions at once.`,
|
|
101149
|
-
|
|
101264
|
+
`- Example: search '"ForwardMessage" OR "SessionLimiter"' finds files with either exact symbol in one call.`,
|
|
101265
|
+
`- Example: search '"limitDRL" OR "doRollingWindowWrite"' finds both rate limiting functions at once.`,
|
|
101266
|
+
"- Use AND (or just put quoted terms together) when you need both terms in the same file.",
|
|
101150
101267
|
"",
|
|
101151
101268
|
"Parallel tool calls:",
|
|
101152
101269
|
"- When you need to search for INDEPENDENT concepts, call multiple search tools IN PARALLEL (same response).",
|
|
@@ -101160,10 +101277,10 @@ function buildSearchDelegateTask({ searchQuery, searchPath, exact, language, all
|
|
|
101160
101277
|
' Query: "Find the IP allowlist middleware"',
|
|
101161
101278
|
' \u2192 search "allowlist middleware" (one search, probe handles IP/ip/Ip variations)',
|
|
101162
101279
|
' Query: "Find ForwardMessage and SessionLimiter"',
|
|
101163
|
-
` \u2192 search '"ForwardMessage" "SessionLimiter"' (one OR search finds both exact symbols)`,
|
|
101280
|
+
` \u2192 search '"ForwardMessage" OR "SessionLimiter"' (one OR search finds both exact symbols)`,
|
|
101164
101281
|
' OR: search exact=true "ForwardMessage" + search exact=true "SessionLimiter" IN PARALLEL',
|
|
101165
101282
|
' Query: "Find limitDRL and limitRedis functions"',
|
|
101166
|
-
` \u2192 search '"limitDRL" "limitRedis"' (one OR search, quoted to prevent camelCase splitting)`,
|
|
101283
|
+
` \u2192 search '"limitDRL" OR "limitRedis"' (one OR search, quoted to prevent camelCase splitting)`,
|
|
101167
101284
|
' Query: "Find ThrottleRetryLimit usage"',
|
|
101168
101285
|
' \u2192 search exact=true "ThrottleRetryLimit" (one search, if no results the symbol does not exist \u2014 stop)',
|
|
101169
101286
|
' Query: "How does BM25 scoring work with SIMD optimization?"',
|
|
@@ -101171,7 +101288,7 @@ function buildSearchDelegateTask({ searchQuery, searchPath, exact, language, all
|
|
|
101171
101288
|
"",
|
|
101172
101289
|
"BAD search strategy (never do this):",
|
|
101173
101290
|
' \u2192 search "AllowedIPs" \u2192 search "allowedIps" \u2192 search "allowed_ips" (WRONG: case/style variations, probe handles them)',
|
|
101174
|
-
` \u2192 search "limitDRL" \u2192 search "LimitDRL" (WRONG: case variation \u2014 combine with OR: '"limitDRL" "limitRedis"')`,
|
|
101291
|
+
` \u2192 search "limitDRL" \u2192 search "LimitDRL" (WRONG: case variation \u2014 combine with OR: '"limitDRL" OR "limitRedis"')`,
|
|
101175
101292
|
' \u2192 search "throttle_retry_limit" after searching "ThrottleRetryLimit" (WRONG: snake_case variation, probe handles it)',
|
|
101176
101293
|
' \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)',
|
|
101177
101294
|
' \u2192 search "func (k *RateLimitAndQuotaCheck) handleRateLimitFailure" (WRONG: do not search full function signatures, just use exact=true "handleRateLimitFailure")',
|
|
@@ -101184,15 +101301,34 @@ function buildSearchDelegateTask({ searchQuery, searchPath, exact, language, all
|
|
|
101184
101301
|
'- To bypass stopword filtering: wrap terms in quotes ("return", "struct") or set exact=true. Both disable stemming and splitting too.',
|
|
101185
101302
|
'- camelCase terms are split: getUserData becomes "get", "user", "data" \u2014 so one search covers all naming styles.',
|
|
101186
101303
|
'- Do NOT search for full function signatures like "func (r *Type) Method(args)". Just search for the method name with exact=true.',
|
|
101304
|
+
'- Do NOT search for file names (e.g., "sliding_log.go"). Use listFiles to discover files by name.',
|
|
101305
|
+
"",
|
|
101306
|
+
"PAGINATION:",
|
|
101307
|
+
"- Search results are paginated (~20k tokens per page).",
|
|
101308
|
+
"- If your search returned relevant files, call the same query with nextPage=true to check for more.",
|
|
101309
|
+
'- Keep paginating while results stay relevant. Stop when results are off-topic or "All results retrieved".',
|
|
101310
|
+
"",
|
|
101311
|
+
"WHEN TO STOP:",
|
|
101312
|
+
"- After you have explored the main concept AND related subsystems.",
|
|
101313
|
+
"- Once you have 5-15 targets covering different aspects of the query.",
|
|
101314
|
+
'- If you get a "DUPLICATE SEARCH BLOCKED" message, move on.',
|
|
101187
101315
|
"",
|
|
101188
101316
|
"Strategy:",
|
|
101189
|
-
"1. Analyze the query
|
|
101190
|
-
|
|
101191
|
-
"
|
|
101317
|
+
"1. Analyze the query \u2014 identify key concepts, then brainstorm SYNONYMS and alternative terms for each.",
|
|
101318
|
+
' Code naming often differs from the concept: "authentication" \u2192 verify, credentials, login, auth;',
|
|
101319
|
+
' "rate limiting" \u2192 throttle, quota, limiter, bucket; "error handling" \u2192 catch, recover, panic.',
|
|
101320
|
+
" Think about what a developer would NAME the function/struct/variable, not just the concept.",
|
|
101321
|
+
"2. Run INDEPENDENT searches in PARALLEL \u2014 search for the main concept AND synonyms simultaneously.",
|
|
101322
|
+
" After each search, check if results are relevant. If yes, call nextPage=true for more results.",
|
|
101323
|
+
`3. Combine related symbols into OR searches: '"symbolA" OR "symbolB"' finds files with either.`,
|
|
101192
101324
|
"4. For known symbol names use exact=true. For concepts use default (exact=false).",
|
|
101193
|
-
"5.
|
|
101194
|
-
"
|
|
101195
|
-
"
|
|
101325
|
+
"5. After your first round of searches, READ the extracted code and look for connected code:",
|
|
101326
|
+
" - Function calls to other important functions \u2192 include those targets.",
|
|
101327
|
+
" - Type references and imports \u2192 include type definitions.",
|
|
101328
|
+
" - Registered handlers/middleware \u2192 include all registered items.",
|
|
101329
|
+
"6. If a search returns results, use extract to verify relevance. Run multiple extracts in parallel too.",
|
|
101330
|
+
"7. If a search returns NO results, the term does not exist. Do NOT retry with variations. Move on.",
|
|
101331
|
+
"8. Once you have enough targets (typically 5-15), output your final JSON answer immediately.",
|
|
101196
101332
|
"",
|
|
101197
101333
|
`Query: ${searchQuery}`,
|
|
101198
101334
|
`Search path(s): ${searchPath}`,
|
|
@@ -101201,7 +101337,9 @@ function buildSearchDelegateTask({ searchQuery, searchPath, exact, language, all
|
|
|
101201
101337
|
'Return ONLY valid JSON: {"targets": ["path/to/file.ext#Symbol", "path/to/file.ext:line", "path/to/file.ext:start-end"]}',
|
|
101202
101338
|
'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.',
|
|
101203
101339
|
"Prefer #Symbol when a function/class name is clear; otherwise use line numbers.",
|
|
101204
|
-
"Deduplicate targets. Do NOT explain or answer - ONLY return the JSON targets."
|
|
101340
|
+
"Deduplicate targets. Do NOT explain or answer - ONLY return the JSON targets.",
|
|
101341
|
+
"",
|
|
101342
|
+
"Remember: if your search returned relevant results, use nextPage=true to check for more before outputting."
|
|
101205
101343
|
].join("\n");
|
|
101206
101344
|
}
|
|
101207
101345
|
var import_ai5, import_fs11, CODE_SEARCH_SCHEMA, searchTool, queryTool, extractTool, delegateTool, analyzeAllTool;
|
|
@@ -101246,6 +101384,7 @@ var init_vercel = __esm({
|
|
|
101246
101384
|
return result;
|
|
101247
101385
|
};
|
|
101248
101386
|
const previousSearches = /* @__PURE__ */ new Set();
|
|
101387
|
+
let consecutiveDupBlocks = 0;
|
|
101249
101388
|
const paginationCounts = /* @__PURE__ */ new Map();
|
|
101250
101389
|
const MAX_PAGES_PER_QUERY = 3;
|
|
101251
101390
|
return (0, import_ai5.tool)({
|
|
@@ -101298,12 +101437,17 @@ var init_vercel = __esm({
|
|
|
101298
101437
|
const searchKey = `${searchQuery}::${exact || false}`;
|
|
101299
101438
|
if (!nextPage) {
|
|
101300
101439
|
if (previousSearches.has(searchKey)) {
|
|
101440
|
+
consecutiveDupBlocks++;
|
|
101301
101441
|
if (debug) {
|
|
101302
|
-
console.error(`[DEDUP] Blocked duplicate search: "${searchQuery}" (path: "${searchPath}")`);
|
|
101442
|
+
console.error(`[DEDUP] Blocked duplicate search (${consecutiveDupBlocks}x): "${searchQuery}" (path: "${searchPath}")`);
|
|
101303
101443
|
}
|
|
101304
|
-
|
|
101444
|
+
if (consecutiveDupBlocks >= 3) {
|
|
101445
|
+
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.";
|
|
101446
|
+
}
|
|
101447
|
+
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.";
|
|
101305
101448
|
}
|
|
101306
101449
|
previousSearches.add(searchKey);
|
|
101450
|
+
consecutiveDupBlocks = 0;
|
|
101307
101451
|
paginationCounts.set(searchKey, 0);
|
|
101308
101452
|
} else {
|
|
101309
101453
|
const pageCount = (paginationCounts.get(searchKey) || 0) + 1;
|
|
@@ -101324,7 +101468,11 @@ var init_vercel = __esm({
|
|
|
101324
101468
|
return result;
|
|
101325
101469
|
} catch (error40) {
|
|
101326
101470
|
console.error("Error executing search command:", error40);
|
|
101327
|
-
|
|
101471
|
+
const formatted = formatErrorForAI(error40);
|
|
101472
|
+
if (error40.category === "path_error" || error40.message?.includes("does not exist")) {
|
|
101473
|
+
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.";
|
|
101474
|
+
}
|
|
101475
|
+
return formatted;
|
|
101328
101476
|
}
|
|
101329
101477
|
}
|
|
101330
101478
|
try {
|
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-rc291",
|
|
4
4
|
"description": "Node.js wrapper for the probe code search tool",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"module": "src/index.js",
|
|
@@ -49,6 +49,7 @@
|
|
|
49
49
|
],
|
|
50
50
|
"scripts": {
|
|
51
51
|
"postinstall": "node scripts/postinstall.js",
|
|
52
|
+
"prepare": "npm run build:mcp",
|
|
52
53
|
"build:mcp": "node scripts/build-mcp.cjs",
|
|
53
54
|
"build:agent": "node scripts/build-agent.cjs",
|
|
54
55
|
"build:types": "echo 'TypeScript definitions already manually created'",
|