@probelabs/probe 0.6.0-rc101 → 0.6.0-rc103
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/build/agent/ProbeAgent.js +254 -4
- package/build/agent/index.js +257 -41
- package/build/mcp/index.js +24 -145
- package/build/mcp/index.ts +23 -162
- package/build/tools/system-message.js +7 -1
- package/cjs/agent/ProbeAgent.cjs +202 -11
- package/cjs/index.cjs +214 -23
- package/package.json +1 -1
- package/src/agent/ProbeAgent.js +254 -4
- package/src/agent/index.js +50 -18
- package/src/mcp/index.ts +23 -162
- package/src/tools/system-message.js +7 -1
package/cjs/agent/ProbeAgent.cjs
CHANGED
|
@@ -1840,7 +1840,7 @@ async function delegate({ task, timeout = 300, debug = false, currentIteration =
|
|
|
1840
1840
|
console.error(`[DELEGATE] Using binary at: ${binaryPath}`);
|
|
1841
1841
|
console.error(`[DELEGATE] Command args: ${args.join(" ")}`);
|
|
1842
1842
|
}
|
|
1843
|
-
return new Promise((
|
|
1843
|
+
return new Promise((resolve2, reject) => {
|
|
1844
1844
|
const delegationSpan = tracer ? tracer.createDelegationSpan(sessionId, task) : null;
|
|
1845
1845
|
const process2 = (0, import_child_process5.spawn)(binaryPath, args, {
|
|
1846
1846
|
stdio: ["pipe", "pipe", "pipe"],
|
|
@@ -1905,7 +1905,7 @@ async function delegate({ task, timeout = 300, debug = false, currentIteration =
|
|
|
1905
1905
|
delegationSpan.end();
|
|
1906
1906
|
}
|
|
1907
1907
|
}
|
|
1908
|
-
|
|
1908
|
+
resolve2(response);
|
|
1909
1909
|
} else {
|
|
1910
1910
|
const errorMessage = stderr.trim() || `Delegate process failed with exit code ${code}`;
|
|
1911
1911
|
if (debug) {
|
|
@@ -2268,13 +2268,19 @@ For GitHub-compatible mermaid diagrams, avoid single quotes and parentheses in n
|
|
|
2268
2268
|
|
|
2269
2269
|
**Rules:**
|
|
2270
2270
|
- NO single quotes in any node labels: 'text' \u2192 "text" or text
|
|
2271
|
-
- NO parentheses in square brackets: [Text (detail)] \u2192 [Text - detail]
|
|
2271
|
+
- NO parentheses in square brackets: [Text (detail)] \u2192 [Text - detail]
|
|
2272
2272
|
- NO complex expressions in diamonds: {a && b} \u2192 {condition}
|
|
2273
|
+
- NO HTML tags in node labels: [<pre>code</pre>] \u2192 ["code block"] or [Code Block]
|
|
2273
2274
|
- USE single quotes for styles and classes: classDef highlight fill:'#ff9999'
|
|
2275
|
+
- CRITICAL: When using quotes in node labels, place them INSIDE the brackets: ["quoted text"], NOT [quoted text"]
|
|
2274
2276
|
|
|
2275
2277
|
**Examples:**
|
|
2276
2278
|
- \u2705 [Load Config] ["Run command"] {Valid?}
|
|
2279
|
+
- \u2705 ["depends_on: [generate-items]"] (correct quote placement)
|
|
2280
|
+
- \u2705 ["Code Block"] (clean text instead of HTML)
|
|
2277
2281
|
- \u274C [Load (config)] [Run 'command'] {isValid('x')}
|
|
2282
|
+
- \u274C [depends_on: [generate-items"] (incorrect quote placement - quote ends inside bracket)
|
|
2283
|
+
- \u274C [<pre>depends_on: [generate-items]</pre>] (HTML tags in node labels)
|
|
2278
2284
|
|
|
2279
2285
|
**Diagram Type Selection:**
|
|
2280
2286
|
|
|
@@ -2776,7 +2782,7 @@ function createMockProvider() {
|
|
|
2776
2782
|
provider: "mock",
|
|
2777
2783
|
// Mock the doGenerate method used by Vercel AI SDK
|
|
2778
2784
|
doGenerate: async ({ messages, tools: tools2 }) => {
|
|
2779
|
-
await new Promise((
|
|
2785
|
+
await new Promise((resolve2) => setTimeout(resolve2, 10));
|
|
2780
2786
|
return {
|
|
2781
2787
|
text: "This is a mock response for testing",
|
|
2782
2788
|
toolCalls: [],
|
|
@@ -4602,7 +4608,7 @@ __export(ProbeAgent_exports, {
|
|
|
4602
4608
|
ProbeAgent: () => ProbeAgent
|
|
4603
4609
|
});
|
|
4604
4610
|
module.exports = __toCommonJS(ProbeAgent_exports);
|
|
4605
|
-
var import_anthropic, import_openai, import_google, import_ai2, import_crypto5, import_events2, MAX_TOOL_ITERATIONS, MAX_HISTORY_MESSAGES, ProbeAgent;
|
|
4611
|
+
var import_anthropic, import_openai, import_google, import_ai2, import_crypto5, import_events2, import_fs6, import_promises, import_path8, MAX_TOOL_ITERATIONS, MAX_HISTORY_MESSAGES, SUPPORTED_IMAGE_EXTENSIONS, MAX_IMAGE_FILE_SIZE, ProbeAgent;
|
|
4606
4612
|
var init_ProbeAgent = __esm({
|
|
4607
4613
|
"src/agent/ProbeAgent.js"() {
|
|
4608
4614
|
import_anthropic = require("@ai-sdk/anthropic");
|
|
@@ -4611,6 +4617,9 @@ var init_ProbeAgent = __esm({
|
|
|
4611
4617
|
import_ai2 = require("ai");
|
|
4612
4618
|
import_crypto5 = require("crypto");
|
|
4613
4619
|
import_events2 = require("events");
|
|
4620
|
+
import_fs6 = require("fs");
|
|
4621
|
+
import_promises = require("fs/promises");
|
|
4622
|
+
import_path8 = require("path");
|
|
4614
4623
|
init_tokenCounter();
|
|
4615
4624
|
init_tools2();
|
|
4616
4625
|
init_common();
|
|
@@ -4621,6 +4630,8 @@ var init_ProbeAgent = __esm({
|
|
|
4621
4630
|
init_mcp();
|
|
4622
4631
|
MAX_TOOL_ITERATIONS = parseInt(process.env.MAX_TOOL_ITERATIONS || "30", 10);
|
|
4623
4632
|
MAX_HISTORY_MESSAGES = 100;
|
|
4633
|
+
SUPPORTED_IMAGE_EXTENSIONS = ["png", "jpg", "jpeg", "webp", "gif", "bmp", "svg"];
|
|
4634
|
+
MAX_IMAGE_FILE_SIZE = 20 * 1024 * 1024;
|
|
4624
4635
|
ProbeAgent = class {
|
|
4625
4636
|
/**
|
|
4626
4637
|
* Create a new ProbeAgent instance
|
|
@@ -4664,6 +4675,8 @@ var init_ProbeAgent = __esm({
|
|
|
4664
4675
|
}
|
|
4665
4676
|
this.initializeTools();
|
|
4666
4677
|
this.history = [];
|
|
4678
|
+
this.pendingImages = /* @__PURE__ */ new Map();
|
|
4679
|
+
this.currentImages = [];
|
|
4667
4680
|
this.events = new import_events2.EventEmitter();
|
|
4668
4681
|
this.enableMcp = !!options.enableMcp || process.env.ENABLE_MCP === "1";
|
|
4669
4682
|
this.mcpConfigPath = options.mcpConfigPath || null;
|
|
@@ -4789,6 +4802,175 @@ var init_ProbeAgent = __esm({
|
|
|
4789
4802
|
console.log(`Using Google API with model: ${this.model}${apiUrl ? ` (URL: ${apiUrl})` : ""}`);
|
|
4790
4803
|
}
|
|
4791
4804
|
}
|
|
4805
|
+
/**
|
|
4806
|
+
* Process assistant response content and detect/load image references
|
|
4807
|
+
* @param {string} content - The assistant's response content
|
|
4808
|
+
* @returns {Promise<void>}
|
|
4809
|
+
*/
|
|
4810
|
+
async processImageReferences(content) {
|
|
4811
|
+
if (!content) return;
|
|
4812
|
+
const extensionsPattern = `(?:${SUPPORTED_IMAGE_EXTENSIONS.join("|")})`;
|
|
4813
|
+
const imagePatterns = [
|
|
4814
|
+
// Direct file path mentions: "./screenshot.png", "/path/to/image.jpg", etc.
|
|
4815
|
+
new RegExp(`(?:\\.?\\.\\/)?[^\\s"'<>\\[\\]]+\\.${extensionsPattern}(?!\\w)`, "gi"),
|
|
4816
|
+
// Contextual mentions: "look at image.png", "the file screenshot.jpg shows"
|
|
4817
|
+
new RegExp(`(?:image|file|screenshot|diagram|photo|picture|graphic)\\s*:?\\s*([^\\s"'<>\\[\\]]+\\.${extensionsPattern})(?!\\w)`, "gi"),
|
|
4818
|
+
// Tool result mentions: often contain file paths
|
|
4819
|
+
new RegExp(`(?:found|saved|created|generated).*?([^\\s"'<>\\[\\]]+\\.${extensionsPattern})(?!\\w)`, "gi")
|
|
4820
|
+
];
|
|
4821
|
+
const foundPaths = /* @__PURE__ */ new Set();
|
|
4822
|
+
for (const pattern of imagePatterns) {
|
|
4823
|
+
let match;
|
|
4824
|
+
while ((match = pattern.exec(content)) !== null) {
|
|
4825
|
+
const imagePath = match[1] || match[0];
|
|
4826
|
+
if (imagePath && imagePath.length > 0) {
|
|
4827
|
+
foundPaths.add(imagePath.trim());
|
|
4828
|
+
}
|
|
4829
|
+
}
|
|
4830
|
+
}
|
|
4831
|
+
if (foundPaths.size === 0) return;
|
|
4832
|
+
if (this.debug) {
|
|
4833
|
+
console.log(`[DEBUG] Found ${foundPaths.size} potential image references:`, Array.from(foundPaths));
|
|
4834
|
+
}
|
|
4835
|
+
for (const imagePath of foundPaths) {
|
|
4836
|
+
await this.loadImageIfValid(imagePath);
|
|
4837
|
+
}
|
|
4838
|
+
}
|
|
4839
|
+
/**
|
|
4840
|
+
* Load and cache an image if it's valid and accessible
|
|
4841
|
+
* @param {string} imagePath - Path to the image file
|
|
4842
|
+
* @returns {Promise<boolean>} - True if image was loaded successfully
|
|
4843
|
+
*/
|
|
4844
|
+
async loadImageIfValid(imagePath) {
|
|
4845
|
+
try {
|
|
4846
|
+
if (this.pendingImages.has(imagePath)) {
|
|
4847
|
+
if (this.debug) {
|
|
4848
|
+
console.log(`[DEBUG] Image already loaded: ${imagePath}`);
|
|
4849
|
+
}
|
|
4850
|
+
return true;
|
|
4851
|
+
}
|
|
4852
|
+
const allowedDirs = this.allowedFolders && this.allowedFolders.length > 0 ? this.allowedFolders : [process.cwd()];
|
|
4853
|
+
let absolutePath;
|
|
4854
|
+
let isPathAllowed = false;
|
|
4855
|
+
if ((0, import_path8.isAbsolute)(imagePath)) {
|
|
4856
|
+
absolutePath = imagePath;
|
|
4857
|
+
isPathAllowed = allowedDirs.some((dir) => absolutePath.startsWith((0, import_path8.resolve)(dir)));
|
|
4858
|
+
} else {
|
|
4859
|
+
for (const dir of allowedDirs) {
|
|
4860
|
+
const resolvedPath = (0, import_path8.resolve)(dir, imagePath);
|
|
4861
|
+
if (resolvedPath.startsWith((0, import_path8.resolve)(dir))) {
|
|
4862
|
+
absolutePath = resolvedPath;
|
|
4863
|
+
isPathAllowed = true;
|
|
4864
|
+
break;
|
|
4865
|
+
}
|
|
4866
|
+
}
|
|
4867
|
+
}
|
|
4868
|
+
if (!isPathAllowed) {
|
|
4869
|
+
if (this.debug) {
|
|
4870
|
+
console.log(`[DEBUG] Image path outside allowed directories: ${imagePath}`);
|
|
4871
|
+
}
|
|
4872
|
+
return false;
|
|
4873
|
+
}
|
|
4874
|
+
let fileStats;
|
|
4875
|
+
try {
|
|
4876
|
+
fileStats = await (0, import_promises.stat)(absolutePath);
|
|
4877
|
+
} catch (error) {
|
|
4878
|
+
if (this.debug) {
|
|
4879
|
+
console.log(`[DEBUG] Image file not found: ${absolutePath}`);
|
|
4880
|
+
}
|
|
4881
|
+
return false;
|
|
4882
|
+
}
|
|
4883
|
+
if (fileStats.size > MAX_IMAGE_FILE_SIZE) {
|
|
4884
|
+
if (this.debug) {
|
|
4885
|
+
console.log(`[DEBUG] Image file too large: ${absolutePath} (${fileStats.size} bytes, max: ${MAX_IMAGE_FILE_SIZE})`);
|
|
4886
|
+
}
|
|
4887
|
+
return false;
|
|
4888
|
+
}
|
|
4889
|
+
const extension = absolutePath.toLowerCase().split(".").pop();
|
|
4890
|
+
if (!SUPPORTED_IMAGE_EXTENSIONS.includes(extension)) {
|
|
4891
|
+
if (this.debug) {
|
|
4892
|
+
console.log(`[DEBUG] Unsupported image format: ${extension}`);
|
|
4893
|
+
}
|
|
4894
|
+
return false;
|
|
4895
|
+
}
|
|
4896
|
+
const mimeTypes = {
|
|
4897
|
+
"png": "image/png",
|
|
4898
|
+
"jpg": "image/jpeg",
|
|
4899
|
+
"jpeg": "image/jpeg",
|
|
4900
|
+
"webp": "image/webp",
|
|
4901
|
+
"gif": "image/gif",
|
|
4902
|
+
"bmp": "image/bmp",
|
|
4903
|
+
"svg": "image/svg+xml"
|
|
4904
|
+
};
|
|
4905
|
+
const mimeType = mimeTypes[extension];
|
|
4906
|
+
const fileBuffer = await (0, import_promises.readFile)(absolutePath);
|
|
4907
|
+
const base64Data = fileBuffer.toString("base64");
|
|
4908
|
+
const dataUrl = `data:${mimeType};base64,${base64Data}`;
|
|
4909
|
+
this.pendingImages.set(imagePath, dataUrl);
|
|
4910
|
+
if (this.debug) {
|
|
4911
|
+
console.log(`[DEBUG] Successfully loaded image: ${imagePath} (${fileBuffer.length} bytes)`);
|
|
4912
|
+
}
|
|
4913
|
+
return true;
|
|
4914
|
+
} catch (error) {
|
|
4915
|
+
if (this.debug) {
|
|
4916
|
+
console.log(`[DEBUG] Failed to load image ${imagePath}: ${error.message}`);
|
|
4917
|
+
}
|
|
4918
|
+
return false;
|
|
4919
|
+
}
|
|
4920
|
+
}
|
|
4921
|
+
/**
|
|
4922
|
+
* Get all currently loaded images as an array for AI model consumption
|
|
4923
|
+
* @returns {Array<string>} - Array of base64 data URLs
|
|
4924
|
+
*/
|
|
4925
|
+
getCurrentImages() {
|
|
4926
|
+
return Array.from(this.pendingImages.values());
|
|
4927
|
+
}
|
|
4928
|
+
/**
|
|
4929
|
+
* Clear loaded images (useful for new conversations)
|
|
4930
|
+
*/
|
|
4931
|
+
clearLoadedImages() {
|
|
4932
|
+
this.pendingImages.clear();
|
|
4933
|
+
this.currentImages = [];
|
|
4934
|
+
if (this.debug) {
|
|
4935
|
+
console.log("[DEBUG] Cleared all loaded images");
|
|
4936
|
+
}
|
|
4937
|
+
}
|
|
4938
|
+
/**
|
|
4939
|
+
* Prepare messages for AI consumption, adding images to the latest user message if available
|
|
4940
|
+
* @param {Array} messages - Current conversation messages
|
|
4941
|
+
* @returns {Array} - Messages formatted for AI SDK with potential image content
|
|
4942
|
+
*/
|
|
4943
|
+
prepareMessagesWithImages(messages) {
|
|
4944
|
+
const loadedImages = this.getCurrentImages();
|
|
4945
|
+
if (loadedImages.length === 0) {
|
|
4946
|
+
return messages;
|
|
4947
|
+
}
|
|
4948
|
+
const messagesWithImages = [...messages];
|
|
4949
|
+
const lastUserMessageIndex = messagesWithImages.map((m) => m.role).lastIndexOf("user");
|
|
4950
|
+
if (lastUserMessageIndex === -1) {
|
|
4951
|
+
if (this.debug) {
|
|
4952
|
+
console.log("[DEBUG] No user messages found to attach images to");
|
|
4953
|
+
}
|
|
4954
|
+
return messages;
|
|
4955
|
+
}
|
|
4956
|
+
const lastUserMessage = messagesWithImages[lastUserMessageIndex];
|
|
4957
|
+
if (typeof lastUserMessage.content === "string") {
|
|
4958
|
+
messagesWithImages[lastUserMessageIndex] = {
|
|
4959
|
+
...lastUserMessage,
|
|
4960
|
+
content: [
|
|
4961
|
+
{ type: "text", text: lastUserMessage.content },
|
|
4962
|
+
...loadedImages.map((imageData) => ({
|
|
4963
|
+
type: "image",
|
|
4964
|
+
image: imageData
|
|
4965
|
+
}))
|
|
4966
|
+
]
|
|
4967
|
+
};
|
|
4968
|
+
if (this.debug) {
|
|
4969
|
+
console.log(`[DEBUG] Added ${loadedImages.length} images to the latest user message`);
|
|
4970
|
+
}
|
|
4971
|
+
}
|
|
4972
|
+
return messagesWithImages;
|
|
4973
|
+
}
|
|
4792
4974
|
/**
|
|
4793
4975
|
* Initialize mock model for testing
|
|
4794
4976
|
*/
|
|
@@ -4888,7 +5070,7 @@ Examples:
|
|
|
4888
5070
|
</extract>
|
|
4889
5071
|
|
|
4890
5072
|
<attempt_completion>
|
|
4891
|
-
|
|
5073
|
+
The configuration is loaded from src/config.js lines 15-25 which contains the database settings.
|
|
4892
5074
|
</attempt_completion>
|
|
4893
5075
|
|
|
4894
5076
|
# Special Case: Quick Completion
|
|
@@ -4915,7 +5097,7 @@ I need to find code related to error handling in the search module. The most app
|
|
|
4915
5097
|
6. Wait for the tool execution result, which will be provided in the next message (within a <tool_result> block).
|
|
4916
5098
|
7. Analyze the tool result and decide the next step. If more tool calls are needed, repeat steps 2-6.
|
|
4917
5099
|
8. If the task is fully complete and all previous steps were successful, use the \`<attempt_completion>\` tool to provide the final answer. This is the ONLY way to finish the task.
|
|
4918
|
-
9. If you cannot proceed (e.g., missing information, invalid request), explain the issue clearly
|
|
5100
|
+
9. If you cannot proceed (e.g., missing information, invalid request), use \`<attempt_completion>\` to explain the issue clearly with an appropriate message directly inside the tags.
|
|
4919
5101
|
10. If your previous response was already correct and complete, you may use \`<attempt_complete>\` as a shorthand.
|
|
4920
5102
|
|
|
4921
5103
|
Available Tools:
|
|
@@ -5170,9 +5352,10 @@ You are working with a repository located at: ${searchDirectory}
|
|
|
5170
5352
|
let assistantResponseContent = "";
|
|
5171
5353
|
try {
|
|
5172
5354
|
const executeAIRequest = async () => {
|
|
5355
|
+
const messagesForAI = this.prepareMessagesWithImages(currentMessages);
|
|
5173
5356
|
const result = await (0, import_ai2.streamText)({
|
|
5174
5357
|
model: this.provider(this.model),
|
|
5175
|
-
messages:
|
|
5358
|
+
messages: messagesForAI,
|
|
5176
5359
|
maxTokens: maxResponseTokens,
|
|
5177
5360
|
temperature: 0.3
|
|
5178
5361
|
});
|
|
@@ -5206,6 +5389,9 @@ You are working with a repository located at: ${searchDirectory}
|
|
|
5206
5389
|
const assistantPreview = createMessagePreview(assistantResponseContent);
|
|
5207
5390
|
console.log(`[DEBUG] Assistant response (${assistantResponseContent.length} chars): ${assistantPreview}`);
|
|
5208
5391
|
}
|
|
5392
|
+
if (assistantResponseContent) {
|
|
5393
|
+
await this.processImageReferences(assistantResponseContent);
|
|
5394
|
+
}
|
|
5209
5395
|
const validTools = [
|
|
5210
5396
|
"search",
|
|
5211
5397
|
"query",
|
|
@@ -5330,12 +5516,17 @@ ${toolResultContent}
|
|
|
5330
5516
|
throw toolError;
|
|
5331
5517
|
}
|
|
5332
5518
|
currentMessages.push({ role: "assistant", content: assistantResponseContent });
|
|
5519
|
+
const toolResultContent = typeof toolResult === "string" ? toolResult : JSON.stringify(toolResult, null, 2);
|
|
5520
|
+
const toolResultMessage = `<tool_result>
|
|
5521
|
+
${toolResultContent}
|
|
5522
|
+
</tool_result>`;
|
|
5333
5523
|
currentMessages.push({
|
|
5334
5524
|
role: "user",
|
|
5335
|
-
content:
|
|
5336
|
-
${typeof toolResult === "string" ? toolResult : JSON.stringify(toolResult, null, 2)}
|
|
5337
|
-
</tool_result>`
|
|
5525
|
+
content: toolResultMessage
|
|
5338
5526
|
});
|
|
5527
|
+
if (toolResultContent) {
|
|
5528
|
+
await this.processImageReferences(toolResultContent);
|
|
5529
|
+
}
|
|
5339
5530
|
if (this.debug) {
|
|
5340
5531
|
console.log(`[DEBUG] Tool ${toolName} executed successfully. Result length: ${typeof toolResult === "string" ? toolResult.length : JSON.stringify(toolResult).length}`);
|
|
5341
5532
|
}
|