@probelabs/probe 0.6.0-rc167 → 0.6.0-rc169
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.d.ts +7 -1
- package/build/agent/ProbeAgent.js +353 -32
- package/build/agent/engines/codex.js +347 -0
- package/build/agent/engines/enhanced-claude-code.js +3 -42
- package/build/agent/index.js +1036 -81
- package/build/agent/mcp/built-in-server.js +334 -8
- package/build/agent/shared/Session.js +53 -0
- package/build/agent/shared/prompts.js +129 -0
- package/build/agent/tools.js +26 -9
- package/cjs/agent/ProbeAgent.cjs +1074 -92
- package/cjs/index.cjs +1083 -101
- package/index.d.ts +7 -1
- package/package.json +1 -1
- package/src/agent/ProbeAgent.d.ts +7 -1
- package/src/agent/ProbeAgent.js +353 -32
- package/src/agent/engines/codex.js +347 -0
- package/src/agent/engines/enhanced-claude-code.js +3 -42
- package/src/agent/mcp/built-in-server.js +334 -8
- package/src/agent/shared/Session.js +53 -0
- package/src/agent/shared/prompts.js +129 -0
- package/src/agent/tools.js +26 -9
|
@@ -5,12 +5,71 @@
|
|
|
5
5
|
|
|
6
6
|
import { createServer } from 'http';
|
|
7
7
|
import { EventEmitter } from 'events';
|
|
8
|
+
import { randomUUID } from 'crypto';
|
|
8
9
|
import { Server as MCPServer } from '@modelcontextprotocol/sdk/server/index.js';
|
|
10
|
+
import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';
|
|
11
|
+
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
|
|
9
12
|
import {
|
|
10
13
|
CallToolRequestSchema,
|
|
11
|
-
ListToolsRequestSchema
|
|
14
|
+
ListToolsRequestSchema,
|
|
15
|
+
isInitializeRequest
|
|
12
16
|
} from '@modelcontextprotocol/sdk/types.js';
|
|
13
17
|
|
|
18
|
+
/**
|
|
19
|
+
* Simple in-memory event store for resumability
|
|
20
|
+
*/
|
|
21
|
+
class InMemoryEventStore {
|
|
22
|
+
constructor() {
|
|
23
|
+
this.events = new Map();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
generateEventId(streamId) {
|
|
27
|
+
return `${streamId}_${Date.now()}_${Math.random().toString(36).substring(2, 10)}`;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
getStreamIdFromEventId(eventId) {
|
|
31
|
+
const parts = eventId.split('_');
|
|
32
|
+
return parts.length > 0 ? parts[0] : '';
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async storeEvent(streamId, message) {
|
|
36
|
+
const eventId = this.generateEventId(streamId);
|
|
37
|
+
this.events.set(eventId, { streamId, message });
|
|
38
|
+
return eventId;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async replayEventsAfter(lastEventId, { send }) {
|
|
42
|
+
if (!lastEventId || !this.events.has(lastEventId)) {
|
|
43
|
+
return '';
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const streamId = this.getStreamIdFromEventId(lastEventId);
|
|
47
|
+
if (!streamId) {
|
|
48
|
+
return '';
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
let foundLastEvent = false;
|
|
52
|
+
const sortedEvents = [...this.events.entries()].sort((a, b) => a[0].localeCompare(b[0]));
|
|
53
|
+
|
|
54
|
+
for (const [eventId, { streamId: eventStreamId, message }] of sortedEvents) {
|
|
55
|
+
if (eventStreamId !== streamId) {
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (eventId === lastEventId) {
|
|
60
|
+
foundLastEvent = true;
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (foundLastEvent) {
|
|
65
|
+
await send(eventId, message);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return streamId;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
14
73
|
/**
|
|
15
74
|
* Built-in MCP Server that runs in-process
|
|
16
75
|
*/
|
|
@@ -22,6 +81,8 @@ export class BuiltInMCPServer extends EventEmitter {
|
|
|
22
81
|
this.host = options.host || '127.0.0.1';
|
|
23
82
|
this.httpServer = null;
|
|
24
83
|
this.mcpServer = null;
|
|
84
|
+
this.sseTransports = new Map(); // Map of sessionId -> SSEServerTransport (deprecated)
|
|
85
|
+
this.streamableTransports = new Map(); // Map of sessionId -> StreamableHTTPServerTransport
|
|
25
86
|
this.connections = new Set();
|
|
26
87
|
this.debug = options.debug || false;
|
|
27
88
|
}
|
|
@@ -53,12 +114,14 @@ export class BuiltInMCPServer extends EventEmitter {
|
|
|
53
114
|
|
|
54
115
|
// Start listening on ephemeral port
|
|
55
116
|
return new Promise((resolve, reject) => {
|
|
56
|
-
this.httpServer.listen(this.port, this.host, () => {
|
|
117
|
+
this.httpServer.listen(this.port, this.host, async () => {
|
|
57
118
|
const address = this.httpServer.address();
|
|
58
119
|
this.port = address.port;
|
|
59
120
|
|
|
60
121
|
if (this.debug) {
|
|
61
122
|
console.log(`[MCP] Built-in server started at http://${this.host}:${this.port}`);
|
|
123
|
+
console.log(`[MCP] SSE endpoint: http://${this.host}:${this.port}/sse`);
|
|
124
|
+
console.log(`[MCP] Messages endpoint: http://${this.host}:${this.port}/messages`);
|
|
62
125
|
}
|
|
63
126
|
|
|
64
127
|
this.emit('ready', { host: this.host, port: this.port });
|
|
@@ -75,6 +138,10 @@ export class BuiltInMCPServer extends EventEmitter {
|
|
|
75
138
|
handleRequest(req, res) {
|
|
76
139
|
const { method, url } = req;
|
|
77
140
|
|
|
141
|
+
if (this.debug) {
|
|
142
|
+
console.log(`[MCP] Request: ${method} ${url}`);
|
|
143
|
+
}
|
|
144
|
+
|
|
78
145
|
// CORS headers for local development
|
|
79
146
|
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
80
147
|
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
|
|
@@ -86,9 +153,18 @@ export class BuiltInMCPServer extends EventEmitter {
|
|
|
86
153
|
return;
|
|
87
154
|
}
|
|
88
155
|
|
|
89
|
-
// Handle SSE endpoint
|
|
156
|
+
// Handle SSE endpoint (GET) - create new transport per connection
|
|
90
157
|
if (url === '/sse' && method === 'GET') {
|
|
91
|
-
this.
|
|
158
|
+
if (this.debug) {
|
|
159
|
+
console.log('[MCP] Routing to handleSSEConnection');
|
|
160
|
+
}
|
|
161
|
+
this.handleSSEConnection(req, res);
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Handle /messages endpoint (POST) - route to existing transport
|
|
166
|
+
if (url.startsWith('/messages') && method === 'POST') {
|
|
167
|
+
this.handleSSEMessage(req, res);
|
|
92
168
|
return;
|
|
93
169
|
}
|
|
94
170
|
|
|
@@ -98,9 +174,9 @@ export class BuiltInMCPServer extends EventEmitter {
|
|
|
98
174
|
return;
|
|
99
175
|
}
|
|
100
176
|
|
|
101
|
-
// Handle
|
|
102
|
-
if (url === '/mcp'
|
|
103
|
-
this.
|
|
177
|
+
// Handle Streamable HTTP protocol (GET/POST/DELETE on /mcp)
|
|
178
|
+
if (url === '/mcp') {
|
|
179
|
+
this.handleStreamableHTTP(req, res);
|
|
104
180
|
return;
|
|
105
181
|
}
|
|
106
182
|
|
|
@@ -121,7 +197,227 @@ export class BuiltInMCPServer extends EventEmitter {
|
|
|
121
197
|
}
|
|
122
198
|
|
|
123
199
|
/**
|
|
124
|
-
* Handle
|
|
200
|
+
* Handle SSE connection (GET /sse) - creates new transport
|
|
201
|
+
*/
|
|
202
|
+
async handleSSEConnection(req, res) {
|
|
203
|
+
if (this.debug) {
|
|
204
|
+
console.log('[MCP] New SSE connection request');
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Create new SSEServerTransport for this connection
|
|
208
|
+
const transport = new SSEServerTransport('/messages', res);
|
|
209
|
+
|
|
210
|
+
// Store transport by sessionId
|
|
211
|
+
this.sseTransports.set(transport.sessionId, transport);
|
|
212
|
+
|
|
213
|
+
// Clean up on connection close
|
|
214
|
+
res.on('close', () => {
|
|
215
|
+
if (this.debug) {
|
|
216
|
+
console.log('[MCP] SSE connection closed, sessionId:', transport.sessionId);
|
|
217
|
+
}
|
|
218
|
+
this.sseTransports.delete(transport.sessionId);
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
// Connect MCP server to this transport
|
|
222
|
+
try {
|
|
223
|
+
await this.mcpServer.connect(transport);
|
|
224
|
+
if (this.debug) {
|
|
225
|
+
console.log('[MCP] MCP server connected to SSE transport, sessionId:', transport.sessionId);
|
|
226
|
+
}
|
|
227
|
+
} catch (error) {
|
|
228
|
+
if (this.debug) {
|
|
229
|
+
console.error('[MCP] Error connecting MCP server to transport:', error);
|
|
230
|
+
}
|
|
231
|
+
this.sseTransports.delete(transport.sessionId);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Handle SSE message (POST /messages?sessionId=...) - routes to existing transport
|
|
237
|
+
*/
|
|
238
|
+
async handleSSEMessage(req, res) {
|
|
239
|
+
// Parse URL to get sessionId from query parameter
|
|
240
|
+
const url = new URL(req.url, `http://${req.headers.host}`);
|
|
241
|
+
const sessionId = url.searchParams.get('sessionId');
|
|
242
|
+
|
|
243
|
+
if (!sessionId) {
|
|
244
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
245
|
+
res.end(JSON.stringify({
|
|
246
|
+
jsonrpc: '2.0',
|
|
247
|
+
error: {
|
|
248
|
+
code: -32000,
|
|
249
|
+
message: 'Bad Request: sessionId query parameter is required'
|
|
250
|
+
},
|
|
251
|
+
id: null
|
|
252
|
+
}));
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// Find transport for this session
|
|
257
|
+
const transport = this.sseTransports.get(sessionId);
|
|
258
|
+
if (!transport) {
|
|
259
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
260
|
+
res.end(JSON.stringify({
|
|
261
|
+
jsonrpc: '2.0',
|
|
262
|
+
error: {
|
|
263
|
+
code: -32000,
|
|
264
|
+
message: `Bad Request: No transport found for sessionId: ${sessionId}`
|
|
265
|
+
},
|
|
266
|
+
id: null
|
|
267
|
+
}));
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// Read request body
|
|
272
|
+
let body = '';
|
|
273
|
+
req.on('data', chunk => {
|
|
274
|
+
body += chunk.toString();
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
req.on('end', async () => {
|
|
278
|
+
try {
|
|
279
|
+
const message = JSON.parse(body);
|
|
280
|
+
await transport.handlePostMessage(req, res, message);
|
|
281
|
+
} catch (error) {
|
|
282
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
283
|
+
res.end(JSON.stringify({
|
|
284
|
+
jsonrpc: '2.0',
|
|
285
|
+
error: {
|
|
286
|
+
code: -32603,
|
|
287
|
+
message: 'Internal error',
|
|
288
|
+
data: error.message
|
|
289
|
+
},
|
|
290
|
+
id: null
|
|
291
|
+
}));
|
|
292
|
+
}
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Handle Streamable HTTP protocol (GET/POST/DELETE on /mcp)
|
|
298
|
+
*/
|
|
299
|
+
async handleStreamableHTTP(req, res) {
|
|
300
|
+
const { method } = req;
|
|
301
|
+
|
|
302
|
+
if (this.debug) {
|
|
303
|
+
console.log(`[MCP] Streamable HTTP ${method} request`);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
try {
|
|
307
|
+
// Parse request body for POST requests
|
|
308
|
+
let body = null;
|
|
309
|
+
if (method === 'POST') {
|
|
310
|
+
body = await this.parseRequestBody(req);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// Check for existing session ID in header
|
|
314
|
+
const sessionId = req.headers['mcp-session-id'];
|
|
315
|
+
let transport;
|
|
316
|
+
|
|
317
|
+
if (sessionId && this.streamableTransports.has(sessionId)) {
|
|
318
|
+
// Reuse existing transport
|
|
319
|
+
transport = this.streamableTransports.get(sessionId);
|
|
320
|
+
if (this.debug) {
|
|
321
|
+
console.log(`[MCP] Reusing existing transport for session: ${sessionId}`);
|
|
322
|
+
}
|
|
323
|
+
} else if (!sessionId && method === 'POST' && body && isInitializeRequest(body)) {
|
|
324
|
+
// New session - create transport for initialization request
|
|
325
|
+
if (this.debug) {
|
|
326
|
+
console.log('[MCP] Creating new Streamable HTTP transport for initialization');
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
const eventStore = new InMemoryEventStore();
|
|
330
|
+
transport = new StreamableHTTPServerTransport({
|
|
331
|
+
sessionIdGenerator: () => randomUUID(),
|
|
332
|
+
eventStore, // Enable resumability
|
|
333
|
+
onsessioninitialized: (newSessionId) => {
|
|
334
|
+
// Store the transport by session ID
|
|
335
|
+
if (this.debug) {
|
|
336
|
+
console.log(`[MCP] Streamable HTTP session initialized: ${newSessionId}`);
|
|
337
|
+
}
|
|
338
|
+
this.streamableTransports.set(newSessionId, transport);
|
|
339
|
+
},
|
|
340
|
+
onsessionclosed: (closedSessionId) => {
|
|
341
|
+
// Remove transport when session is closed
|
|
342
|
+
if (this.debug) {
|
|
343
|
+
console.log(`[MCP] Streamable HTTP session closed: ${closedSessionId}`);
|
|
344
|
+
}
|
|
345
|
+
this.streamableTransports.delete(closedSessionId);
|
|
346
|
+
}
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
// Set up onclose handler
|
|
350
|
+
transport.onclose = () => {
|
|
351
|
+
const sid = transport.sessionId;
|
|
352
|
+
if (sid && this.streamableTransports.has(sid)) {
|
|
353
|
+
if (this.debug) {
|
|
354
|
+
console.log(`[MCP] Transport closed for session ${sid}`);
|
|
355
|
+
}
|
|
356
|
+
this.streamableTransports.delete(sid);
|
|
357
|
+
}
|
|
358
|
+
};
|
|
359
|
+
|
|
360
|
+
// Connect the transport to the MCP server
|
|
361
|
+
await this.mcpServer.connect(transport);
|
|
362
|
+
} else {
|
|
363
|
+
// Invalid request - no session ID or not an initialization request
|
|
364
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
365
|
+
res.end(JSON.stringify({
|
|
366
|
+
jsonrpc: '2.0',
|
|
367
|
+
error: {
|
|
368
|
+
code: -32000,
|
|
369
|
+
message: 'Bad Request: No valid session ID provided or not an initialization request'
|
|
370
|
+
},
|
|
371
|
+
id: null
|
|
372
|
+
}));
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// Handle the request with the transport
|
|
377
|
+
await transport.handleRequest(req, res, body);
|
|
378
|
+
} catch (error) {
|
|
379
|
+
if (this.debug) {
|
|
380
|
+
console.error('[MCP] Error handling Streamable HTTP request:', error);
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
if (!res.headersSent) {
|
|
384
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
385
|
+
res.end(JSON.stringify({
|
|
386
|
+
jsonrpc: '2.0',
|
|
387
|
+
error: {
|
|
388
|
+
code: -32603,
|
|
389
|
+
message: 'Internal server error',
|
|
390
|
+
data: error.message
|
|
391
|
+
},
|
|
392
|
+
id: null
|
|
393
|
+
}));
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
/**
|
|
399
|
+
* Parse request body as JSON
|
|
400
|
+
*/
|
|
401
|
+
async parseRequestBody(req) {
|
|
402
|
+
return new Promise((resolve, reject) => {
|
|
403
|
+
let body = '';
|
|
404
|
+
req.on('data', chunk => {
|
|
405
|
+
body += chunk.toString();
|
|
406
|
+
});
|
|
407
|
+
req.on('end', () => {
|
|
408
|
+
try {
|
|
409
|
+
const parsed = body ? JSON.parse(body) : null;
|
|
410
|
+
resolve(parsed);
|
|
411
|
+
} catch (error) {
|
|
412
|
+
reject(error);
|
|
413
|
+
}
|
|
414
|
+
});
|
|
415
|
+
req.on('error', reject);
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
/**
|
|
420
|
+
* Handle Server-Sent Events connection (DEPRECATED - use handleSSEConnection instead)
|
|
125
421
|
*/
|
|
126
422
|
handleSSE(req, res) {
|
|
127
423
|
res.writeHead(200, {
|
|
@@ -430,6 +726,36 @@ export class BuiltInMCPServer extends EventEmitter {
|
|
|
430
726
|
* Stop the server
|
|
431
727
|
*/
|
|
432
728
|
async stop() {
|
|
729
|
+
// Close all Streamable HTTP transports
|
|
730
|
+
for (const [sessionId, transport] of this.streamableTransports.entries()) {
|
|
731
|
+
try {
|
|
732
|
+
await transport.close();
|
|
733
|
+
if (this.debug) {
|
|
734
|
+
console.log(`[MCP] Closed Streamable HTTP transport for session: ${sessionId}`);
|
|
735
|
+
}
|
|
736
|
+
} catch (error) {
|
|
737
|
+
if (this.debug) {
|
|
738
|
+
console.error(`[MCP] Error closing Streamable HTTP transport ${sessionId}:`, error);
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
this.streamableTransports.clear();
|
|
743
|
+
|
|
744
|
+
// Close all SSE transports
|
|
745
|
+
for (const [sessionId, transport] of this.sseTransports.entries()) {
|
|
746
|
+
try {
|
|
747
|
+
await transport.close();
|
|
748
|
+
if (this.debug) {
|
|
749
|
+
console.log(`[MCP] Closed SSE transport for session: ${sessionId}`);
|
|
750
|
+
}
|
|
751
|
+
} catch (error) {
|
|
752
|
+
if (this.debug) {
|
|
753
|
+
console.error(`[MCP] Error closing SSE transport ${sessionId}:`, error);
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
this.sseTransports.clear();
|
|
758
|
+
|
|
433
759
|
// Close all SSE connections
|
|
434
760
|
for (const connection of this.connections) {
|
|
435
761
|
connection.end();
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base Session class for AI provider engines
|
|
3
|
+
* Manages conversation state and message counting
|
|
4
|
+
*/
|
|
5
|
+
export class Session {
|
|
6
|
+
constructor(id, debug = false) {
|
|
7
|
+
this.id = id;
|
|
8
|
+
this.conversationId = null; // Provider-specific conversation/thread ID for resumption
|
|
9
|
+
this.messageCount = 0;
|
|
10
|
+
this.debug = debug;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Set the conversation ID for session resumption
|
|
15
|
+
* @param {string} conversationId - Provider's conversation/thread ID
|
|
16
|
+
*/
|
|
17
|
+
setConversationId(conversationId) {
|
|
18
|
+
this.conversationId = conversationId;
|
|
19
|
+
if (this.debug) {
|
|
20
|
+
console.log(`[Session ${this.id}] Conversation ID: ${conversationId}`);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Increment the message count
|
|
26
|
+
*/
|
|
27
|
+
incrementMessageCount() {
|
|
28
|
+
this.messageCount++;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Get session info as plain object
|
|
33
|
+
* @returns {Object} Session information
|
|
34
|
+
*/
|
|
35
|
+
getInfo() {
|
|
36
|
+
return {
|
|
37
|
+
id: this.id,
|
|
38
|
+
conversationId: this.conversationId,
|
|
39
|
+
messageCount: this.messageCount
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Get resume arguments for CLI commands (used by Claude Code)
|
|
45
|
+
* @returns {Array<string>} CLI arguments for resuming conversation
|
|
46
|
+
*/
|
|
47
|
+
getResumeArgs() {
|
|
48
|
+
if (this.conversationId && this.messageCount > 0) {
|
|
49
|
+
return ['--resume', this.conversationId];
|
|
50
|
+
}
|
|
51
|
+
return [];
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared predefined prompts for all AI providers
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export const predefinedPrompts = {
|
|
6
|
+
'code-explorer': `You are ProbeChat Code Explorer - an AI assistant focused on reading and explaining code using Probe's semantic search capabilities.
|
|
7
|
+
|
|
8
|
+
Your primary role is to explore codebases, understand code structure, and provide clear explanations. You should:
|
|
9
|
+
|
|
10
|
+
1. Use the 'search' tool to find relevant code snippets across the codebase
|
|
11
|
+
2. Use the 'query' tool to locate specific symbols, functions, or classes
|
|
12
|
+
3. Use the 'extract' tool to get full context for files or specific code blocks
|
|
13
|
+
4. Explain code behavior, architecture patterns, and relationships between components
|
|
14
|
+
5. Provide concise summaries with key insights highlighted
|
|
15
|
+
|
|
16
|
+
When exploring code:
|
|
17
|
+
- Start with targeted searches to find relevant areas
|
|
18
|
+
- Extract full context when you need to understand implementation details
|
|
19
|
+
- Trace function calls and data flow to understand how components interact
|
|
20
|
+
- Focus on "why" and "how" rather than just describing what the code does
|
|
21
|
+
|
|
22
|
+
You should NOT:
|
|
23
|
+
- Make changes to the codebase (read-only access)
|
|
24
|
+
- Execute or run code
|
|
25
|
+
- Make assumptions without verifying through search/query/extract`,
|
|
26
|
+
|
|
27
|
+
'architect': `You are ProbeChat Architect - a senior software architect specialized in analyzing and designing software systems.
|
|
28
|
+
|
|
29
|
+
Your role is to:
|
|
30
|
+
|
|
31
|
+
1. Analyze existing codebases to understand architecture patterns and design decisions
|
|
32
|
+
2. Identify architectural issues, technical debt, and improvement opportunities
|
|
33
|
+
3. Propose refactoring strategies and architectural changes
|
|
34
|
+
4. Design new features that fit well with existing architecture
|
|
35
|
+
5. Create high-level architecture diagrams and documentation
|
|
36
|
+
|
|
37
|
+
When analyzing architecture:
|
|
38
|
+
- Use search/query/extract to understand component boundaries and dependencies
|
|
39
|
+
- Identify layers, modules, and their responsibilities
|
|
40
|
+
- Look for patterns like MVC, microservices, event-driven, etc.
|
|
41
|
+
- Assess coupling, cohesion, and separation of concerns
|
|
42
|
+
- Consider scalability, maintainability, and testability
|
|
43
|
+
|
|
44
|
+
When proposing changes:
|
|
45
|
+
- Provide clear rationale backed by architectural principles
|
|
46
|
+
- Consider migration paths and backwards compatibility
|
|
47
|
+
- Identify risks and tradeoffs
|
|
48
|
+
- Suggest incremental implementation steps`,
|
|
49
|
+
|
|
50
|
+
'code-review': `You are ProbeChat Code Reviewer - a meticulous code reviewer focused on quality, best practices, and maintainability.
|
|
51
|
+
|
|
52
|
+
Your role is to:
|
|
53
|
+
|
|
54
|
+
1. Review code changes for correctness, clarity, and best practices
|
|
55
|
+
2. Identify bugs, security issues, and performance problems
|
|
56
|
+
3. Suggest improvements for readability and maintainability
|
|
57
|
+
4. Ensure consistency with project conventions and patterns
|
|
58
|
+
5. Verify test coverage and edge case handling
|
|
59
|
+
|
|
60
|
+
When reviewing code:
|
|
61
|
+
- Use search to find similar patterns in the codebase for consistency
|
|
62
|
+
- Look for common issues: null checks, error handling, resource leaks
|
|
63
|
+
- Check for security vulnerabilities: injection, XSS, etc.
|
|
64
|
+
- Assess complexity and suggest simplifications
|
|
65
|
+
- Verify naming conventions and code organization
|
|
66
|
+
|
|
67
|
+
Provide constructive feedback:
|
|
68
|
+
- Start with what's good about the code
|
|
69
|
+
- Be specific about issues with examples
|
|
70
|
+
- Suggest concrete improvements with code snippets
|
|
71
|
+
- Prioritize critical issues over style preferences
|
|
72
|
+
- Explain the "why" behind your suggestions`,
|
|
73
|
+
|
|
74
|
+
'engineer': `You are a senior engineer who helps implement features and fix bugs.
|
|
75
|
+
|
|
76
|
+
Your role is to:
|
|
77
|
+
|
|
78
|
+
1. Implement new features following project conventions
|
|
79
|
+
2. Fix bugs by understanding root causes
|
|
80
|
+
3. Refactor code to improve quality
|
|
81
|
+
4. Write clear, maintainable code
|
|
82
|
+
5. Add appropriate tests and documentation
|
|
83
|
+
|
|
84
|
+
When implementing:
|
|
85
|
+
- Use search/query to understand existing patterns
|
|
86
|
+
- Follow the project's coding style and conventions
|
|
87
|
+
- Consider edge cases and error handling
|
|
88
|
+
- Write self-documenting code with clear names
|
|
89
|
+
- Add comments for complex logic
|
|
90
|
+
|
|
91
|
+
You have access to:
|
|
92
|
+
- search: Find relevant code patterns
|
|
93
|
+
- query: Locate specific symbols/functions
|
|
94
|
+
- extract: Get full file context
|
|
95
|
+
- implement: Create or modify files (use carefully)
|
|
96
|
+
- delegate: Break down complex tasks`,
|
|
97
|
+
|
|
98
|
+
'support': `You are ProbeChat Support - a helpful assistant focused on answering questions about codebases.
|
|
99
|
+
|
|
100
|
+
Your role is to:
|
|
101
|
+
|
|
102
|
+
1. Answer questions about how code works
|
|
103
|
+
2. Help users locate specific functionality
|
|
104
|
+
3. Explain error messages and debugging approaches
|
|
105
|
+
4. Guide users to relevant documentation
|
|
106
|
+
5. Provide examples and usage patterns
|
|
107
|
+
|
|
108
|
+
When helping users:
|
|
109
|
+
- Ask clarifying questions if the request is ambiguous
|
|
110
|
+
- Use search/query to find relevant code quickly
|
|
111
|
+
- Provide clear, step-by-step explanations
|
|
112
|
+
- Include code examples when helpful
|
|
113
|
+
- Point to relevant files and line numbers
|
|
114
|
+
|
|
115
|
+
You should be:
|
|
116
|
+
- Patient and encouraging
|
|
117
|
+
- Clear and concise
|
|
118
|
+
- Thorough but not overwhelming
|
|
119
|
+
- Honest about limitations (say "I don't know" when appropriate)`
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Get a predefined prompt by type
|
|
124
|
+
* @param {string} type - The prompt type
|
|
125
|
+
* @returns {string|null} The prompt text or null if not found
|
|
126
|
+
*/
|
|
127
|
+
export function getPredefinedPrompt(type) {
|
|
128
|
+
return predefinedPrompts[type] || null;
|
|
129
|
+
}
|
package/build/agent/tools.js
CHANGED
|
@@ -31,21 +31,39 @@ import { processXmlWithThinkingAndRecovery } from './xmlParsingUtils.js';
|
|
|
31
31
|
|
|
32
32
|
// Create configured tool instances
|
|
33
33
|
export function createTools(configOptions) {
|
|
34
|
-
const tools = {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
34
|
+
const tools = {};
|
|
35
|
+
|
|
36
|
+
const isToolAllowed =
|
|
37
|
+
configOptions.isToolAllowed ||
|
|
38
|
+
((toolName) => {
|
|
39
|
+
if (!configOptions.allowedTools) return true;
|
|
40
|
+
return configOptions.allowedTools.isEnabled(toolName);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
// Core tools
|
|
44
|
+
if (isToolAllowed('search')) {
|
|
45
|
+
tools.searchTool = searchTool(configOptions);
|
|
46
|
+
}
|
|
47
|
+
if (isToolAllowed('query')) {
|
|
48
|
+
tools.queryTool = queryTool(configOptions);
|
|
49
|
+
}
|
|
50
|
+
if (isToolAllowed('extract')) {
|
|
51
|
+
tools.extractTool = extractTool(configOptions);
|
|
52
|
+
}
|
|
53
|
+
if (configOptions.enableDelegate && isToolAllowed('delegate')) {
|
|
54
|
+
tools.delegateTool = delegateTool(configOptions);
|
|
55
|
+
}
|
|
40
56
|
|
|
41
57
|
// Add bash tool if enabled
|
|
42
|
-
if (configOptions.enableBash) {
|
|
58
|
+
if (configOptions.enableBash && isToolAllowed('bash')) {
|
|
43
59
|
tools.bashTool = bashTool(configOptions);
|
|
44
60
|
}
|
|
45
61
|
|
|
46
62
|
// Add edit and create tools if enabled
|
|
47
|
-
if (configOptions.allowEdit) {
|
|
63
|
+
if (configOptions.allowEdit && isToolAllowed('edit')) {
|
|
48
64
|
tools.editTool = editTool(configOptions);
|
|
65
|
+
}
|
|
66
|
+
if (configOptions.allowEdit && isToolAllowed('create')) {
|
|
49
67
|
tools.createTool = createTool(configOptions);
|
|
50
68
|
}
|
|
51
69
|
|
|
@@ -199,4 +217,3 @@ export function parseXmlToolCallWithThinking(xmlString, validTools) {
|
|
|
199
217
|
// Otherwise, use the original parseXmlToolCall function to parse the cleaned XML string
|
|
200
218
|
return parseXmlToolCall(cleanedXmlString, validTools);
|
|
201
219
|
}
|
|
202
|
-
|