@probelabs/probe-chat 0.6.0-rc191 → 0.6.0-rc197
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/index.js +52 -38
- package/package.json +3 -3
- package/probeChat.js +111 -7
package/index.js
CHANGED
|
@@ -427,11 +427,6 @@ export function main() {
|
|
|
427
427
|
|
|
428
428
|
// --- Non-Interactive Mode ---
|
|
429
429
|
if (isNonInteractive) {
|
|
430
|
-
if (!hasApiKeys) {
|
|
431
|
-
logError(chalk.red('No API key provided. Please set ANTHROPIC_API_KEY, OPENAI_API_KEY, or GOOGLE_API_KEY environment variable.'));
|
|
432
|
-
process.exit(1);
|
|
433
|
-
}
|
|
434
|
-
|
|
435
430
|
let chat;
|
|
436
431
|
try {
|
|
437
432
|
// Pass session ID if provided, ProbeChat generates one otherwise
|
|
@@ -445,8 +440,6 @@ export function main() {
|
|
|
445
440
|
bashConfig: bashConfig,
|
|
446
441
|
completionPrompt: completionPrompt
|
|
447
442
|
});
|
|
448
|
-
// Model/Provider info is logged via logInfo above if debug enabled
|
|
449
|
-
logInfo(chalk.blue(`Using Session ID: ${chat.getSessionId()}`)); // Log the actual session ID being used
|
|
450
443
|
} catch (error) {
|
|
451
444
|
logError(chalk.red(`Initializing chat failed: ${error.message}`));
|
|
452
445
|
process.exit(1);
|
|
@@ -468,6 +461,19 @@ export function main() {
|
|
|
468
461
|
// Async function to handle the single chat request
|
|
469
462
|
const runNonInteractiveChat = async () => {
|
|
470
463
|
try {
|
|
464
|
+
// Initialize the chat (handles API key validation, claude-code/codex fallback)
|
|
465
|
+
await chat.initialize();
|
|
466
|
+
|
|
467
|
+
// Validate provider after initialization
|
|
468
|
+
const providerInfo = chat.getProviderInfo();
|
|
469
|
+
if (providerInfo.provider === 'unknown' || providerInfo.provider === 'uninitialized') {
|
|
470
|
+
throw new Error('No valid AI provider available. Please set an API key or install claude/codex CLI.');
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
// Log provider info after successful initialization
|
|
474
|
+
logInfo(chalk.green(`Using provider: ${providerInfo.provider} with model: ${providerInfo.model}`));
|
|
475
|
+
logInfo(chalk.blue(`Using Session ID: ${chat.getSessionId()}`));
|
|
476
|
+
|
|
471
477
|
// Get message from command line argument or stdin
|
|
472
478
|
let message = options.message;
|
|
473
479
|
|
|
@@ -533,16 +539,6 @@ export function main() {
|
|
|
533
539
|
// --- Interactive CLI Mode ---
|
|
534
540
|
// (This block only runs if not non-interactive and not web mode)
|
|
535
541
|
|
|
536
|
-
if (!hasApiKeys) {
|
|
537
|
-
// Use logError and standard console.log for setup instructions
|
|
538
|
-
logError(chalk.red('No API key provided. Please set ANTHROPIC_API_KEY, OPENAI_API_KEY, or GOOGLE_API_KEY environment variable.'));
|
|
539
|
-
console.log(chalk.cyan('You can find these instructions in the .env.example file:'));
|
|
540
|
-
console.log(chalk.cyan('1. Create a .env file by copying .env.example'));
|
|
541
|
-
console.log(chalk.cyan('2. Add your API key to the .env file'));
|
|
542
|
-
console.log(chalk.cyan('3. Restart the application'));
|
|
543
|
-
process.exit(1);
|
|
544
|
-
}
|
|
545
|
-
|
|
546
542
|
// Initialize ProbeChat for CLI mode
|
|
547
543
|
let chat;
|
|
548
544
|
try {
|
|
@@ -557,26 +553,44 @@ export function main() {
|
|
|
557
553
|
bashConfig: bashConfig,
|
|
558
554
|
completionPrompt: completionPrompt
|
|
559
555
|
});
|
|
560
|
-
|
|
561
|
-
// Log model/provider info using logInfo
|
|
562
|
-
if (chat.apiType === 'anthropic') {
|
|
563
|
-
logInfo(chalk.green(`Using Anthropic API with model: ${chat.model}`));
|
|
564
|
-
} else if (chat.apiType === 'openai') {
|
|
565
|
-
logInfo(chalk.green(`Using OpenAI API with model: ${chat.model}`));
|
|
566
|
-
} else if (chat.apiType === 'google') {
|
|
567
|
-
logInfo(chalk.green(`Using Google API with model: ${chat.model}`));
|
|
568
|
-
}
|
|
569
|
-
|
|
570
|
-
logInfo(chalk.blue(`Session ID: ${chat.getSessionId()}`));
|
|
571
|
-
logInfo(chalk.cyan('Type "exit" or "quit" to end the chat'));
|
|
572
|
-
logInfo(chalk.cyan('Type "usage" to see token usage statistics'));
|
|
573
|
-
logInfo(chalk.cyan('Type "clear" to clear the chat history'));
|
|
574
|
-
logInfo(chalk.cyan('-------------------------------------------'));
|
|
575
556
|
} catch (error) {
|
|
576
|
-
logError(chalk.red(`Error
|
|
557
|
+
logError(chalk.red(`Error creating chat instance: ${error.message}`));
|
|
577
558
|
process.exit(1);
|
|
578
559
|
}
|
|
579
560
|
|
|
561
|
+
// Async initialization wrapper for interactive mode
|
|
562
|
+
async function initializeAndStartChat() {
|
|
563
|
+
try {
|
|
564
|
+
// Initialize the chat (handles API key validation, claude-code/codex fallback)
|
|
565
|
+
await chat.initialize();
|
|
566
|
+
|
|
567
|
+
// Validate provider after initialization
|
|
568
|
+
const providerInfo = chat.getProviderInfo();
|
|
569
|
+
if (providerInfo.provider === 'unknown' || providerInfo.provider === 'uninitialized') {
|
|
570
|
+
throw new Error('No valid AI provider available. Please set an API key or install claude/codex CLI.');
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
// Log provider info after successful initialization
|
|
574
|
+
if (providerInfo.isCliProvider) {
|
|
575
|
+
logInfo(chalk.green(`Using ${providerInfo.provider} CLI`));
|
|
576
|
+
} else {
|
|
577
|
+
logInfo(chalk.green(`Using ${providerInfo.provider} API with model: ${providerInfo.model}`));
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
logInfo(chalk.blue(`Session ID: ${chat.getSessionId()}`));
|
|
581
|
+
logInfo(chalk.cyan('Type "exit" or "quit" to end the chat'));
|
|
582
|
+
logInfo(chalk.cyan('Type "usage" to see token usage statistics'));
|
|
583
|
+
logInfo(chalk.cyan('Type "clear" to clear the chat history'));
|
|
584
|
+
logInfo(chalk.cyan('-------------------------------------------'));
|
|
585
|
+
|
|
586
|
+
// Start the interactive chat loop
|
|
587
|
+
await runInteractiveChat();
|
|
588
|
+
} catch (error) {
|
|
589
|
+
logError(chalk.red(`Error initializing chat: ${error.message}`));
|
|
590
|
+
process.exit(1);
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
|
|
580
594
|
// Format AI response for interactive mode
|
|
581
595
|
function formatResponseInteractive(response) {
|
|
582
596
|
// Check if response is a structured object with response and tokenUsage properties
|
|
@@ -598,7 +612,7 @@ export function main() {
|
|
|
598
612
|
}
|
|
599
613
|
|
|
600
614
|
// Main interactive chat loop
|
|
601
|
-
async function
|
|
615
|
+
async function runInteractiveChat() {
|
|
602
616
|
while (true) {
|
|
603
617
|
const { message } = await inquirer.prompt([
|
|
604
618
|
{
|
|
@@ -613,7 +627,7 @@ export function main() {
|
|
|
613
627
|
logInfo(chalk.yellow('Goodbye!'));
|
|
614
628
|
break;
|
|
615
629
|
} else if (message.toLowerCase() === 'usage') {
|
|
616
|
-
const usage = chat.
|
|
630
|
+
const usage = chat.getUsageSummary();
|
|
617
631
|
const display = new TokenUsageDisplay();
|
|
618
632
|
const formatted = display.format(usage);
|
|
619
633
|
|
|
@@ -630,9 +644,9 @@ export function main() {
|
|
|
630
644
|
process.stdout.write('\x1B]0;Context: ' + formatted.contextWindow + '\x07');
|
|
631
645
|
continue;
|
|
632
646
|
} else if (message.toLowerCase() === 'clear') {
|
|
633
|
-
|
|
647
|
+
chat.clearHistory();
|
|
634
648
|
logInfo(chalk.yellow('Chat history cleared'));
|
|
635
|
-
logInfo(chalk.blue(`New session ID: ${
|
|
649
|
+
logInfo(chalk.blue(`New session ID: ${chat.getSessionId()}`));
|
|
636
650
|
continue;
|
|
637
651
|
}
|
|
638
652
|
|
|
@@ -656,7 +670,7 @@ export function main() {
|
|
|
656
670
|
}
|
|
657
671
|
}
|
|
658
672
|
|
|
659
|
-
|
|
673
|
+
initializeAndStartChat().catch((error) => {
|
|
660
674
|
logError(chalk.red(`Fatal error in interactive chat: ${error.message}`));
|
|
661
675
|
process.exit(1);
|
|
662
676
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@probelabs/probe-chat",
|
|
3
|
-
"version": "0.6.0-
|
|
3
|
+
"version": "0.6.0-rc197",
|
|
4
4
|
"description": "CLI and web interface for Probe code search (formerly @probelabs/probe-web and @probelabs/probe-chat)",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -32,13 +32,13 @@
|
|
|
32
32
|
"@ai-sdk/anthropic": "^2.0.8",
|
|
33
33
|
"@ai-sdk/google": "^2.0.14",
|
|
34
34
|
"@ai-sdk/openai": "^2.0.10",
|
|
35
|
-
"@
|
|
35
|
+
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
36
36
|
"@opentelemetry/api": "^1.9.0",
|
|
37
37
|
"@opentelemetry/exporter-trace-otlp-http": "^0.203.0",
|
|
38
38
|
"@opentelemetry/resources": "^2.0.1",
|
|
39
39
|
"@opentelemetry/sdk-node": "^0.203.0",
|
|
40
40
|
"@opentelemetry/semantic-conventions": "^1.36.0",
|
|
41
|
-
"@
|
|
41
|
+
"@probelabs/probe": "latest",
|
|
42
42
|
"ai": "^5.0.0",
|
|
43
43
|
"chalk": "^5.3.0",
|
|
44
44
|
"commander": "^11.1.0",
|
package/probeChat.js
CHANGED
|
@@ -266,6 +266,9 @@ export class ProbeChat {
|
|
|
266
266
|
constructor(options = {}) {
|
|
267
267
|
this.isNonInteractive = options.isNonInteractive || process.env.PROBE_NON_INTERACTIVE === '1';
|
|
268
268
|
this.debug = options.debug || process.env.DEBUG_CHAT === '1';
|
|
269
|
+
this._initialized = false;
|
|
270
|
+
this._initializing = null; // Promise to prevent concurrent initialization
|
|
271
|
+
this._initError = null; // Store initialization error to prevent infinite retries
|
|
269
272
|
|
|
270
273
|
// Initialize ProbeAgent with MCP support
|
|
271
274
|
const agentOptions = {
|
|
@@ -283,11 +286,81 @@ export class ProbeChat {
|
|
|
283
286
|
|
|
284
287
|
if (this.debug) {
|
|
285
288
|
console.log(`[DEBUG] ProbeChat initialized with MCP ${agentOptions.enableMcp ? 'enabled' : 'disabled'}`);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Initialize the chat agent asynchronously.
|
|
294
|
+
*
|
|
295
|
+
* This method is safe to call multiple times - subsequent calls will return
|
|
296
|
+
* immediately if already initialized, or wait for the ongoing initialization.
|
|
297
|
+
*
|
|
298
|
+
* If initialization fails, the error is stored and rethrown on subsequent calls
|
|
299
|
+
* to prevent infinite retry loops.
|
|
300
|
+
*
|
|
301
|
+
* This initializes the ProbeAgent which handles:
|
|
302
|
+
* - API key validation
|
|
303
|
+
* - Auto-detection of claude-code or codex CLI as fallback
|
|
304
|
+
* - MCP initialization
|
|
305
|
+
* - History loading from storage
|
|
306
|
+
*
|
|
307
|
+
* @returns {Promise<void>}
|
|
308
|
+
* @throws {Error} If no valid AI provider is available
|
|
309
|
+
*/
|
|
310
|
+
async initialize() {
|
|
311
|
+
// If previous initialization failed, rethrow the stored error
|
|
312
|
+
if (this._initError) {
|
|
313
|
+
throw this._initError;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// Return existing initialization promise if in progress (prevents concurrent init)
|
|
317
|
+
if (this._initializing) {
|
|
318
|
+
return this._initializing;
|
|
319
|
+
}
|
|
286
320
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
321
|
+
// Already initialized successfully
|
|
322
|
+
if (this._initialized) {
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
this._initializing = (async () => {
|
|
327
|
+
try {
|
|
328
|
+
await this.agent.initialize();
|
|
329
|
+
|
|
330
|
+
// Mark as initialized immediately after agent init succeeds
|
|
331
|
+
this._initialized = true;
|
|
332
|
+
|
|
333
|
+
// Debug logging wrapped in try-catch to not affect initialization state
|
|
334
|
+
if (this.debug) {
|
|
335
|
+
try {
|
|
336
|
+
const provider = this.agent.clientApiProvider || this.agent.apiType || 'unknown';
|
|
337
|
+
console.log(`[DEBUG] ProbeChat agent initialized with provider: ${provider}`);
|
|
338
|
+
this.logAvailableTools();
|
|
339
|
+
} catch (logError) {
|
|
340
|
+
console.error('[DEBUG] Logging failed:', logError.message);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
} catch (error) {
|
|
344
|
+
// Store error to prevent infinite retry loops
|
|
345
|
+
this._initError = error;
|
|
346
|
+
throw error;
|
|
347
|
+
} finally {
|
|
348
|
+
// Always clear the initializing promise
|
|
349
|
+
this._initializing = null;
|
|
350
|
+
}
|
|
351
|
+
})();
|
|
352
|
+
|
|
353
|
+
return this._initializing;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* Ensure the agent is initialized before use.
|
|
358
|
+
* Called automatically by chat() - you don't need to call this directly.
|
|
359
|
+
* @private
|
|
360
|
+
*/
|
|
361
|
+
async _ensureInitialized() {
|
|
362
|
+
if (!this._initialized) {
|
|
363
|
+
await this.initialize();
|
|
291
364
|
}
|
|
292
365
|
}
|
|
293
366
|
|
|
@@ -333,18 +406,27 @@ export class ProbeChat {
|
|
|
333
406
|
}
|
|
334
407
|
|
|
335
408
|
/**
|
|
336
|
-
*
|
|
409
|
+
* Send a chat message and get AI response.
|
|
410
|
+
*
|
|
411
|
+
* This method automatically initializes the agent on first call if not already
|
|
412
|
+
* initialized. For explicit control over initialization (e.g., to handle errors
|
|
413
|
+
* separately), call initialize() first.
|
|
414
|
+
*
|
|
337
415
|
* @param {string} message - The user's question
|
|
338
416
|
* @param {Object} [options] - Optional configuration
|
|
339
417
|
* @param {string} [options.schema] - JSON schema for structured output
|
|
340
418
|
* @param {Array} [options.images] - Array of image data (base64 strings or URLs)
|
|
341
|
-
* @returns {Promise<
|
|
419
|
+
* @returns {Promise<Object>} - The AI response with token usage
|
|
420
|
+
* @throws {Error} If message is empty or if no valid AI provider is available
|
|
342
421
|
*/
|
|
343
422
|
async chat(message, options = {}) {
|
|
344
423
|
if (!message || typeof message !== 'string' || message.trim().length === 0) {
|
|
345
424
|
throw new Error('Message is required and must be a non-empty string');
|
|
346
425
|
}
|
|
347
426
|
|
|
427
|
+
// Ensure the agent is initialized (handles API key validation, claude-code/codex fallback)
|
|
428
|
+
await this._ensureInitialized();
|
|
429
|
+
|
|
348
430
|
// Extract images from the message text if not provided in options
|
|
349
431
|
let images = options.images || [];
|
|
350
432
|
let cleanedMessage = message;
|
|
@@ -379,6 +461,26 @@ export class ProbeChat {
|
|
|
379
461
|
return this.agent.sessionId;
|
|
380
462
|
}
|
|
381
463
|
|
|
464
|
+
/**
|
|
465
|
+
* Get provider information
|
|
466
|
+
* @returns {Object} - Provider info with type and model
|
|
467
|
+
*/
|
|
468
|
+
getProviderInfo() {
|
|
469
|
+
return {
|
|
470
|
+
provider: this.agent.clientApiProvider || this.agent.apiType || 'unknown',
|
|
471
|
+
model: this.agent.model || 'unknown',
|
|
472
|
+
isCliProvider: ['claude-code', 'codex'].includes(this.agent.clientApiProvider || this.agent.apiType)
|
|
473
|
+
};
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
/**
|
|
477
|
+
* Check if the agent is initialized
|
|
478
|
+
* @returns {boolean}
|
|
479
|
+
*/
|
|
480
|
+
isInitialized() {
|
|
481
|
+
return this._initialized;
|
|
482
|
+
}
|
|
483
|
+
|
|
382
484
|
/**
|
|
383
485
|
* Get usage summary for the current session
|
|
384
486
|
*/
|
|
@@ -387,11 +489,13 @@ export class ProbeChat {
|
|
|
387
489
|
}
|
|
388
490
|
|
|
389
491
|
/**
|
|
390
|
-
* Clear conversation history
|
|
492
|
+
* Clear conversation history and return the new session ID
|
|
493
|
+
* @returns {string} - The new session ID
|
|
391
494
|
*/
|
|
392
495
|
clearHistory() {
|
|
393
496
|
this.agent.clearHistory();
|
|
394
497
|
this.tokenUsage.clear();
|
|
498
|
+
return this.agent.sessionId;
|
|
395
499
|
}
|
|
396
500
|
|
|
397
501
|
/**
|