@probelabs/probe 0.6.0-rc119 → 0.6.0-rc121
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 +85 -10
- package/build/agent/acp/server.js +11 -6
- package/build/agent/index.js +83 -19
- package/build/agent/mcp/client.js +17 -3
- package/build/agent/schemaUtils.js +7 -4
- package/cjs/agent/ProbeAgent.cjs +73 -15
- package/cjs/index.cjs +73 -15
- package/package.json +1 -1
- package/src/agent/ProbeAgent.js +85 -10
- package/src/agent/acp/server.js +11 -6
- package/src/agent/index.js +5 -1
- package/src/agent/mcp/client.js +17 -3
- package/src/agent/schemaUtils.js +7 -4
|
@@ -8,7 +8,7 @@ import { randomUUID } from 'crypto';
|
|
|
8
8
|
import { EventEmitter } from 'events';
|
|
9
9
|
import { existsSync } from 'fs';
|
|
10
10
|
import { readFile, stat } from 'fs/promises';
|
|
11
|
-
import { resolve, isAbsolute } from 'path';
|
|
11
|
+
import { resolve, isAbsolute, dirname } from 'path';
|
|
12
12
|
import { TokenCounter } from './tokenCounter.js';
|
|
13
13
|
import {
|
|
14
14
|
createTools,
|
|
@@ -145,12 +145,48 @@ export class ProbeAgent {
|
|
|
145
145
|
// Initialize the AI model
|
|
146
146
|
this.initializeModel();
|
|
147
147
|
|
|
148
|
+
// Note: MCP initialization is now done in initialize() method
|
|
149
|
+
// Constructor must remain synchronous for backward compatibility
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Initialize the agent asynchronously (must be called after constructor)
|
|
154
|
+
* This method initializes MCP and merges MCP tools into the tool list
|
|
155
|
+
*/
|
|
156
|
+
async initialize() {
|
|
148
157
|
// Initialize MCP if enabled
|
|
149
158
|
if (this.enableMcp) {
|
|
150
|
-
|
|
159
|
+
try {
|
|
160
|
+
await this.initializeMCP();
|
|
161
|
+
|
|
162
|
+
// Merge MCP tools into toolImplementations for unified access
|
|
163
|
+
if (this.mcpBridge) {
|
|
164
|
+
const mcpTools = this.mcpBridge.mcpTools || {};
|
|
165
|
+
for (const [toolName, toolImpl] of Object.entries(mcpTools)) {
|
|
166
|
+
this.toolImplementations[toolName] = toolImpl;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Log all available tools after MCP initialization
|
|
171
|
+
if (this.debug) {
|
|
172
|
+
const allToolNames = Object.keys(this.toolImplementations);
|
|
173
|
+
const nativeToolCount = allToolNames.filter(name => !this.mcpBridge?.mcpTools?.[name]).length;
|
|
174
|
+
const mcpToolCount = allToolNames.length - nativeToolCount;
|
|
175
|
+
|
|
176
|
+
console.error('\n[DEBUG] ========================================');
|
|
177
|
+
console.error('[DEBUG] All Tools Initialized');
|
|
178
|
+
console.error(`[DEBUG] Native tools: ${nativeToolCount}, MCP tools: ${mcpToolCount}`);
|
|
179
|
+
console.error('[DEBUG] Available tools:');
|
|
180
|
+
for (const toolName of allToolNames) {
|
|
181
|
+
const isMCP = this.mcpBridge?.mcpTools?.[toolName] ? ' (MCP)' : '';
|
|
182
|
+
console.error(`[DEBUG] - ${toolName}${isMCP}`);
|
|
183
|
+
}
|
|
184
|
+
console.error('[DEBUG] ========================================\n');
|
|
185
|
+
}
|
|
186
|
+
} catch (error) {
|
|
151
187
|
console.error('[MCP] Failed to initialize MCP:', error);
|
|
152
188
|
this.mcpBridge = null;
|
|
153
|
-
}
|
|
189
|
+
}
|
|
154
190
|
}
|
|
155
191
|
}
|
|
156
192
|
|
|
@@ -442,23 +478,62 @@ export class ProbeAgent {
|
|
|
442
478
|
}
|
|
443
479
|
|
|
444
480
|
/**
|
|
445
|
-
* Extract directory paths from listFiles tool
|
|
481
|
+
* Extract directory paths from tool output (both listFiles and extract tool)
|
|
446
482
|
* @param {string} content - Tool output content
|
|
447
483
|
* @returns {string[]} - Array of directory paths
|
|
448
484
|
*/
|
|
449
485
|
extractListFilesDirectories(content) {
|
|
450
486
|
const directories = [];
|
|
451
487
|
|
|
452
|
-
// Pattern
|
|
453
|
-
|
|
488
|
+
// Pattern 1: Extract directory from extract tool "File:" header
|
|
489
|
+
// Format: "File: /path/to/file.md" or "File: ./relative/path/file.md"
|
|
490
|
+
const fileHeaderPattern = /^File:\s+(.+)$/gm;
|
|
454
491
|
|
|
455
492
|
let match;
|
|
493
|
+
while ((match = fileHeaderPattern.exec(content)) !== null) {
|
|
494
|
+
const filePath = match[1].trim();
|
|
495
|
+
// Get directory from file path
|
|
496
|
+
const dir = dirname(filePath);
|
|
497
|
+
if (dir && dir !== '.') {
|
|
498
|
+
directories.push(dir);
|
|
499
|
+
if (this.debug) {
|
|
500
|
+
console.log(`[DEBUG] Extracted directory context from File header: ${dir}`);
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
// Pattern 2: Extract directory from listFiles output format: "/path/to/directory:"
|
|
506
|
+
// Matches absolute paths (/path/to/dir:) or current directory markers (.:) or Windows paths (C:\path:) at start of line
|
|
507
|
+
// Very strict to avoid matching random text like ".Something:" or "./Some text:"
|
|
508
|
+
const dirPattern = /^(\/[^\n:]+|[A-Z]:\\[^\n:]+|\.\.?(?:\/[^\n:]+)?):\s*$/gm;
|
|
509
|
+
|
|
456
510
|
while ((match = dirPattern.exec(content)) !== null) {
|
|
457
511
|
const dirPath = match[1].trim();
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
512
|
+
|
|
513
|
+
// Strict validation: must look like an actual filesystem path
|
|
514
|
+
// Reject if contains spaces or other characters that wouldn't be in listFiles output
|
|
515
|
+
const hasInvalidChars = /\s/.test(dirPath); // Contains whitespace
|
|
516
|
+
|
|
517
|
+
// Validate this looks like an actual path, not random text
|
|
518
|
+
// Must be either: absolute path (Unix or Windows), or ./ or ../ followed by valid path chars
|
|
519
|
+
const isValidPath = (
|
|
520
|
+
!hasInvalidChars && (
|
|
521
|
+
dirPath.startsWith('/') || // Unix absolute path
|
|
522
|
+
/^[A-Z]:\\/.test(dirPath) || // Windows absolute path (C:\)
|
|
523
|
+
dirPath === '.' || // Current directory
|
|
524
|
+
dirPath === '..' || // Parent directory
|
|
525
|
+
(dirPath.startsWith('./') && dirPath.length > 2 && !dirPath.includes(' ')) || // ./something (no spaces)
|
|
526
|
+
(dirPath.startsWith('../') && dirPath.length > 3 && !dirPath.includes(' ')) // ../something (no spaces)
|
|
527
|
+
)
|
|
528
|
+
);
|
|
529
|
+
|
|
530
|
+
if (isValidPath) {
|
|
531
|
+
// Avoid duplicates
|
|
532
|
+
if (!directories.includes(dirPath)) {
|
|
533
|
+
directories.push(dirPath);
|
|
534
|
+
if (this.debug) {
|
|
535
|
+
console.log(`[DEBUG] Extracted directory context from listFiles: ${dirPath}`);
|
|
536
|
+
}
|
|
462
537
|
}
|
|
463
538
|
}
|
|
464
539
|
}
|
|
@@ -31,12 +31,14 @@ class ACPSession {
|
|
|
31
31
|
/**
|
|
32
32
|
* Get or create ProbeAgent for this session
|
|
33
33
|
*/
|
|
34
|
-
getAgent(config = {}) {
|
|
34
|
+
async getAgent(config = {}) {
|
|
35
35
|
if (!this.agent) {
|
|
36
36
|
this.agent = new ProbeAgent({
|
|
37
37
|
sessionId: this.id,
|
|
38
38
|
...config
|
|
39
39
|
});
|
|
40
|
+
// Initialize MCP if enabled
|
|
41
|
+
await this.agent.initialize();
|
|
40
42
|
}
|
|
41
43
|
return this.agent;
|
|
42
44
|
}
|
|
@@ -320,20 +322,23 @@ export class ACPServer {
|
|
|
320
322
|
}
|
|
321
323
|
|
|
322
324
|
session.touch();
|
|
323
|
-
|
|
325
|
+
|
|
324
326
|
// Get or create ProbeAgent for this session
|
|
325
|
-
const agent = session.getAgent({
|
|
327
|
+
const agent = await session.getAgent({
|
|
326
328
|
path: this.options.path,
|
|
327
329
|
provider: this.options.provider,
|
|
328
330
|
model: this.options.model,
|
|
329
331
|
allowEdit: this.options.allowEdit,
|
|
330
|
-
debug: this.options.debug
|
|
332
|
+
debug: this.options.debug,
|
|
333
|
+
enableMcp: this.options.enableMcp,
|
|
334
|
+
mcpConfig: this.options.mcpConfig,
|
|
335
|
+
mcpConfigPath: this.options.mcpConfigPath
|
|
331
336
|
});
|
|
332
|
-
|
|
337
|
+
|
|
333
338
|
if (this.options.debug) {
|
|
334
339
|
console.error(`[ACP] Processing prompt for session ${params.sessionId}:`, params.message.substring(0, 100));
|
|
335
340
|
}
|
|
336
|
-
|
|
341
|
+
|
|
337
342
|
try {
|
|
338
343
|
// Process the message with the ProbeAgent
|
|
339
344
|
const response = await agent.answer(params.message);
|
package/build/agent/index.js
CHANGED
|
@@ -60866,13 +60866,14 @@ async function validateMermaidDiagram(diagram) {
|
|
|
60866
60866
|
};
|
|
60867
60867
|
}
|
|
60868
60868
|
const result = validate(diagram);
|
|
60869
|
-
|
|
60869
|
+
const actualErrors = (result.errors || []).filter((err) => err.severity === "error");
|
|
60870
|
+
if (actualErrors.length === 0) {
|
|
60870
60871
|
return {
|
|
60871
60872
|
isValid: true,
|
|
60872
60873
|
diagramType: result.type || "unknown"
|
|
60873
60874
|
};
|
|
60874
60875
|
} else {
|
|
60875
|
-
const errorMessages =
|
|
60876
|
+
const errorMessages = actualErrors.map((err) => {
|
|
60876
60877
|
const location = err.line ? `line ${err.line}${err.column ? `:${err.column}` : ""}` : "";
|
|
60877
60878
|
return location ? `${location} - ${err.message}` : err.message;
|
|
60878
60879
|
});
|
|
@@ -60881,8 +60882,8 @@ async function validateMermaidDiagram(diagram) {
|
|
|
60881
60882
|
diagramType: result.type || "unknown",
|
|
60882
60883
|
error: errorMessages[0] || "Validation failed",
|
|
60883
60884
|
detailedError: errorMessages.join("\n"),
|
|
60884
|
-
errors:
|
|
60885
|
-
// Include
|
|
60885
|
+
errors: actualErrors
|
|
60886
|
+
// Include only actual errors for AI fixing
|
|
60886
60887
|
};
|
|
60887
60888
|
}
|
|
60888
60889
|
} catch (error2) {
|
|
@@ -61770,10 +61771,19 @@ var init_client2 = __esm({
|
|
|
61770
61771
|
if (this.debug) {
|
|
61771
61772
|
console.error(`[MCP] Calling ${toolName} with args:`, args);
|
|
61772
61773
|
}
|
|
61773
|
-
const
|
|
61774
|
-
|
|
61775
|
-
|
|
61774
|
+
const timeout = this.config?.settings?.timeout || 3e4;
|
|
61775
|
+
const timeoutPromise = new Promise((_2, reject2) => {
|
|
61776
|
+
setTimeout(() => {
|
|
61777
|
+
reject2(new Error(`MCP tool call timeout after ${timeout}ms`));
|
|
61778
|
+
}, timeout);
|
|
61776
61779
|
});
|
|
61780
|
+
const result = await Promise.race([
|
|
61781
|
+
clientInfo.client.callTool({
|
|
61782
|
+
name: tool3.originalName,
|
|
61783
|
+
arguments: args
|
|
61784
|
+
}),
|
|
61785
|
+
timeoutPromise
|
|
61786
|
+
]);
|
|
61777
61787
|
return result;
|
|
61778
61788
|
} catch (error2) {
|
|
61779
61789
|
console.error(`[MCP] Error calling tool ${toolName}:`, error2);
|
|
@@ -62106,7 +62116,7 @@ import { randomUUID as randomUUID4 } from "crypto";
|
|
|
62106
62116
|
import { EventEmitter as EventEmitter2 } from "events";
|
|
62107
62117
|
import { existsSync as existsSync4 } from "fs";
|
|
62108
62118
|
import { readFile, stat } from "fs/promises";
|
|
62109
|
-
import { resolve as resolve3, isAbsolute } from "path";
|
|
62119
|
+
import { resolve as resolve3, isAbsolute, dirname as dirname3 } from "path";
|
|
62110
62120
|
var MAX_TOOL_ITERATIONS, MAX_HISTORY_MESSAGES, SUPPORTED_IMAGE_EXTENSIONS, MAX_IMAGE_FILE_SIZE, ProbeAgent;
|
|
62111
62121
|
var init_ProbeAgent = __esm({
|
|
62112
62122
|
"src/agent/ProbeAgent.js"() {
|
|
@@ -62187,11 +62197,39 @@ var init_ProbeAgent = __esm({
|
|
|
62187
62197
|
this.mcpServers = options.mcpServers || null;
|
|
62188
62198
|
this.mcpBridge = null;
|
|
62189
62199
|
this.initializeModel();
|
|
62200
|
+
}
|
|
62201
|
+
/**
|
|
62202
|
+
* Initialize the agent asynchronously (must be called after constructor)
|
|
62203
|
+
* This method initializes MCP and merges MCP tools into the tool list
|
|
62204
|
+
*/
|
|
62205
|
+
async initialize() {
|
|
62190
62206
|
if (this.enableMcp) {
|
|
62191
|
-
|
|
62207
|
+
try {
|
|
62208
|
+
await this.initializeMCP();
|
|
62209
|
+
if (this.mcpBridge) {
|
|
62210
|
+
const mcpTools = this.mcpBridge.mcpTools || {};
|
|
62211
|
+
for (const [toolName, toolImpl] of Object.entries(mcpTools)) {
|
|
62212
|
+
this.toolImplementations[toolName] = toolImpl;
|
|
62213
|
+
}
|
|
62214
|
+
}
|
|
62215
|
+
if (this.debug) {
|
|
62216
|
+
const allToolNames = Object.keys(this.toolImplementations);
|
|
62217
|
+
const nativeToolCount = allToolNames.filter((name14) => !this.mcpBridge?.mcpTools?.[name14]).length;
|
|
62218
|
+
const mcpToolCount = allToolNames.length - nativeToolCount;
|
|
62219
|
+
console.error("\n[DEBUG] ========================================");
|
|
62220
|
+
console.error("[DEBUG] All Tools Initialized");
|
|
62221
|
+
console.error(`[DEBUG] Native tools: ${nativeToolCount}, MCP tools: ${mcpToolCount}`);
|
|
62222
|
+
console.error("[DEBUG] Available tools:");
|
|
62223
|
+
for (const toolName of allToolNames) {
|
|
62224
|
+
const isMCP = this.mcpBridge?.mcpTools?.[toolName] ? " (MCP)" : "";
|
|
62225
|
+
console.error(`[DEBUG] - ${toolName}${isMCP}`);
|
|
62226
|
+
}
|
|
62227
|
+
console.error("[DEBUG] ========================================\n");
|
|
62228
|
+
}
|
|
62229
|
+
} catch (error2) {
|
|
62192
62230
|
console.error("[MCP] Failed to initialize MCP:", error2);
|
|
62193
62231
|
this.mcpBridge = null;
|
|
62194
|
-
}
|
|
62232
|
+
}
|
|
62195
62233
|
}
|
|
62196
62234
|
}
|
|
62197
62235
|
/**
|
|
@@ -62418,20 +62456,40 @@ var init_ProbeAgent = __esm({
|
|
|
62418
62456
|
}
|
|
62419
62457
|
}
|
|
62420
62458
|
/**
|
|
62421
|
-
* Extract directory paths from listFiles tool
|
|
62459
|
+
* Extract directory paths from tool output (both listFiles and extract tool)
|
|
62422
62460
|
* @param {string} content - Tool output content
|
|
62423
62461
|
* @returns {string[]} - Array of directory paths
|
|
62424
62462
|
*/
|
|
62425
62463
|
extractListFilesDirectories(content) {
|
|
62426
62464
|
const directories = [];
|
|
62427
|
-
const
|
|
62465
|
+
const fileHeaderPattern = /^File:\s+(.+)$/gm;
|
|
62428
62466
|
let match;
|
|
62467
|
+
while ((match = fileHeaderPattern.exec(content)) !== null) {
|
|
62468
|
+
const filePath = match[1].trim();
|
|
62469
|
+
const dir = dirname3(filePath);
|
|
62470
|
+
if (dir && dir !== ".") {
|
|
62471
|
+
directories.push(dir);
|
|
62472
|
+
if (this.debug) {
|
|
62473
|
+
console.log(`[DEBUG] Extracted directory context from File header: ${dir}`);
|
|
62474
|
+
}
|
|
62475
|
+
}
|
|
62476
|
+
}
|
|
62477
|
+
const dirPattern = /^(\/[^\n:]+|[A-Z]:\\[^\n:]+|\.\.?(?:\/[^\n:]+)?):\s*$/gm;
|
|
62429
62478
|
while ((match = dirPattern.exec(content)) !== null) {
|
|
62430
62479
|
const dirPath = match[1].trim();
|
|
62431
|
-
|
|
62432
|
-
|
|
62433
|
-
|
|
62434
|
-
|
|
62480
|
+
const hasInvalidChars = /\s/.test(dirPath);
|
|
62481
|
+
const isValidPath = !hasInvalidChars && (dirPath.startsWith("/") || // Unix absolute path
|
|
62482
|
+
/^[A-Z]:\\/.test(dirPath) || // Windows absolute path (C:\)
|
|
62483
|
+
dirPath === "." || // Current directory
|
|
62484
|
+
dirPath === ".." || // Parent directory
|
|
62485
|
+
dirPath.startsWith("./") && dirPath.length > 2 && !dirPath.includes(" ") || // ./something (no spaces)
|
|
62486
|
+
dirPath.startsWith("../") && dirPath.length > 3 && !dirPath.includes(" "));
|
|
62487
|
+
if (isValidPath) {
|
|
62488
|
+
if (!directories.includes(dirPath)) {
|
|
62489
|
+
directories.push(dirPath);
|
|
62490
|
+
if (this.debug) {
|
|
62491
|
+
console.log(`[DEBUG] Extracted directory context from listFiles: ${dirPath}`);
|
|
62492
|
+
}
|
|
62435
62493
|
}
|
|
62436
62494
|
}
|
|
62437
62495
|
}
|
|
@@ -64033,12 +64091,13 @@ var ACPSession = class {
|
|
|
64033
64091
|
/**
|
|
64034
64092
|
* Get or create ProbeAgent for this session
|
|
64035
64093
|
*/
|
|
64036
|
-
getAgent(config = {}) {
|
|
64094
|
+
async getAgent(config = {}) {
|
|
64037
64095
|
if (!this.agent) {
|
|
64038
64096
|
this.agent = new ProbeAgent({
|
|
64039
64097
|
sessionId: this.id,
|
|
64040
64098
|
...config
|
|
64041
64099
|
});
|
|
64100
|
+
await this.agent.initialize();
|
|
64042
64101
|
}
|
|
64043
64102
|
return this.agent;
|
|
64044
64103
|
}
|
|
@@ -64266,12 +64325,15 @@ var ACPServer = class {
|
|
|
64266
64325
|
throw new Error(`Session not found: ${params.sessionId}`);
|
|
64267
64326
|
}
|
|
64268
64327
|
session.touch();
|
|
64269
|
-
const agent = session.getAgent({
|
|
64328
|
+
const agent = await session.getAgent({
|
|
64270
64329
|
path: this.options.path,
|
|
64271
64330
|
provider: this.options.provider,
|
|
64272
64331
|
model: this.options.model,
|
|
64273
64332
|
allowEdit: this.options.allowEdit,
|
|
64274
|
-
debug: this.options.debug
|
|
64333
|
+
debug: this.options.debug,
|
|
64334
|
+
enableMcp: this.options.enableMcp,
|
|
64335
|
+
mcpConfig: this.options.mcpConfig,
|
|
64336
|
+
mcpConfigPath: this.options.mcpConfigPath
|
|
64275
64337
|
});
|
|
64276
64338
|
if (this.options.debug) {
|
|
64277
64339
|
console.error(`[ACP] Processing prompt for session ${params.sessionId}:`, params.message.substring(0, 100));
|
|
@@ -64716,6 +64778,7 @@ var ProbeAgentMcpServer = class {
|
|
|
64716
64778
|
disableMermaidValidation: !!args.no_mermaid_validation
|
|
64717
64779
|
};
|
|
64718
64780
|
this.agent = new ProbeAgent(agentConfig2);
|
|
64781
|
+
await this.agent.initialize();
|
|
64719
64782
|
}
|
|
64720
64783
|
const agent = this.agent;
|
|
64721
64784
|
let result = await agent.answer(query2, [], { schema });
|
|
@@ -64952,6 +65015,7 @@ async function main() {
|
|
|
64952
65015
|
bashConfig
|
|
64953
65016
|
};
|
|
64954
65017
|
const agent = new ProbeAgent(agentConfig2);
|
|
65018
|
+
await agent.initialize();
|
|
64955
65019
|
let result;
|
|
64956
65020
|
if (appTracer) {
|
|
64957
65021
|
const sessionSpan = appTracer.createSessionSpan({
|
|
@@ -242,11 +242,25 @@ export class MCPClientManager {
|
|
|
242
242
|
console.error(`[MCP] Calling ${toolName} with args:`, args);
|
|
243
243
|
}
|
|
244
244
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
245
|
+
// Get timeout from config (default 30 seconds)
|
|
246
|
+
const timeout = this.config?.settings?.timeout || 30000;
|
|
247
|
+
|
|
248
|
+
// Create a timeout promise
|
|
249
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
250
|
+
setTimeout(() => {
|
|
251
|
+
reject(new Error(`MCP tool call timeout after ${timeout}ms`));
|
|
252
|
+
}, timeout);
|
|
248
253
|
});
|
|
249
254
|
|
|
255
|
+
// Race between the actual call and timeout
|
|
256
|
+
const result = await Promise.race([
|
|
257
|
+
clientInfo.client.callTool({
|
|
258
|
+
name: tool.originalName,
|
|
259
|
+
arguments: args
|
|
260
|
+
}),
|
|
261
|
+
timeoutPromise
|
|
262
|
+
]);
|
|
263
|
+
|
|
250
264
|
return result;
|
|
251
265
|
} catch (error) {
|
|
252
266
|
console.error(`[MCP] Error calling tool ${toolName}:`, error);
|
|
@@ -562,15 +562,18 @@ export async function validateMermaidDiagram(diagram) {
|
|
|
562
562
|
const result = validate(diagram);
|
|
563
563
|
|
|
564
564
|
// Maid returns { type: string, errors: array }
|
|
565
|
-
//
|
|
566
|
-
|
|
565
|
+
// Only count actual errors (severity: 'error'), not warnings
|
|
566
|
+
const actualErrors = (result.errors || []).filter(err => err.severity === 'error');
|
|
567
|
+
|
|
568
|
+
// Valid if no actual errors (warnings are OK)
|
|
569
|
+
if (actualErrors.length === 0) {
|
|
567
570
|
return {
|
|
568
571
|
isValid: true,
|
|
569
572
|
diagramType: result.type || 'unknown'
|
|
570
573
|
};
|
|
571
574
|
} else {
|
|
572
575
|
// Format maid errors into a readable error message
|
|
573
|
-
const errorMessages =
|
|
576
|
+
const errorMessages = actualErrors.map(err => {
|
|
574
577
|
const location = err.line ? `line ${err.line}${err.column ? `:${err.column}` : ''}` : '';
|
|
575
578
|
return location ? `${location} - ${err.message}` : err.message;
|
|
576
579
|
});
|
|
@@ -580,7 +583,7 @@ export async function validateMermaidDiagram(diagram) {
|
|
|
580
583
|
diagramType: result.type || 'unknown',
|
|
581
584
|
error: errorMessages[0] || 'Validation failed',
|
|
582
585
|
detailedError: errorMessages.join('\n'),
|
|
583
|
-
errors:
|
|
586
|
+
errors: actualErrors // Include only actual errors for AI fixing
|
|
584
587
|
};
|
|
585
588
|
}
|
|
586
589
|
|
package/cjs/agent/ProbeAgent.cjs
CHANGED
|
@@ -60588,13 +60588,14 @@ async function validateMermaidDiagram(diagram) {
|
|
|
60588
60588
|
};
|
|
60589
60589
|
}
|
|
60590
60590
|
const result = validate(diagram);
|
|
60591
|
-
|
|
60591
|
+
const actualErrors = (result.errors || []).filter((err) => err.severity === "error");
|
|
60592
|
+
if (actualErrors.length === 0) {
|
|
60592
60593
|
return {
|
|
60593
60594
|
isValid: true,
|
|
60594
60595
|
diagramType: result.type || "unknown"
|
|
60595
60596
|
};
|
|
60596
60597
|
} else {
|
|
60597
|
-
const errorMessages =
|
|
60598
|
+
const errorMessages = actualErrors.map((err) => {
|
|
60598
60599
|
const location = err.line ? `line ${err.line}${err.column ? `:${err.column}` : ""}` : "";
|
|
60599
60600
|
return location ? `${location} - ${err.message}` : err.message;
|
|
60600
60601
|
});
|
|
@@ -60603,8 +60604,8 @@ async function validateMermaidDiagram(diagram) {
|
|
|
60603
60604
|
diagramType: result.type || "unknown",
|
|
60604
60605
|
error: errorMessages[0] || "Validation failed",
|
|
60605
60606
|
detailedError: errorMessages.join("\n"),
|
|
60606
|
-
errors:
|
|
60607
|
-
// Include
|
|
60607
|
+
errors: actualErrors
|
|
60608
|
+
// Include only actual errors for AI fixing
|
|
60608
60609
|
};
|
|
60609
60610
|
}
|
|
60610
60611
|
} catch (error2) {
|
|
@@ -61492,10 +61493,19 @@ var init_client2 = __esm({
|
|
|
61492
61493
|
if (this.debug) {
|
|
61493
61494
|
console.error(`[MCP] Calling ${toolName} with args:`, args);
|
|
61494
61495
|
}
|
|
61495
|
-
const
|
|
61496
|
-
|
|
61497
|
-
|
|
61496
|
+
const timeout = this.config?.settings?.timeout || 3e4;
|
|
61497
|
+
const timeoutPromise = new Promise((_2, reject2) => {
|
|
61498
|
+
setTimeout(() => {
|
|
61499
|
+
reject2(new Error(`MCP tool call timeout after ${timeout}ms`));
|
|
61500
|
+
}, timeout);
|
|
61498
61501
|
});
|
|
61502
|
+
const result = await Promise.race([
|
|
61503
|
+
clientInfo.client.callTool({
|
|
61504
|
+
name: tool3.originalName,
|
|
61505
|
+
arguments: args
|
|
61506
|
+
}),
|
|
61507
|
+
timeoutPromise
|
|
61508
|
+
]);
|
|
61499
61509
|
return result;
|
|
61500
61510
|
} catch (error2) {
|
|
61501
61511
|
console.error(`[MCP] Error calling tool ${toolName}:`, error2);
|
|
@@ -61909,11 +61919,39 @@ var init_ProbeAgent = __esm({
|
|
|
61909
61919
|
this.mcpServers = options.mcpServers || null;
|
|
61910
61920
|
this.mcpBridge = null;
|
|
61911
61921
|
this.initializeModel();
|
|
61922
|
+
}
|
|
61923
|
+
/**
|
|
61924
|
+
* Initialize the agent asynchronously (must be called after constructor)
|
|
61925
|
+
* This method initializes MCP and merges MCP tools into the tool list
|
|
61926
|
+
*/
|
|
61927
|
+
async initialize() {
|
|
61912
61928
|
if (this.enableMcp) {
|
|
61913
|
-
|
|
61929
|
+
try {
|
|
61930
|
+
await this.initializeMCP();
|
|
61931
|
+
if (this.mcpBridge) {
|
|
61932
|
+
const mcpTools = this.mcpBridge.mcpTools || {};
|
|
61933
|
+
for (const [toolName, toolImpl] of Object.entries(mcpTools)) {
|
|
61934
|
+
this.toolImplementations[toolName] = toolImpl;
|
|
61935
|
+
}
|
|
61936
|
+
}
|
|
61937
|
+
if (this.debug) {
|
|
61938
|
+
const allToolNames = Object.keys(this.toolImplementations);
|
|
61939
|
+
const nativeToolCount = allToolNames.filter((name14) => !this.mcpBridge?.mcpTools?.[name14]).length;
|
|
61940
|
+
const mcpToolCount = allToolNames.length - nativeToolCount;
|
|
61941
|
+
console.error("\n[DEBUG] ========================================");
|
|
61942
|
+
console.error("[DEBUG] All Tools Initialized");
|
|
61943
|
+
console.error(`[DEBUG] Native tools: ${nativeToolCount}, MCP tools: ${mcpToolCount}`);
|
|
61944
|
+
console.error("[DEBUG] Available tools:");
|
|
61945
|
+
for (const toolName of allToolNames) {
|
|
61946
|
+
const isMCP = this.mcpBridge?.mcpTools?.[toolName] ? " (MCP)" : "";
|
|
61947
|
+
console.error(`[DEBUG] - ${toolName}${isMCP}`);
|
|
61948
|
+
}
|
|
61949
|
+
console.error("[DEBUG] ========================================\n");
|
|
61950
|
+
}
|
|
61951
|
+
} catch (error2) {
|
|
61914
61952
|
console.error("[MCP] Failed to initialize MCP:", error2);
|
|
61915
61953
|
this.mcpBridge = null;
|
|
61916
|
-
}
|
|
61954
|
+
}
|
|
61917
61955
|
}
|
|
61918
61956
|
}
|
|
61919
61957
|
/**
|
|
@@ -62140,20 +62178,40 @@ var init_ProbeAgent = __esm({
|
|
|
62140
62178
|
}
|
|
62141
62179
|
}
|
|
62142
62180
|
/**
|
|
62143
|
-
* Extract directory paths from listFiles tool
|
|
62181
|
+
* Extract directory paths from tool output (both listFiles and extract tool)
|
|
62144
62182
|
* @param {string} content - Tool output content
|
|
62145
62183
|
* @returns {string[]} - Array of directory paths
|
|
62146
62184
|
*/
|
|
62147
62185
|
extractListFilesDirectories(content) {
|
|
62148
62186
|
const directories = [];
|
|
62149
|
-
const
|
|
62187
|
+
const fileHeaderPattern = /^File:\s+(.+)$/gm;
|
|
62150
62188
|
let match;
|
|
62189
|
+
while ((match = fileHeaderPattern.exec(content)) !== null) {
|
|
62190
|
+
const filePath = match[1].trim();
|
|
62191
|
+
const dir = (0, import_path10.dirname)(filePath);
|
|
62192
|
+
if (dir && dir !== ".") {
|
|
62193
|
+
directories.push(dir);
|
|
62194
|
+
if (this.debug) {
|
|
62195
|
+
console.log(`[DEBUG] Extracted directory context from File header: ${dir}`);
|
|
62196
|
+
}
|
|
62197
|
+
}
|
|
62198
|
+
}
|
|
62199
|
+
const dirPattern = /^(\/[^\n:]+|[A-Z]:\\[^\n:]+|\.\.?(?:\/[^\n:]+)?):\s*$/gm;
|
|
62151
62200
|
while ((match = dirPattern.exec(content)) !== null) {
|
|
62152
62201
|
const dirPath = match[1].trim();
|
|
62153
|
-
|
|
62154
|
-
|
|
62155
|
-
|
|
62156
|
-
|
|
62202
|
+
const hasInvalidChars = /\s/.test(dirPath);
|
|
62203
|
+
const isValidPath = !hasInvalidChars && (dirPath.startsWith("/") || // Unix absolute path
|
|
62204
|
+
/^[A-Z]:\\/.test(dirPath) || // Windows absolute path (C:\)
|
|
62205
|
+
dirPath === "." || // Current directory
|
|
62206
|
+
dirPath === ".." || // Parent directory
|
|
62207
|
+
dirPath.startsWith("./") && dirPath.length > 2 && !dirPath.includes(" ") || // ./something (no spaces)
|
|
62208
|
+
dirPath.startsWith("../") && dirPath.length > 3 && !dirPath.includes(" "));
|
|
62209
|
+
if (isValidPath) {
|
|
62210
|
+
if (!directories.includes(dirPath)) {
|
|
62211
|
+
directories.push(dirPath);
|
|
62212
|
+
if (this.debug) {
|
|
62213
|
+
console.log(`[DEBUG] Extracted directory context from listFiles: ${dirPath}`);
|
|
62214
|
+
}
|
|
62157
62215
|
}
|
|
62158
62216
|
}
|
|
62159
62217
|
}
|
package/cjs/index.cjs
CHANGED
|
@@ -60732,13 +60732,14 @@ async function validateMermaidDiagram(diagram) {
|
|
|
60732
60732
|
};
|
|
60733
60733
|
}
|
|
60734
60734
|
const result = validate(diagram);
|
|
60735
|
-
|
|
60735
|
+
const actualErrors = (result.errors || []).filter((err) => err.severity === "error");
|
|
60736
|
+
if (actualErrors.length === 0) {
|
|
60736
60737
|
return {
|
|
60737
60738
|
isValid: true,
|
|
60738
60739
|
diagramType: result.type || "unknown"
|
|
60739
60740
|
};
|
|
60740
60741
|
} else {
|
|
60741
|
-
const errorMessages =
|
|
60742
|
+
const errorMessages = actualErrors.map((err) => {
|
|
60742
60743
|
const location = err.line ? `line ${err.line}${err.column ? `:${err.column}` : ""}` : "";
|
|
60743
60744
|
return location ? `${location} - ${err.message}` : err.message;
|
|
60744
60745
|
});
|
|
@@ -60747,8 +60748,8 @@ async function validateMermaidDiagram(diagram) {
|
|
|
60747
60748
|
diagramType: result.type || "unknown",
|
|
60748
60749
|
error: errorMessages[0] || "Validation failed",
|
|
60749
60750
|
detailedError: errorMessages.join("\n"),
|
|
60750
|
-
errors:
|
|
60751
|
-
// Include
|
|
60751
|
+
errors: actualErrors
|
|
60752
|
+
// Include only actual errors for AI fixing
|
|
60752
60753
|
};
|
|
60753
60754
|
}
|
|
60754
60755
|
} catch (error2) {
|
|
@@ -61636,10 +61637,19 @@ var init_client2 = __esm({
|
|
|
61636
61637
|
if (this.debug) {
|
|
61637
61638
|
console.error(`[MCP] Calling ${toolName} with args:`, args);
|
|
61638
61639
|
}
|
|
61639
|
-
const
|
|
61640
|
-
|
|
61641
|
-
|
|
61640
|
+
const timeout = this.config?.settings?.timeout || 3e4;
|
|
61641
|
+
const timeoutPromise = new Promise((_2, reject2) => {
|
|
61642
|
+
setTimeout(() => {
|
|
61643
|
+
reject2(new Error(`MCP tool call timeout after ${timeout}ms`));
|
|
61644
|
+
}, timeout);
|
|
61642
61645
|
});
|
|
61646
|
+
const result = await Promise.race([
|
|
61647
|
+
clientInfo.client.callTool({
|
|
61648
|
+
name: tool3.originalName,
|
|
61649
|
+
arguments: args
|
|
61650
|
+
}),
|
|
61651
|
+
timeoutPromise
|
|
61652
|
+
]);
|
|
61643
61653
|
return result;
|
|
61644
61654
|
} catch (error2) {
|
|
61645
61655
|
console.error(`[MCP] Error calling tool ${toolName}:`, error2);
|
|
@@ -62053,11 +62063,39 @@ var init_ProbeAgent = __esm({
|
|
|
62053
62063
|
this.mcpServers = options.mcpServers || null;
|
|
62054
62064
|
this.mcpBridge = null;
|
|
62055
62065
|
this.initializeModel();
|
|
62066
|
+
}
|
|
62067
|
+
/**
|
|
62068
|
+
* Initialize the agent asynchronously (must be called after constructor)
|
|
62069
|
+
* This method initializes MCP and merges MCP tools into the tool list
|
|
62070
|
+
*/
|
|
62071
|
+
async initialize() {
|
|
62056
62072
|
if (this.enableMcp) {
|
|
62057
|
-
|
|
62073
|
+
try {
|
|
62074
|
+
await this.initializeMCP();
|
|
62075
|
+
if (this.mcpBridge) {
|
|
62076
|
+
const mcpTools = this.mcpBridge.mcpTools || {};
|
|
62077
|
+
for (const [toolName, toolImpl] of Object.entries(mcpTools)) {
|
|
62078
|
+
this.toolImplementations[toolName] = toolImpl;
|
|
62079
|
+
}
|
|
62080
|
+
}
|
|
62081
|
+
if (this.debug) {
|
|
62082
|
+
const allToolNames = Object.keys(this.toolImplementations);
|
|
62083
|
+
const nativeToolCount = allToolNames.filter((name14) => !this.mcpBridge?.mcpTools?.[name14]).length;
|
|
62084
|
+
const mcpToolCount = allToolNames.length - nativeToolCount;
|
|
62085
|
+
console.error("\n[DEBUG] ========================================");
|
|
62086
|
+
console.error("[DEBUG] All Tools Initialized");
|
|
62087
|
+
console.error(`[DEBUG] Native tools: ${nativeToolCount}, MCP tools: ${mcpToolCount}`);
|
|
62088
|
+
console.error("[DEBUG] Available tools:");
|
|
62089
|
+
for (const toolName of allToolNames) {
|
|
62090
|
+
const isMCP = this.mcpBridge?.mcpTools?.[toolName] ? " (MCP)" : "";
|
|
62091
|
+
console.error(`[DEBUG] - ${toolName}${isMCP}`);
|
|
62092
|
+
}
|
|
62093
|
+
console.error("[DEBUG] ========================================\n");
|
|
62094
|
+
}
|
|
62095
|
+
} catch (error2) {
|
|
62058
62096
|
console.error("[MCP] Failed to initialize MCP:", error2);
|
|
62059
62097
|
this.mcpBridge = null;
|
|
62060
|
-
}
|
|
62098
|
+
}
|
|
62061
62099
|
}
|
|
62062
62100
|
}
|
|
62063
62101
|
/**
|
|
@@ -62284,20 +62322,40 @@ var init_ProbeAgent = __esm({
|
|
|
62284
62322
|
}
|
|
62285
62323
|
}
|
|
62286
62324
|
/**
|
|
62287
|
-
* Extract directory paths from listFiles tool
|
|
62325
|
+
* Extract directory paths from tool output (both listFiles and extract tool)
|
|
62288
62326
|
* @param {string} content - Tool output content
|
|
62289
62327
|
* @returns {string[]} - Array of directory paths
|
|
62290
62328
|
*/
|
|
62291
62329
|
extractListFilesDirectories(content) {
|
|
62292
62330
|
const directories = [];
|
|
62293
|
-
const
|
|
62331
|
+
const fileHeaderPattern = /^File:\s+(.+)$/gm;
|
|
62294
62332
|
let match;
|
|
62333
|
+
while ((match = fileHeaderPattern.exec(content)) !== null) {
|
|
62334
|
+
const filePath = match[1].trim();
|
|
62335
|
+
const dir = (0, import_path9.dirname)(filePath);
|
|
62336
|
+
if (dir && dir !== ".") {
|
|
62337
|
+
directories.push(dir);
|
|
62338
|
+
if (this.debug) {
|
|
62339
|
+
console.log(`[DEBUG] Extracted directory context from File header: ${dir}`);
|
|
62340
|
+
}
|
|
62341
|
+
}
|
|
62342
|
+
}
|
|
62343
|
+
const dirPattern = /^(\/[^\n:]+|[A-Z]:\\[^\n:]+|\.\.?(?:\/[^\n:]+)?):\s*$/gm;
|
|
62295
62344
|
while ((match = dirPattern.exec(content)) !== null) {
|
|
62296
62345
|
const dirPath = match[1].trim();
|
|
62297
|
-
|
|
62298
|
-
|
|
62299
|
-
|
|
62300
|
-
|
|
62346
|
+
const hasInvalidChars = /\s/.test(dirPath);
|
|
62347
|
+
const isValidPath = !hasInvalidChars && (dirPath.startsWith("/") || // Unix absolute path
|
|
62348
|
+
/^[A-Z]:\\/.test(dirPath) || // Windows absolute path (C:\)
|
|
62349
|
+
dirPath === "." || // Current directory
|
|
62350
|
+
dirPath === ".." || // Parent directory
|
|
62351
|
+
dirPath.startsWith("./") && dirPath.length > 2 && !dirPath.includes(" ") || // ./something (no spaces)
|
|
62352
|
+
dirPath.startsWith("../") && dirPath.length > 3 && !dirPath.includes(" "));
|
|
62353
|
+
if (isValidPath) {
|
|
62354
|
+
if (!directories.includes(dirPath)) {
|
|
62355
|
+
directories.push(dirPath);
|
|
62356
|
+
if (this.debug) {
|
|
62357
|
+
console.log(`[DEBUG] Extracted directory context from listFiles: ${dirPath}`);
|
|
62358
|
+
}
|
|
62301
62359
|
}
|
|
62302
62360
|
}
|
|
62303
62361
|
}
|
package/package.json
CHANGED
package/src/agent/ProbeAgent.js
CHANGED
|
@@ -8,7 +8,7 @@ import { randomUUID } from 'crypto';
|
|
|
8
8
|
import { EventEmitter } from 'events';
|
|
9
9
|
import { existsSync } from 'fs';
|
|
10
10
|
import { readFile, stat } from 'fs/promises';
|
|
11
|
-
import { resolve, isAbsolute } from 'path';
|
|
11
|
+
import { resolve, isAbsolute, dirname } from 'path';
|
|
12
12
|
import { TokenCounter } from './tokenCounter.js';
|
|
13
13
|
import {
|
|
14
14
|
createTools,
|
|
@@ -145,12 +145,48 @@ export class ProbeAgent {
|
|
|
145
145
|
// Initialize the AI model
|
|
146
146
|
this.initializeModel();
|
|
147
147
|
|
|
148
|
+
// Note: MCP initialization is now done in initialize() method
|
|
149
|
+
// Constructor must remain synchronous for backward compatibility
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Initialize the agent asynchronously (must be called after constructor)
|
|
154
|
+
* This method initializes MCP and merges MCP tools into the tool list
|
|
155
|
+
*/
|
|
156
|
+
async initialize() {
|
|
148
157
|
// Initialize MCP if enabled
|
|
149
158
|
if (this.enableMcp) {
|
|
150
|
-
|
|
159
|
+
try {
|
|
160
|
+
await this.initializeMCP();
|
|
161
|
+
|
|
162
|
+
// Merge MCP tools into toolImplementations for unified access
|
|
163
|
+
if (this.mcpBridge) {
|
|
164
|
+
const mcpTools = this.mcpBridge.mcpTools || {};
|
|
165
|
+
for (const [toolName, toolImpl] of Object.entries(mcpTools)) {
|
|
166
|
+
this.toolImplementations[toolName] = toolImpl;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Log all available tools after MCP initialization
|
|
171
|
+
if (this.debug) {
|
|
172
|
+
const allToolNames = Object.keys(this.toolImplementations);
|
|
173
|
+
const nativeToolCount = allToolNames.filter(name => !this.mcpBridge?.mcpTools?.[name]).length;
|
|
174
|
+
const mcpToolCount = allToolNames.length - nativeToolCount;
|
|
175
|
+
|
|
176
|
+
console.error('\n[DEBUG] ========================================');
|
|
177
|
+
console.error('[DEBUG] All Tools Initialized');
|
|
178
|
+
console.error(`[DEBUG] Native tools: ${nativeToolCount}, MCP tools: ${mcpToolCount}`);
|
|
179
|
+
console.error('[DEBUG] Available tools:');
|
|
180
|
+
for (const toolName of allToolNames) {
|
|
181
|
+
const isMCP = this.mcpBridge?.mcpTools?.[toolName] ? ' (MCP)' : '';
|
|
182
|
+
console.error(`[DEBUG] - ${toolName}${isMCP}`);
|
|
183
|
+
}
|
|
184
|
+
console.error('[DEBUG] ========================================\n');
|
|
185
|
+
}
|
|
186
|
+
} catch (error) {
|
|
151
187
|
console.error('[MCP] Failed to initialize MCP:', error);
|
|
152
188
|
this.mcpBridge = null;
|
|
153
|
-
}
|
|
189
|
+
}
|
|
154
190
|
}
|
|
155
191
|
}
|
|
156
192
|
|
|
@@ -442,23 +478,62 @@ export class ProbeAgent {
|
|
|
442
478
|
}
|
|
443
479
|
|
|
444
480
|
/**
|
|
445
|
-
* Extract directory paths from listFiles tool
|
|
481
|
+
* Extract directory paths from tool output (both listFiles and extract tool)
|
|
446
482
|
* @param {string} content - Tool output content
|
|
447
483
|
* @returns {string[]} - Array of directory paths
|
|
448
484
|
*/
|
|
449
485
|
extractListFilesDirectories(content) {
|
|
450
486
|
const directories = [];
|
|
451
487
|
|
|
452
|
-
// Pattern
|
|
453
|
-
|
|
488
|
+
// Pattern 1: Extract directory from extract tool "File:" header
|
|
489
|
+
// Format: "File: /path/to/file.md" or "File: ./relative/path/file.md"
|
|
490
|
+
const fileHeaderPattern = /^File:\s+(.+)$/gm;
|
|
454
491
|
|
|
455
492
|
let match;
|
|
493
|
+
while ((match = fileHeaderPattern.exec(content)) !== null) {
|
|
494
|
+
const filePath = match[1].trim();
|
|
495
|
+
// Get directory from file path
|
|
496
|
+
const dir = dirname(filePath);
|
|
497
|
+
if (dir && dir !== '.') {
|
|
498
|
+
directories.push(dir);
|
|
499
|
+
if (this.debug) {
|
|
500
|
+
console.log(`[DEBUG] Extracted directory context from File header: ${dir}`);
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
// Pattern 2: Extract directory from listFiles output format: "/path/to/directory:"
|
|
506
|
+
// Matches absolute paths (/path/to/dir:) or current directory markers (.:) or Windows paths (C:\path:) at start of line
|
|
507
|
+
// Very strict to avoid matching random text like ".Something:" or "./Some text:"
|
|
508
|
+
const dirPattern = /^(\/[^\n:]+|[A-Z]:\\[^\n:]+|\.\.?(?:\/[^\n:]+)?):\s*$/gm;
|
|
509
|
+
|
|
456
510
|
while ((match = dirPattern.exec(content)) !== null) {
|
|
457
511
|
const dirPath = match[1].trim();
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
512
|
+
|
|
513
|
+
// Strict validation: must look like an actual filesystem path
|
|
514
|
+
// Reject if contains spaces or other characters that wouldn't be in listFiles output
|
|
515
|
+
const hasInvalidChars = /\s/.test(dirPath); // Contains whitespace
|
|
516
|
+
|
|
517
|
+
// Validate this looks like an actual path, not random text
|
|
518
|
+
// Must be either: absolute path (Unix or Windows), or ./ or ../ followed by valid path chars
|
|
519
|
+
const isValidPath = (
|
|
520
|
+
!hasInvalidChars && (
|
|
521
|
+
dirPath.startsWith('/') || // Unix absolute path
|
|
522
|
+
/^[A-Z]:\\/.test(dirPath) || // Windows absolute path (C:\)
|
|
523
|
+
dirPath === '.' || // Current directory
|
|
524
|
+
dirPath === '..' || // Parent directory
|
|
525
|
+
(dirPath.startsWith('./') && dirPath.length > 2 && !dirPath.includes(' ')) || // ./something (no spaces)
|
|
526
|
+
(dirPath.startsWith('../') && dirPath.length > 3 && !dirPath.includes(' ')) // ../something (no spaces)
|
|
527
|
+
)
|
|
528
|
+
);
|
|
529
|
+
|
|
530
|
+
if (isValidPath) {
|
|
531
|
+
// Avoid duplicates
|
|
532
|
+
if (!directories.includes(dirPath)) {
|
|
533
|
+
directories.push(dirPath);
|
|
534
|
+
if (this.debug) {
|
|
535
|
+
console.log(`[DEBUG] Extracted directory context from listFiles: ${dirPath}`);
|
|
536
|
+
}
|
|
462
537
|
}
|
|
463
538
|
}
|
|
464
539
|
}
|
package/src/agent/acp/server.js
CHANGED
|
@@ -31,12 +31,14 @@ class ACPSession {
|
|
|
31
31
|
/**
|
|
32
32
|
* Get or create ProbeAgent for this session
|
|
33
33
|
*/
|
|
34
|
-
getAgent(config = {}) {
|
|
34
|
+
async getAgent(config = {}) {
|
|
35
35
|
if (!this.agent) {
|
|
36
36
|
this.agent = new ProbeAgent({
|
|
37
37
|
sessionId: this.id,
|
|
38
38
|
...config
|
|
39
39
|
});
|
|
40
|
+
// Initialize MCP if enabled
|
|
41
|
+
await this.agent.initialize();
|
|
40
42
|
}
|
|
41
43
|
return this.agent;
|
|
42
44
|
}
|
|
@@ -320,20 +322,23 @@ export class ACPServer {
|
|
|
320
322
|
}
|
|
321
323
|
|
|
322
324
|
session.touch();
|
|
323
|
-
|
|
325
|
+
|
|
324
326
|
// Get or create ProbeAgent for this session
|
|
325
|
-
const agent = session.getAgent({
|
|
327
|
+
const agent = await session.getAgent({
|
|
326
328
|
path: this.options.path,
|
|
327
329
|
provider: this.options.provider,
|
|
328
330
|
model: this.options.model,
|
|
329
331
|
allowEdit: this.options.allowEdit,
|
|
330
|
-
debug: this.options.debug
|
|
332
|
+
debug: this.options.debug,
|
|
333
|
+
enableMcp: this.options.enableMcp,
|
|
334
|
+
mcpConfig: this.options.mcpConfig,
|
|
335
|
+
mcpConfigPath: this.options.mcpConfigPath
|
|
331
336
|
});
|
|
332
|
-
|
|
337
|
+
|
|
333
338
|
if (this.options.debug) {
|
|
334
339
|
console.error(`[ACP] Processing prompt for session ${params.sessionId}:`, params.message.substring(0, 100));
|
|
335
340
|
}
|
|
336
|
-
|
|
341
|
+
|
|
337
342
|
try {
|
|
338
343
|
// Process the message with the ProbeAgent
|
|
339
344
|
const response = await agent.answer(params.message);
|
package/src/agent/index.js
CHANGED
|
@@ -428,6 +428,8 @@ class ProbeAgentMcpServer {
|
|
|
428
428
|
};
|
|
429
429
|
|
|
430
430
|
this.agent = new ProbeAgent(agentConfig);
|
|
431
|
+
// Initialize MCP if enabled
|
|
432
|
+
await this.agent.initialize();
|
|
431
433
|
}
|
|
432
434
|
|
|
433
435
|
const agent = this.agent;
|
|
@@ -717,7 +719,9 @@ async function main() {
|
|
|
717
719
|
};
|
|
718
720
|
|
|
719
721
|
const agent = new ProbeAgent(agentConfig);
|
|
720
|
-
|
|
722
|
+
// Initialize MCP if enabled
|
|
723
|
+
await agent.initialize();
|
|
724
|
+
|
|
721
725
|
// Execute with tracing if available
|
|
722
726
|
let result;
|
|
723
727
|
if (appTracer) {
|
package/src/agent/mcp/client.js
CHANGED
|
@@ -242,11 +242,25 @@ export class MCPClientManager {
|
|
|
242
242
|
console.error(`[MCP] Calling ${toolName} with args:`, args);
|
|
243
243
|
}
|
|
244
244
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
245
|
+
// Get timeout from config (default 30 seconds)
|
|
246
|
+
const timeout = this.config?.settings?.timeout || 30000;
|
|
247
|
+
|
|
248
|
+
// Create a timeout promise
|
|
249
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
250
|
+
setTimeout(() => {
|
|
251
|
+
reject(new Error(`MCP tool call timeout after ${timeout}ms`));
|
|
252
|
+
}, timeout);
|
|
248
253
|
});
|
|
249
254
|
|
|
255
|
+
// Race between the actual call and timeout
|
|
256
|
+
const result = await Promise.race([
|
|
257
|
+
clientInfo.client.callTool({
|
|
258
|
+
name: tool.originalName,
|
|
259
|
+
arguments: args
|
|
260
|
+
}),
|
|
261
|
+
timeoutPromise
|
|
262
|
+
]);
|
|
263
|
+
|
|
250
264
|
return result;
|
|
251
265
|
} catch (error) {
|
|
252
266
|
console.error(`[MCP] Error calling tool ${toolName}:`, error);
|
package/src/agent/schemaUtils.js
CHANGED
|
@@ -562,15 +562,18 @@ export async function validateMermaidDiagram(diagram) {
|
|
|
562
562
|
const result = validate(diagram);
|
|
563
563
|
|
|
564
564
|
// Maid returns { type: string, errors: array }
|
|
565
|
-
//
|
|
566
|
-
|
|
565
|
+
// Only count actual errors (severity: 'error'), not warnings
|
|
566
|
+
const actualErrors = (result.errors || []).filter(err => err.severity === 'error');
|
|
567
|
+
|
|
568
|
+
// Valid if no actual errors (warnings are OK)
|
|
569
|
+
if (actualErrors.length === 0) {
|
|
567
570
|
return {
|
|
568
571
|
isValid: true,
|
|
569
572
|
diagramType: result.type || 'unknown'
|
|
570
573
|
};
|
|
571
574
|
} else {
|
|
572
575
|
// Format maid errors into a readable error message
|
|
573
|
-
const errorMessages =
|
|
576
|
+
const errorMessages = actualErrors.map(err => {
|
|
574
577
|
const location = err.line ? `line ${err.line}${err.column ? `:${err.column}` : ''}` : '';
|
|
575
578
|
return location ? `${location} - ${err.message}` : err.message;
|
|
576
579
|
});
|
|
@@ -580,7 +583,7 @@ export async function validateMermaidDiagram(diagram) {
|
|
|
580
583
|
diagramType: result.type || 'unknown',
|
|
581
584
|
error: errorMessages[0] || 'Validation failed',
|
|
582
585
|
detailedError: errorMessages.join('\n'),
|
|
583
|
-
errors:
|
|
586
|
+
errors: actualErrors // Include only actual errors for AI fixing
|
|
584
587
|
};
|
|
585
588
|
}
|
|
586
589
|
|