@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
|
@@ -11,6 +11,8 @@ export interface ProbeAgentOptions {
|
|
|
11
11
|
sessionId?: string;
|
|
12
12
|
/** Custom system prompt to replace the default system message */
|
|
13
13
|
customPrompt?: string;
|
|
14
|
+
/** Alias for customPrompt. More intuitive naming for system prompts. */
|
|
15
|
+
systemPrompt?: string;
|
|
14
16
|
/** Predefined prompt type (persona) */
|
|
15
17
|
promptType?: 'code-explorer' | 'engineer' | 'code-review' | 'support' | 'architect';
|
|
16
18
|
/** Allow the use of the 'implement' tool for code editing */
|
|
@@ -35,6 +37,10 @@ export interface ProbeAgentOptions {
|
|
|
35
37
|
mcpConfig?: any;
|
|
36
38
|
/** @deprecated Use mcpConfig instead */
|
|
37
39
|
mcpServers?: any[];
|
|
40
|
+
/** List of allowed tool names. Use ['*'] for all tools (default), [] or null for no tools (raw AI mode), or specific tool names like ['search', 'query', 'extract']. Supports exclusion with '!' prefix (e.g., ['*', '!bash']). */
|
|
41
|
+
allowedTools?: string[] | null;
|
|
42
|
+
/** Convenience flag to disable all tools (equivalent to allowedTools: []). Takes precedence over allowedTools if set. */
|
|
43
|
+
disableTools?: boolean;
|
|
38
44
|
/** Retry configuration for handling transient API failures */
|
|
39
45
|
retry?: RetryOptions;
|
|
40
46
|
/** Fallback configuration for multi-provider support */
|
|
@@ -227,4 +233,4 @@ export interface ProbeAgentEvents {
|
|
|
227
233
|
}
|
|
228
234
|
|
|
229
235
|
// Default export
|
|
230
|
-
export { ProbeAgent as default };
|
|
236
|
+
export { ProbeAgent as default };
|
|
@@ -55,6 +55,7 @@ import {
|
|
|
55
55
|
validateAndFixMermaidResponse
|
|
56
56
|
} from './schemaUtils.js';
|
|
57
57
|
import { removeThinkingTags } from './xmlParsingUtils.js';
|
|
58
|
+
import { predefinedPrompts } from './shared/prompts.js';
|
|
58
59
|
import {
|
|
59
60
|
MCPXmlBridge,
|
|
60
61
|
parseHybridXmlToolCall,
|
|
@@ -89,6 +90,7 @@ export class ProbeAgent {
|
|
|
89
90
|
* @param {Object} options - Configuration options
|
|
90
91
|
* @param {string} [options.sessionId] - Optional session ID
|
|
91
92
|
* @param {string} [options.customPrompt] - Custom prompt to replace the default system message
|
|
93
|
+
* @param {string} [options.systemPrompt] - Alias for customPrompt; takes precedence when both are provided
|
|
92
94
|
* @param {string} [options.promptType] - Predefined prompt type (architect, code-review, support)
|
|
93
95
|
* @param {boolean} [options.allowEdit=false] - Allow the use of the 'implement' tool
|
|
94
96
|
* @param {boolean} [options.enableDelegate=false] - Enable the delegate tool for task distribution to subagents
|
|
@@ -125,7 +127,8 @@ export class ProbeAgent {
|
|
|
125
127
|
constructor(options = {}) {
|
|
126
128
|
// Basic configuration
|
|
127
129
|
this.sessionId = options.sessionId || randomUUID();
|
|
128
|
-
|
|
130
|
+
// Support systemPrompt alias (overrides customPrompt when both are provided)
|
|
131
|
+
this.customPrompt = options.systemPrompt || options.customPrompt || null;
|
|
129
132
|
this.promptType = options.promptType || 'code-explorer';
|
|
130
133
|
this.allowEdit = !!options.allowEdit;
|
|
131
134
|
this.enableDelegate = !!options.enableDelegate;
|
|
@@ -304,12 +307,14 @@ export class ProbeAgent {
|
|
|
304
307
|
* This method initializes MCP and merges MCP tools into the tool list, and loads history from storage
|
|
305
308
|
*/
|
|
306
309
|
async initialize() {
|
|
307
|
-
// Check if we need to auto-detect claude-code provider
|
|
310
|
+
// Check if we need to auto-detect claude-code or codex provider
|
|
308
311
|
// This happens when no API keys are set and no provider is specified
|
|
309
|
-
if (!this.provider && !this.clientApiProvider && this.apiType !== 'claude-code') {
|
|
312
|
+
if (!this.provider && !this.clientApiProvider && this.apiType !== 'claude-code' && this.apiType !== 'codex') {
|
|
310
313
|
// Check if initializeModel marked as uninitialized (no API keys)
|
|
311
314
|
if (this.apiType === 'uninitialized') {
|
|
312
315
|
const claudeAvailable = await this.isClaudeCommandAvailable();
|
|
316
|
+
const codexAvailable = await this.isCodexCommandAvailable();
|
|
317
|
+
|
|
313
318
|
if (claudeAvailable) {
|
|
314
319
|
if (this.debug) {
|
|
315
320
|
console.log('[DEBUG] No API keys found, but claude command detected');
|
|
@@ -320,11 +325,22 @@ export class ProbeAgent {
|
|
|
320
325
|
this.provider = null;
|
|
321
326
|
this.model = this.clientApiModel || 'claude-3-5-sonnet-20241022';
|
|
322
327
|
this.apiType = 'claude-code';
|
|
328
|
+
} else if (codexAvailable) {
|
|
329
|
+
if (this.debug) {
|
|
330
|
+
console.log('[DEBUG] No API keys found, but codex command detected');
|
|
331
|
+
console.log('[DEBUG] Auto-switching to codex provider');
|
|
332
|
+
}
|
|
333
|
+
// Set provider to codex
|
|
334
|
+
this.clientApiProvider = 'codex';
|
|
335
|
+
this.provider = null;
|
|
336
|
+
this.model = this.clientApiModel || 'gpt-4o';
|
|
337
|
+
this.apiType = 'codex';
|
|
323
338
|
} else {
|
|
324
|
-
// Neither API keys nor
|
|
325
|
-
throw new Error('No API key provided and claude command
|
|
339
|
+
// Neither API keys nor CLI commands available
|
|
340
|
+
throw new Error('No API key provided and neither claude nor codex command found. Please either:\n' +
|
|
326
341
|
'1. Set an API key: ANTHROPIC_API_KEY, OPENAI_API_KEY, GOOGLE_GENERATIVE_AI_API_KEY, or AWS credentials\n' +
|
|
327
|
-
'2. Install claude command from https://docs.claude.com/en/docs/claude-code'
|
|
342
|
+
'2. Install claude command from https://docs.claude.com/en/docs/claude-code\n' +
|
|
343
|
+
'3. Install codex command from https://openai.com/codex');
|
|
328
344
|
}
|
|
329
345
|
}
|
|
330
346
|
}
|
|
@@ -404,14 +420,20 @@ export class ProbeAgent {
|
|
|
404
420
|
* Initialize tools with configuration
|
|
405
421
|
*/
|
|
406
422
|
initializeTools() {
|
|
423
|
+
const isToolAllowed = (toolName) => this.allowedTools.isEnabled(toolName);
|
|
424
|
+
|
|
407
425
|
const configOptions = {
|
|
408
426
|
sessionId: this.sessionId,
|
|
409
427
|
debug: this.debug,
|
|
410
428
|
defaultPath: this.allowedFolders.length > 0 ? this.allowedFolders[0] : process.cwd(),
|
|
411
429
|
allowedFolders: this.allowedFolders,
|
|
412
430
|
outline: this.outline,
|
|
431
|
+
allowEdit: this.allowEdit,
|
|
432
|
+
enableDelegate: this.enableDelegate,
|
|
413
433
|
enableBash: this.enableBash,
|
|
414
|
-
bashConfig: this.bashConfig
|
|
434
|
+
bashConfig: this.bashConfig,
|
|
435
|
+
allowedTools: this.allowedTools,
|
|
436
|
+
isToolAllowed
|
|
415
437
|
};
|
|
416
438
|
|
|
417
439
|
// Create base tools
|
|
@@ -420,15 +442,33 @@ export class ProbeAgent {
|
|
|
420
442
|
// Create wrapped tools with event emission
|
|
421
443
|
const wrappedTools = createWrappedTools(baseTools);
|
|
422
444
|
|
|
423
|
-
// Store tool instances for execution
|
|
424
|
-
this.toolImplementations = {
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
445
|
+
// Store tool instances for execution (respect allowedTools + feature flags)
|
|
446
|
+
this.toolImplementations = {};
|
|
447
|
+
|
|
448
|
+
if (wrappedTools.searchToolInstance && isToolAllowed('search')) {
|
|
449
|
+
this.toolImplementations.search = wrappedTools.searchToolInstance;
|
|
450
|
+
}
|
|
451
|
+
if (wrappedTools.queryToolInstance && isToolAllowed('query')) {
|
|
452
|
+
this.toolImplementations.query = wrappedTools.queryToolInstance;
|
|
453
|
+
}
|
|
454
|
+
if (wrappedTools.extractToolInstance && isToolAllowed('extract')) {
|
|
455
|
+
this.toolImplementations.extract = wrappedTools.extractToolInstance;
|
|
456
|
+
}
|
|
457
|
+
if (this.enableDelegate && wrappedTools.delegateToolInstance && isToolAllowed('delegate')) {
|
|
458
|
+
this.toolImplementations.delegate = wrappedTools.delegateToolInstance;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
// File browsing tools
|
|
462
|
+
if (isToolAllowed('listFiles')) {
|
|
463
|
+
this.toolImplementations.listFiles = listFilesToolInstance;
|
|
464
|
+
}
|
|
465
|
+
if (isToolAllowed('searchFiles')) {
|
|
466
|
+
this.toolImplementations.searchFiles = searchFilesToolInstance;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
// Image loading tool
|
|
470
|
+
if (isToolAllowed('readImage')) {
|
|
471
|
+
this.toolImplementations.readImage = {
|
|
432
472
|
execute: async (params) => {
|
|
433
473
|
const imagePath = params.path;
|
|
434
474
|
if (!imagePath) {
|
|
@@ -444,20 +484,20 @@ export class ProbeAgent {
|
|
|
444
484
|
|
|
445
485
|
return `Image loaded successfully: ${imagePath}. The image is now available for analysis in the conversation.`;
|
|
446
486
|
}
|
|
447
|
-
}
|
|
448
|
-
}
|
|
487
|
+
};
|
|
488
|
+
}
|
|
449
489
|
|
|
450
|
-
// Add bash tool if enabled
|
|
451
|
-
if (this.enableBash && wrappedTools.bashToolInstance) {
|
|
490
|
+
// Add bash tool if enabled and allowed
|
|
491
|
+
if (this.enableBash && wrappedTools.bashToolInstance && isToolAllowed('bash')) {
|
|
452
492
|
this.toolImplementations.bash = wrappedTools.bashToolInstance;
|
|
453
493
|
}
|
|
454
494
|
|
|
455
|
-
// Add edit and create tools if enabled
|
|
495
|
+
// Add edit and create tools if enabled and allowed
|
|
456
496
|
if (this.allowEdit) {
|
|
457
|
-
if (wrappedTools.editToolInstance) {
|
|
497
|
+
if (wrappedTools.editToolInstance && isToolAllowed('edit')) {
|
|
458
498
|
this.toolImplementations.edit = wrappedTools.editToolInstance;
|
|
459
499
|
}
|
|
460
|
-
if (wrappedTools.createToolInstance) {
|
|
500
|
+
if (wrappedTools.createToolInstance && isToolAllowed('create')) {
|
|
461
501
|
this.toolImplementations.create = wrappedTools.createToolInstance;
|
|
462
502
|
}
|
|
463
503
|
}
|
|
@@ -482,13 +522,34 @@ export class ProbeAgent {
|
|
|
482
522
|
|
|
483
523
|
/**
|
|
484
524
|
* Check if claude command is available on the system
|
|
525
|
+
* Uses execFile instead of exec to avoid shell injection risks
|
|
485
526
|
* @returns {Promise<boolean>} True if claude command is available
|
|
486
527
|
* @private
|
|
487
528
|
*/
|
|
488
529
|
async isClaudeCommandAvailable() {
|
|
489
530
|
try {
|
|
490
|
-
const {
|
|
491
|
-
|
|
531
|
+
const { execFile } = await import('child_process');
|
|
532
|
+
const { promisify } = await import('util');
|
|
533
|
+
const execFileAsync = promisify(execFile);
|
|
534
|
+
await execFileAsync('claude', ['--version'], { timeout: 5000 });
|
|
535
|
+
return true;
|
|
536
|
+
} catch (error) {
|
|
537
|
+
return false;
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
/**
|
|
542
|
+
* Check if codex command is available on the system
|
|
543
|
+
* Uses execFile instead of exec to avoid shell injection risks
|
|
544
|
+
* @returns {Promise<boolean>} True if codex command is available
|
|
545
|
+
* @private
|
|
546
|
+
*/
|
|
547
|
+
async isCodexCommandAvailable() {
|
|
548
|
+
try {
|
|
549
|
+
const { execFile } = await import('child_process');
|
|
550
|
+
const { promisify } = await import('util');
|
|
551
|
+
const execFileAsync = promisify(execFile);
|
|
552
|
+
await execFileAsync('codex', ['--version'], { timeout: 5000 });
|
|
492
553
|
return true;
|
|
493
554
|
} catch (error) {
|
|
494
555
|
return false;
|
|
@@ -521,6 +582,25 @@ export class ProbeAgent {
|
|
|
521
582
|
return;
|
|
522
583
|
}
|
|
523
584
|
|
|
585
|
+
// Skip API key requirement for Codex CLI (uses built-in access in Codex CLI)
|
|
586
|
+
if (this.clientApiProvider === 'codex' || process.env.USE_CODEX === 'true') {
|
|
587
|
+
// Codex CLI engine will be initialized lazily in getEngine()
|
|
588
|
+
// Set minimal defaults for compatibility
|
|
589
|
+
this.provider = null;
|
|
590
|
+
// Only set model if explicitly provided, otherwise let Codex use account default
|
|
591
|
+
this.model = modelName || null;
|
|
592
|
+
this.apiType = 'codex';
|
|
593
|
+
if (this.debug) {
|
|
594
|
+
console.log('[DEBUG] Codex CLI engine selected - will use built-in access if available');
|
|
595
|
+
if (this.model) {
|
|
596
|
+
console.log(`[DEBUG] Using model: ${this.model}`);
|
|
597
|
+
} else {
|
|
598
|
+
console.log('[DEBUG] Using Codex account default model');
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
return;
|
|
602
|
+
}
|
|
603
|
+
|
|
524
604
|
// Get API keys from environment variables
|
|
525
605
|
// Support both ANTHROPIC_API_KEY and ANTHROPIC_AUTH_TOKEN (used by Z.AI)
|
|
526
606
|
const anthropicApiKey = process.env.ANTHROPIC_API_KEY || process.env.ANTHROPIC_AUTH_TOKEN;
|
|
@@ -724,6 +804,60 @@ export class ProbeAgent {
|
|
|
724
804
|
}
|
|
725
805
|
}
|
|
726
806
|
|
|
807
|
+
// Check if we should use Codex engine
|
|
808
|
+
if (this.clientApiProvider === 'codex' || process.env.USE_CODEX === 'true') {
|
|
809
|
+
try {
|
|
810
|
+
const engine = await this.getEngine();
|
|
811
|
+
if (engine && engine.query) {
|
|
812
|
+
// Convert Vercel AI SDK format to engine format
|
|
813
|
+
// Extract the ORIGINAL user message as the main prompt (skip any warning messages)
|
|
814
|
+
// Look for user messages that are NOT the warning message
|
|
815
|
+
const userMessages = options.messages.filter(m =>
|
|
816
|
+
m.role === 'user' &&
|
|
817
|
+
!m.content.includes('WARNING: You have reached the maximum tool iterations limit')
|
|
818
|
+
);
|
|
819
|
+
const lastUserMessage = userMessages[userMessages.length - 1];
|
|
820
|
+
const prompt = lastUserMessage ? lastUserMessage.content : '';
|
|
821
|
+
|
|
822
|
+
// Pass system message and other options
|
|
823
|
+
const engineOptions = {
|
|
824
|
+
maxTokens: options.maxTokens,
|
|
825
|
+
temperature: options.temperature,
|
|
826
|
+
messages: options.messages,
|
|
827
|
+
systemPrompt: options.messages.find(m => m.role === 'system')?.content
|
|
828
|
+
};
|
|
829
|
+
|
|
830
|
+
// Get the engine's query result (async generator)
|
|
831
|
+
const engineStream = engine.query(prompt, engineOptions);
|
|
832
|
+
|
|
833
|
+
// Create a text stream that extracts text from engine messages
|
|
834
|
+
async function* createTextStream() {
|
|
835
|
+
for await (const message of engineStream) {
|
|
836
|
+
if (message.type === 'text' && message.content) {
|
|
837
|
+
yield message.content;
|
|
838
|
+
} else if (typeof message === 'string') {
|
|
839
|
+
// If engine returns plain strings, pass them through
|
|
840
|
+
yield message;
|
|
841
|
+
}
|
|
842
|
+
// Ignore other message types for the text stream
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
// Wrap the engine result to match streamText interface
|
|
847
|
+
return {
|
|
848
|
+
textStream: createTextStream(),
|
|
849
|
+
usage: Promise.resolve({}), // Engine should handle its own usage tracking
|
|
850
|
+
// Add other streamText-compatible properties as needed
|
|
851
|
+
};
|
|
852
|
+
}
|
|
853
|
+
} catch (error) {
|
|
854
|
+
if (this.debug) {
|
|
855
|
+
console.log(`[DEBUG] Failed to use Codex engine, falling back to Vercel:`, error.message);
|
|
856
|
+
}
|
|
857
|
+
// Fall through to use Vercel engine as fallback
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
|
|
727
861
|
// Initialize retry manager if not already created
|
|
728
862
|
if (!this.retryManager) {
|
|
729
863
|
this.retryManager = new RetryManager({
|
|
@@ -908,6 +1042,39 @@ export class ProbeAgent {
|
|
|
908
1042
|
this.clientApiProvider = null;
|
|
909
1043
|
}
|
|
910
1044
|
}
|
|
1045
|
+
|
|
1046
|
+
// Try Codex CLI engine if requested
|
|
1047
|
+
if (this.clientApiProvider === 'codex' || process.env.USE_CODEX === 'true') {
|
|
1048
|
+
try {
|
|
1049
|
+
const { createCodexEngine } = await import('./engines/codex.js');
|
|
1050
|
+
|
|
1051
|
+
// For Codex CLI, use a cleaner system prompt without XML formatting
|
|
1052
|
+
// since it has native MCP support for tools
|
|
1053
|
+
const systemPrompt = this.customPrompt || this.getCodexNativeSystemPrompt();
|
|
1054
|
+
|
|
1055
|
+
this.engine = await createCodexEngine({
|
|
1056
|
+
agent: this, // Pass reference to ProbeAgent for tool access
|
|
1057
|
+
systemPrompt: systemPrompt,
|
|
1058
|
+
customPrompt: this.customPrompt,
|
|
1059
|
+
sessionId: this.options?.sessionId,
|
|
1060
|
+
debug: this.debug,
|
|
1061
|
+
allowedTools: this.allowedTools, // Pass tool filtering configuration
|
|
1062
|
+
model: this.model // Pass model name (e.g., gpt-4o, o3, etc.)
|
|
1063
|
+
});
|
|
1064
|
+
if (this.debug) {
|
|
1065
|
+
console.log('[DEBUG] Using Codex CLI engine with Probe tools');
|
|
1066
|
+
if (this.customPrompt) {
|
|
1067
|
+
console.log('[DEBUG] Using custom prompt/persona');
|
|
1068
|
+
}
|
|
1069
|
+
}
|
|
1070
|
+
return this.engine;
|
|
1071
|
+
} catch (error) {
|
|
1072
|
+
console.warn('[WARNING] Failed to load Codex CLI engine:', error.message);
|
|
1073
|
+
console.warn('[WARNING] Falling back to Vercel AI SDK');
|
|
1074
|
+
this.clientApiProvider = null;
|
|
1075
|
+
}
|
|
1076
|
+
}
|
|
1077
|
+
|
|
911
1078
|
// Default to enhanced Vercel AI SDK (wraps existing logic)
|
|
912
1079
|
const { createEnhancedVercelEngine } = await import('./engines/enhanced-vercel.js');
|
|
913
1080
|
this.engine = createEnhancedVercelEngine(this);
|
|
@@ -917,6 +1084,35 @@ export class ProbeAgent {
|
|
|
917
1084
|
return this.engine;
|
|
918
1085
|
}
|
|
919
1086
|
|
|
1087
|
+
/**
|
|
1088
|
+
* Get session information including thread ID for resumability
|
|
1089
|
+
* @returns {Object} Session info with sessionId, threadId, messageCount
|
|
1090
|
+
*/
|
|
1091
|
+
getSessionInfo() {
|
|
1092
|
+
if (this.engine && this.engine.getSession) {
|
|
1093
|
+
return this.engine.getSession();
|
|
1094
|
+
}
|
|
1095
|
+
return {
|
|
1096
|
+
id: this.sessionId,
|
|
1097
|
+
threadId: null,
|
|
1098
|
+
messageCount: 0
|
|
1099
|
+
};
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
/**
|
|
1103
|
+
* Close the agent and clean up resources (e.g., MCP servers)
|
|
1104
|
+
* @returns {Promise<void>}
|
|
1105
|
+
*/
|
|
1106
|
+
async close() {
|
|
1107
|
+
if (this.engine && this.engine.close) {
|
|
1108
|
+
await this.engine.close();
|
|
1109
|
+
}
|
|
1110
|
+
if (this.mcpBridge) {
|
|
1111
|
+
// Clean up MCP bridge if needed
|
|
1112
|
+
this.mcpBridge = null;
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
|
|
920
1116
|
/**
|
|
921
1117
|
* Process assistant response content and detect/load image references
|
|
922
1118
|
* @param {string} content - The assistant's response content
|
|
@@ -1311,11 +1507,64 @@ export class ProbeAgent {
|
|
|
1311
1507
|
let systemPrompt = '';
|
|
1312
1508
|
|
|
1313
1509
|
// Add persona/role if configured
|
|
1314
|
-
if (this.
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1510
|
+
if (this.customPrompt) {
|
|
1511
|
+
systemPrompt += this.customPrompt + '\n\n';
|
|
1512
|
+
} else if (this.promptType && predefinedPrompts[this.promptType]) {
|
|
1513
|
+
systemPrompt += predefinedPrompts[this.promptType] + '\n\n';
|
|
1514
|
+
} else {
|
|
1515
|
+
// Use default code-explorer prompt
|
|
1516
|
+
systemPrompt += predefinedPrompts['code-explorer'] + '\n\n';
|
|
1517
|
+
}
|
|
1518
|
+
|
|
1519
|
+
// Add high-level instructions about when to use tools
|
|
1520
|
+
systemPrompt += `You have access to powerful code search and analysis tools through MCP:
|
|
1521
|
+
- search: Find code patterns using semantic search
|
|
1522
|
+
- extract: Extract specific code sections with context
|
|
1523
|
+
- query: Use AST patterns for structural code matching
|
|
1524
|
+
- listFiles: Browse directory contents
|
|
1525
|
+
- searchFiles: Find files by name patterns`;
|
|
1526
|
+
|
|
1527
|
+
if (this.enableBash) {
|
|
1528
|
+
systemPrompt += `\n- bash: Execute bash commands for system operations`;
|
|
1529
|
+
}
|
|
1530
|
+
|
|
1531
|
+
systemPrompt += `\n
|
|
1532
|
+
When exploring code:
|
|
1533
|
+
1. Start with search to find relevant code patterns
|
|
1534
|
+
2. Use extract to get detailed context when needed
|
|
1535
|
+
3. Prefer focused, specific searches over broad queries
|
|
1536
|
+
4. Combine multiple tools to build complete understanding`;
|
|
1537
|
+
|
|
1538
|
+
// Add workspace context
|
|
1539
|
+
if (this.allowedFolders && this.allowedFolders.length > 0) {
|
|
1540
|
+
systemPrompt += `\n\nWorkspace: ${this.allowedFolders.join(', ')}`;
|
|
1541
|
+
}
|
|
1542
|
+
|
|
1543
|
+
// Add repository structure if available
|
|
1544
|
+
if (this.fileList) {
|
|
1545
|
+
systemPrompt += `\n\n# Repository Structure\n`;
|
|
1546
|
+
systemPrompt += `You are working with a repository located at: ${this.allowedFolders[0]}\n\n`;
|
|
1547
|
+
systemPrompt += `Here's an overview of the repository structure (showing up to 100 most relevant files):\n\n`;
|
|
1548
|
+
systemPrompt += '```\n' + this.fileList + '\n```\n';
|
|
1549
|
+
}
|
|
1550
|
+
|
|
1551
|
+
return systemPrompt;
|
|
1552
|
+
}
|
|
1553
|
+
|
|
1554
|
+
/**
|
|
1555
|
+
* Get system prompt for Codex CLI (similar to Claude but optimized for Codex)
|
|
1556
|
+
*/
|
|
1557
|
+
getCodexNativeSystemPrompt() {
|
|
1558
|
+
let systemPrompt = '';
|
|
1559
|
+
|
|
1560
|
+
// Add persona/role if configured
|
|
1561
|
+
if (this.customPrompt) {
|
|
1562
|
+
systemPrompt += this.customPrompt + '\n\n';
|
|
1563
|
+
} else if (this.promptType && predefinedPrompts[this.promptType]) {
|
|
1564
|
+
systemPrompt += predefinedPrompts[this.promptType] + '\n\n';
|
|
1565
|
+
} else {
|
|
1566
|
+
// Use default code-explorer prompt
|
|
1567
|
+
systemPrompt += predefinedPrompts['code-explorer'] + '\n\n';
|
|
1319
1568
|
}
|
|
1320
1569
|
|
|
1321
1570
|
// Add high-level instructions about when to use tools
|
|
@@ -1747,8 +1996,9 @@ When troubleshooting:
|
|
|
1747
1996
|
const baseMaxIterations = this.maxIterations || MAX_TOOL_ITERATIONS;
|
|
1748
1997
|
const maxIterations = options.schema ? baseMaxIterations + 4 : baseMaxIterations;
|
|
1749
1998
|
|
|
1750
|
-
// Check if we're using
|
|
1999
|
+
// Check if we're using CLI-based engines which handle their own agentic loop
|
|
1751
2000
|
const isClaudeCode = this.clientApiProvider === 'claude-code' || process.env.USE_CLAUDE_CODE === 'true';
|
|
2001
|
+
const isCodex = this.clientApiProvider === 'codex' || process.env.USE_CODEX === 'true';
|
|
1752
2002
|
|
|
1753
2003
|
if (isClaudeCode) {
|
|
1754
2004
|
// For Claude Code, bypass the tool loop entirely - it handles its own internal dialogue
|
|
@@ -1820,6 +2070,77 @@ When troubleshooting:
|
|
|
1820
2070
|
}
|
|
1821
2071
|
}
|
|
1822
2072
|
|
|
2073
|
+
// Handle Codex engine (same pattern as Claude Code)
|
|
2074
|
+
if (isCodex) {
|
|
2075
|
+
// For Codex, bypass the tool loop entirely - it handles its own internal dialogue
|
|
2076
|
+
if (this.debug) {
|
|
2077
|
+
console.log(`[DEBUG] Using Codex engine - bypassing tool loop (black box mode)`);
|
|
2078
|
+
console.log(`[DEBUG] Sending question directly to Codex: ${message.substring(0, 100)}...`);
|
|
2079
|
+
}
|
|
2080
|
+
|
|
2081
|
+
// Send the message directly to Codex and collect the response
|
|
2082
|
+
try {
|
|
2083
|
+
const engine = await this.getEngine();
|
|
2084
|
+
if (engine && engine.query) {
|
|
2085
|
+
let assistantResponseContent = '';
|
|
2086
|
+
let toolBatch = null;
|
|
2087
|
+
|
|
2088
|
+
// Query Codex directly with the message and schema
|
|
2089
|
+
for await (const chunk of engine.query(message, options)) {
|
|
2090
|
+
if (chunk.type === 'text' && chunk.content) {
|
|
2091
|
+
assistantResponseContent += chunk.content;
|
|
2092
|
+
if (options.onStream) {
|
|
2093
|
+
options.onStream(chunk.content);
|
|
2094
|
+
}
|
|
2095
|
+
} else if (chunk.type === 'toolBatch' && chunk.tools) {
|
|
2096
|
+
// Store tool batch for processing after response
|
|
2097
|
+
toolBatch = chunk.tools;
|
|
2098
|
+
if (this.debug) {
|
|
2099
|
+
console.log(`[DEBUG] Received batch of ${chunk.tools.length} tool events from Codex`);
|
|
2100
|
+
}
|
|
2101
|
+
} else if (chunk.type === 'error') {
|
|
2102
|
+
throw chunk.error;
|
|
2103
|
+
}
|
|
2104
|
+
}
|
|
2105
|
+
|
|
2106
|
+
// Emit tool events after response is complete (batch mode)
|
|
2107
|
+
if (toolBatch && toolBatch.length > 0 && this.events) {
|
|
2108
|
+
if (this.debug) {
|
|
2109
|
+
console.log(`[DEBUG] Emitting ${toolBatch.length} tool events from Codex batch`);
|
|
2110
|
+
}
|
|
2111
|
+
for (const toolEvent of toolBatch) {
|
|
2112
|
+
this.events.emit('toolCall', toolEvent);
|
|
2113
|
+
}
|
|
2114
|
+
}
|
|
2115
|
+
|
|
2116
|
+
// Update history with the exchange
|
|
2117
|
+
this.history.push(userMessage);
|
|
2118
|
+
this.history.push({
|
|
2119
|
+
role: 'assistant',
|
|
2120
|
+
content: assistantResponseContent
|
|
2121
|
+
});
|
|
2122
|
+
|
|
2123
|
+
// Store conversation history
|
|
2124
|
+
// TODO: storeConversationHistory is not yet implemented for Codex
|
|
2125
|
+
// await this.storeConversationHistory(this.history, oldHistoryLength);
|
|
2126
|
+
|
|
2127
|
+
// Emit completion hook
|
|
2128
|
+
await this.hooks.emit(HOOK_TYPES.COMPLETION, {
|
|
2129
|
+
sessionId: this.sessionId,
|
|
2130
|
+
prompt: message,
|
|
2131
|
+
response: assistantResponseContent
|
|
2132
|
+
});
|
|
2133
|
+
|
|
2134
|
+
return assistantResponseContent;
|
|
2135
|
+
}
|
|
2136
|
+
} catch (error) {
|
|
2137
|
+
if (this.debug) {
|
|
2138
|
+
console.error('[DEBUG] Codex error:', error);
|
|
2139
|
+
}
|
|
2140
|
+
throw error;
|
|
2141
|
+
}
|
|
2142
|
+
}
|
|
2143
|
+
|
|
1823
2144
|
if (this.debug) {
|
|
1824
2145
|
console.log(`[DEBUG] Starting agentic flow for question: ${message.substring(0, 100)}...`);
|
|
1825
2146
|
if (options.schema) {
|
|
@@ -1827,7 +2148,7 @@ When troubleshooting:
|
|
|
1827
2148
|
}
|
|
1828
2149
|
}
|
|
1829
2150
|
|
|
1830
|
-
// Tool iteration loop (only for non-
|
|
2151
|
+
// Tool iteration loop (only for non-CLI engines like Vercel/Anthropic/OpenAI)
|
|
1831
2152
|
while (currentIteration < maxIterations && !completionAttempted) {
|
|
1832
2153
|
currentIteration++;
|
|
1833
2154
|
if (this.cancelled) throw new Error('Request was cancelled by the user');
|