@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
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenAI Codex Engine using MCP server approach with event streaming
|
|
3
|
+
* Runs 'codex mcp-server' and handles codex/event notifications
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { spawn } from 'child_process';
|
|
7
|
+
import { randomBytes } from 'crypto';
|
|
8
|
+
import { createInterface } from 'readline';
|
|
9
|
+
import { BuiltInMCPServer } from '../mcp/built-in-server.js';
|
|
10
|
+
import { Session } from '../shared/Session.js';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Codex Engine using MCP Server with event streaming
|
|
14
|
+
*/
|
|
15
|
+
export async function createCodexEngine(options = {}) {
|
|
16
|
+
const { agent, systemPrompt, customPrompt, debug, sessionId, allowedTools, model } = options;
|
|
17
|
+
|
|
18
|
+
const session = new Session(
|
|
19
|
+
sessionId || randomBytes(8).toString('hex'),
|
|
20
|
+
debug
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
// Start built-in MCP server for Probe tools
|
|
24
|
+
let mcpServer = null;
|
|
25
|
+
let mcpServerUrl = null;
|
|
26
|
+
let mcpServerName = null;
|
|
27
|
+
|
|
28
|
+
if (agent) {
|
|
29
|
+
mcpServer = new BuiltInMCPServer(agent, {
|
|
30
|
+
port: 0,
|
|
31
|
+
host: '127.0.0.1',
|
|
32
|
+
debug: debug
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
const { host, port } = await mcpServer.start();
|
|
36
|
+
mcpServerUrl = `http://${host}:${port}/mcp`;
|
|
37
|
+
mcpServerName = `probe_${session.id}`;
|
|
38
|
+
|
|
39
|
+
if (debug) {
|
|
40
|
+
console.log('[DEBUG] Built-in Probe MCP server started');
|
|
41
|
+
console.log('[DEBUG] Probe MCP URL:', mcpServerUrl);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Start Codex MCP server
|
|
46
|
+
if (debug) {
|
|
47
|
+
console.log('[DEBUG] Starting Codex MCP server...');
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const codexProcess = spawn('codex', ['mcp-server'], {
|
|
51
|
+
stdio: ['pipe', 'pipe', 'pipe']
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
// Setup JSON-RPC communication
|
|
55
|
+
let requestId = 0;
|
|
56
|
+
const pendingRequests = new Map();
|
|
57
|
+
const eventHandlers = new Map();
|
|
58
|
+
|
|
59
|
+
// Read stdout line by line
|
|
60
|
+
const stdoutReader = createInterface({
|
|
61
|
+
input: codexProcess.stdout,
|
|
62
|
+
crlfDelay: Infinity
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
stdoutReader.on('line', (line) => {
|
|
66
|
+
try {
|
|
67
|
+
const message = JSON.parse(line);
|
|
68
|
+
|
|
69
|
+
if (debug) {
|
|
70
|
+
if (message.method === 'codex/event') {
|
|
71
|
+
console.log(`[DEBUG] Codex event: ${message.params?.msg?.type}`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Handle responses to our requests
|
|
76
|
+
if (message.id !== undefined && pendingRequests.has(message.id)) {
|
|
77
|
+
const { resolve, reject } = pendingRequests.get(message.id);
|
|
78
|
+
pendingRequests.delete(message.id);
|
|
79
|
+
|
|
80
|
+
if (message.error) {
|
|
81
|
+
reject(new Error(message.error.message || JSON.stringify(message.error)));
|
|
82
|
+
} else {
|
|
83
|
+
resolve(message.result);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Handle notifications (codex/event)
|
|
88
|
+
if (message.method === 'codex/event' && message.params) {
|
|
89
|
+
const requestId = message.params._meta?.requestId;
|
|
90
|
+
if (requestId !== undefined && eventHandlers.has(requestId)) {
|
|
91
|
+
eventHandlers.get(requestId)(message.params);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
} catch (e) {
|
|
95
|
+
if (debug) {
|
|
96
|
+
console.error('[DEBUG] Failed to parse message:', line);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
// Handle stderr
|
|
102
|
+
if (debug) {
|
|
103
|
+
codexProcess.stderr.on('data', (data) => {
|
|
104
|
+
console.error('[CODEX STDERR]', data.toString());
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Send JSON-RPC request
|
|
109
|
+
function sendRequest(method, params = {}) {
|
|
110
|
+
return new Promise((resolve, reject) => {
|
|
111
|
+
const id = ++requestId;
|
|
112
|
+
const request = {
|
|
113
|
+
jsonrpc: '2.0',
|
|
114
|
+
id,
|
|
115
|
+
method,
|
|
116
|
+
params
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
pendingRequests.set(id, { resolve, reject });
|
|
120
|
+
|
|
121
|
+
// Timeout after 10 minutes
|
|
122
|
+
setTimeout(() => {
|
|
123
|
+
if (pendingRequests.has(id)) {
|
|
124
|
+
pendingRequests.delete(id);
|
|
125
|
+
reject(new Error(`Request ${method} timed out after 10 minutes`));
|
|
126
|
+
}
|
|
127
|
+
}, 600000);
|
|
128
|
+
|
|
129
|
+
codexProcess.stdin.write(JSON.stringify(request) + '\n');
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Initialize MCP connection
|
|
134
|
+
await sendRequest('initialize', {
|
|
135
|
+
protocolVersion: '2024-11-05',
|
|
136
|
+
capabilities: { tools: {} },
|
|
137
|
+
clientInfo: {
|
|
138
|
+
name: 'probe-codex-client',
|
|
139
|
+
version: '1.0.0'
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
if (debug) {
|
|
144
|
+
console.log('[DEBUG] Connected to Codex MCP server');
|
|
145
|
+
console.log('[DEBUG] Session:', session.id);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const fullPrompt = combinePrompts(systemPrompt, customPrompt, agent);
|
|
149
|
+
|
|
150
|
+
return {
|
|
151
|
+
sessionId: session.id,
|
|
152
|
+
session,
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Query Codex via MCP protocol with event streaming
|
|
156
|
+
*/
|
|
157
|
+
async *query(prompt, opts = {}) {
|
|
158
|
+
// Build prompt
|
|
159
|
+
let finalPrompt = prompt;
|
|
160
|
+
if (!session.conversationId && fullPrompt) {
|
|
161
|
+
finalPrompt = `${fullPrompt}\n\n${prompt}`;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const isFollowUp = session.conversationId !== null;
|
|
165
|
+
const toolName = isFollowUp ? 'codex-reply' : 'codex';
|
|
166
|
+
|
|
167
|
+
// Build arguments
|
|
168
|
+
const toolArgs = { prompt: finalPrompt };
|
|
169
|
+
|
|
170
|
+
if (isFollowUp) {
|
|
171
|
+
toolArgs.conversationId = session.conversationId;
|
|
172
|
+
if (debug) {
|
|
173
|
+
console.log(`[DEBUG] Follow-up with conversationId: ${session.conversationId}`);
|
|
174
|
+
}
|
|
175
|
+
} else {
|
|
176
|
+
if (model) {
|
|
177
|
+
toolArgs.model = model;
|
|
178
|
+
}
|
|
179
|
+
if (mcpServerUrl && mcpServerName) {
|
|
180
|
+
toolArgs.config = {
|
|
181
|
+
mcp_servers: {
|
|
182
|
+
[mcpServerName]: { url: mcpServerUrl }
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
if (debug) {
|
|
187
|
+
console.log(`[DEBUG] Initial query with tool: ${toolName}`);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
try {
|
|
192
|
+
const reqId = requestId + 1;
|
|
193
|
+
let fullResponse = '';
|
|
194
|
+
let gotSessionId = false;
|
|
195
|
+
|
|
196
|
+
// Register event handler for this request
|
|
197
|
+
const eventPromise = new Promise((resolve) => {
|
|
198
|
+
eventHandlers.set(reqId, (eventParams) => {
|
|
199
|
+
const msg = eventParams.msg;
|
|
200
|
+
|
|
201
|
+
// Extract session_id from session_configured event
|
|
202
|
+
if (msg.type === 'session_configured' && msg.session_id && !gotSessionId) {
|
|
203
|
+
session.setConversationId(msg.session_id);
|
|
204
|
+
gotSessionId = true;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Collect agent messages
|
|
208
|
+
if (msg.type === 'raw_response_item' && msg.item?.role === 'assistant') {
|
|
209
|
+
const content = msg.item.content;
|
|
210
|
+
if (Array.isArray(content)) {
|
|
211
|
+
for (const part of content) {
|
|
212
|
+
if (part.type === 'text' && part.text) {
|
|
213
|
+
fullResponse += part.text;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
// Mark as resolved when we're done
|
|
221
|
+
setTimeout(() => {
|
|
222
|
+
eventHandlers.delete(reqId);
|
|
223
|
+
resolve();
|
|
224
|
+
}, 600000); // 10 min timeout
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
// Call the tool
|
|
228
|
+
const resultPromise = sendRequest('tools/call', {
|
|
229
|
+
name: toolName,
|
|
230
|
+
arguments: toolArgs
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
// Wait for result
|
|
234
|
+
const result = await resultPromise;
|
|
235
|
+
|
|
236
|
+
// Clean up event handler
|
|
237
|
+
eventHandlers.delete(reqId);
|
|
238
|
+
|
|
239
|
+
// Parse result
|
|
240
|
+
if (result && result.content && Array.isArray(result.content)) {
|
|
241
|
+
for (const item of result.content) {
|
|
242
|
+
if (item.type === 'text' && item.text) {
|
|
243
|
+
yield {
|
|
244
|
+
type: 'text',
|
|
245
|
+
content: item.text
|
|
246
|
+
};
|
|
247
|
+
fullResponse = item.text; // Use final result if available
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// If we got a response from events but not from result, yield it
|
|
253
|
+
if (fullResponse && (!result.content || result.content.length === 0)) {
|
|
254
|
+
yield {
|
|
255
|
+
type: 'text',
|
|
256
|
+
content: fullResponse
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
session.incrementMessageCount();
|
|
261
|
+
|
|
262
|
+
yield {
|
|
263
|
+
type: 'metadata',
|
|
264
|
+
data: {
|
|
265
|
+
sessionId: session.id,
|
|
266
|
+
conversationId: session.conversationId,
|
|
267
|
+
messageCount: session.messageCount
|
|
268
|
+
}
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
} catch (error) {
|
|
272
|
+
if (debug) {
|
|
273
|
+
console.error('[DEBUG] Codex query error:', error);
|
|
274
|
+
}
|
|
275
|
+
yield {
|
|
276
|
+
type: 'error',
|
|
277
|
+
error: error
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
},
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Get session info
|
|
284
|
+
*/
|
|
285
|
+
getSession() {
|
|
286
|
+
return session.getInfo();
|
|
287
|
+
},
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Clean up resources
|
|
291
|
+
*/
|
|
292
|
+
async close() {
|
|
293
|
+
try {
|
|
294
|
+
// Close readline interface first to remove event listeners
|
|
295
|
+
if (stdoutReader) {
|
|
296
|
+
stdoutReader.close();
|
|
297
|
+
if (debug) {
|
|
298
|
+
console.log('[DEBUG] Closed stdout reader');
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// Clear all pending requests and event handlers
|
|
303
|
+
pendingRequests.clear();
|
|
304
|
+
eventHandlers.clear();
|
|
305
|
+
|
|
306
|
+
// Kill Codex process
|
|
307
|
+
if (codexProcess && !codexProcess.killed) {
|
|
308
|
+
codexProcess.kill();
|
|
309
|
+
if (debug) {
|
|
310
|
+
console.log('[DEBUG] Killed Codex MCP server process');
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// Stop Probe MCP server
|
|
315
|
+
if (mcpServer) {
|
|
316
|
+
await mcpServer.stop();
|
|
317
|
+
if (debug) {
|
|
318
|
+
console.log('[DEBUG] Stopped Probe MCP server');
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
if (debug) {
|
|
323
|
+
console.log('[DEBUG] Engine closed, session:', session.id);
|
|
324
|
+
}
|
|
325
|
+
} catch (error) {
|
|
326
|
+
if (debug) {
|
|
327
|
+
console.error('[DEBUG] Error during cleanup:', error.message);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* Combine prompts intelligently
|
|
336
|
+
*/
|
|
337
|
+
function combinePrompts(systemPrompt, customPrompt, agent) {
|
|
338
|
+
if (!systemPrompt && customPrompt) {
|
|
339
|
+
return customPrompt;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
if (systemPrompt && customPrompt) {
|
|
343
|
+
return systemPrompt + '\n\n## Additional Instructions\n' + customPrompt;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
return systemPrompt || '';
|
|
347
|
+
}
|
|
@@ -9,42 +9,7 @@ import path from 'path';
|
|
|
9
9
|
import os from 'os';
|
|
10
10
|
import { EventEmitter } from 'events';
|
|
11
11
|
import { BuiltInMCPServer } from '../mcp/built-in-server.js';
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Session manager for Claude Code conversations
|
|
15
|
-
*/
|
|
16
|
-
class ClaudeSession {
|
|
17
|
-
constructor(id, debug = false) {
|
|
18
|
-
this.id = id;
|
|
19
|
-
this.conversationId = null;
|
|
20
|
-
this.messageCount = 0;
|
|
21
|
-
this.debug = debug;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Update session with Claude's conversation ID
|
|
26
|
-
*/
|
|
27
|
-
setConversationId(convId) {
|
|
28
|
-
this.conversationId = convId;
|
|
29
|
-
if (this.debug) {
|
|
30
|
-
console.log(`[Session ${this.id}] Conversation ID: ${convId}`);
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Get resume arguments for continuing conversation
|
|
36
|
-
*/
|
|
37
|
-
getResumeArgs() {
|
|
38
|
-
if (this.conversationId && this.messageCount > 0) {
|
|
39
|
-
return ['--resume', this.conversationId];
|
|
40
|
-
}
|
|
41
|
-
return [];
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
incrementMessageCount() {
|
|
45
|
-
this.messageCount++;
|
|
46
|
-
}
|
|
47
|
-
}
|
|
12
|
+
import { Session } from '../shared/Session.js';
|
|
48
13
|
|
|
49
14
|
/**
|
|
50
15
|
* Enhanced Claude Code Engine
|
|
@@ -53,7 +18,7 @@ export async function createEnhancedClaudeCLIEngine(options = {}) {
|
|
|
53
18
|
const { agent, systemPrompt, customPrompt, debug, sessionId, allowedTools } = options;
|
|
54
19
|
|
|
55
20
|
// Create or reuse session
|
|
56
|
-
const session = new
|
|
21
|
+
const session = new Session(
|
|
57
22
|
sessionId || randomBytes(8).toString('hex'),
|
|
58
23
|
debug
|
|
59
24
|
);
|
|
@@ -335,11 +300,7 @@ export async function createEnhancedClaudeCLIEngine(options = {}) {
|
|
|
335
300
|
* Get session info
|
|
336
301
|
*/
|
|
337
302
|
getSession() {
|
|
338
|
-
return
|
|
339
|
-
id: session.id,
|
|
340
|
-
conversationId: session.conversationId,
|
|
341
|
-
messageCount: session.messageCount
|
|
342
|
-
};
|
|
303
|
+
return session.getInfo();
|
|
343
304
|
},
|
|
344
305
|
|
|
345
306
|
/**
|