@probelabs/probe-chat 0.6.0-rc56 → 0.6.0-rc58
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.html +69 -4
- package/index.js +31 -24
- package/package.json +2 -2
- package/webServer.js +24 -31
package/index.html
CHANGED
|
@@ -1735,15 +1735,67 @@
|
|
|
1735
1735
|
const response = await fetch(`/api/session/${sessionId}/history`);
|
|
1736
1736
|
const data = await response.json();
|
|
1737
1737
|
|
|
1738
|
+
console.log(`[DEBUG] Session restoration data:`, {
|
|
1739
|
+
exists: data.exists,
|
|
1740
|
+
historyLength: data.history ? data.history.length : 'null/undefined',
|
|
1741
|
+
condition: data.exists && data.history && data.history.length > 0
|
|
1742
|
+
});
|
|
1743
|
+
|
|
1738
1744
|
if (data.exists && data.history && data.history.length > 0) {
|
|
1739
1745
|
console.log(`Restored ${data.history.length} messages for session: ${sessionId}`);
|
|
1740
1746
|
|
|
1747
|
+
// Count message types for debugging
|
|
1748
|
+
const messageTypes = {};
|
|
1749
|
+
data.history.forEach(msg => {
|
|
1750
|
+
messageTypes[msg.role] = (messageTypes[msg.role] || 0) + 1;
|
|
1751
|
+
});
|
|
1752
|
+
console.log(`[DEBUG] Message types to restore:`, messageTypes);
|
|
1753
|
+
|
|
1741
1754
|
// Render restored messages
|
|
1742
|
-
|
|
1755
|
+
let currentAiMessage = null;
|
|
1756
|
+
data.history.forEach((message, index) => {
|
|
1757
|
+
console.log(`[DEBUG] Processing message ${index + 1}/${data.history.length}: role=${message.role}`);
|
|
1743
1758
|
if (message.role === 'user') {
|
|
1744
|
-
|
|
1759
|
+
// Ensure images is always an array
|
|
1760
|
+
const images = Array.isArray(message.images) ? message.images : [];
|
|
1761
|
+
addUserMessage(message.content, images);
|
|
1762
|
+
currentAiMessage = null; // Reset for new conversation turn
|
|
1745
1763
|
} else if (message.role === 'assistant') {
|
|
1746
1764
|
addAssistantMessage(message.content);
|
|
1765
|
+
// Get the most recent AI message for tool call rendering
|
|
1766
|
+
const messagesDiv = document.getElementById('messages');
|
|
1767
|
+
const aiMessages = messagesDiv.querySelectorAll('.ai-message');
|
|
1768
|
+
currentAiMessage = aiMessages[aiMessages.length - 1];
|
|
1769
|
+
} else if (message.role === 'toolCall') {
|
|
1770
|
+
console.log(`[DEBUG] Rendering toolCall message`);
|
|
1771
|
+
// Handle tool call messages during restoration
|
|
1772
|
+
try {
|
|
1773
|
+
// Parse the tool call from the stored message
|
|
1774
|
+
const toolCall = {
|
|
1775
|
+
name: message.metadata?.name || 'unknown',
|
|
1776
|
+
args: message.metadata?.args || {},
|
|
1777
|
+
timestamp: message.timestamp,
|
|
1778
|
+
status: 'completed'
|
|
1779
|
+
};
|
|
1780
|
+
|
|
1781
|
+
// If we have a current AI message, add the tool call to it
|
|
1782
|
+
if (currentAiMessage) {
|
|
1783
|
+
addToolCallToMessage(currentAiMessage, toolCall);
|
|
1784
|
+
} else {
|
|
1785
|
+
console.log(`[DEBUG] No current AI message for tool call, creating temporary message`);
|
|
1786
|
+
// Create a temporary AI message for the tool call
|
|
1787
|
+
const tempDiv = document.createElement('div');
|
|
1788
|
+
tempDiv.className = 'ai-message';
|
|
1789
|
+
const messagesDiv = document.getElementById('messages');
|
|
1790
|
+
messagesDiv.appendChild(tempDiv);
|
|
1791
|
+
addToolCallToMessage(tempDiv, toolCall);
|
|
1792
|
+
currentAiMessage = tempDiv;
|
|
1793
|
+
}
|
|
1794
|
+
} catch (error) {
|
|
1795
|
+
console.error('[DEBUG] Error rendering tool call:', error, message);
|
|
1796
|
+
}
|
|
1797
|
+
} else {
|
|
1798
|
+
console.log(`[DEBUG] Skipping message with role: ${message.role}`);
|
|
1747
1799
|
}
|
|
1748
1800
|
});
|
|
1749
1801
|
|
|
@@ -1778,12 +1830,17 @@
|
|
|
1778
1830
|
}
|
|
1779
1831
|
}, 100); // Small delay to ensure DOM is fully updated
|
|
1780
1832
|
} else {
|
|
1781
|
-
console.log(`No history found for session: ${sessionId}
|
|
1833
|
+
console.log(`[DEBUG] No history found for session: ${sessionId}`, {
|
|
1834
|
+
exists: data.exists,
|
|
1835
|
+
historyExists: !!data.history,
|
|
1836
|
+
historyLength: data.history ? data.history.length : 'N/A'
|
|
1837
|
+
});
|
|
1782
1838
|
// Show user-friendly message for session not found
|
|
1783
1839
|
showSessionNotFoundMessage(sessionId);
|
|
1784
1840
|
}
|
|
1785
1841
|
} catch (error) {
|
|
1786
|
-
console.error('Error restoring session history:', error);
|
|
1842
|
+
console.error('[DEBUG] Error restoring session history:', error);
|
|
1843
|
+
console.error('[DEBUG] Error stack:', error.stack);
|
|
1787
1844
|
// Show error message for network or other errors
|
|
1788
1845
|
showSessionNotFoundMessage(sessionId);
|
|
1789
1846
|
}
|
|
@@ -2571,6 +2628,14 @@
|
|
|
2571
2628
|
}
|
|
2572
2629
|
|
|
2573
2630
|
toolDescription = `Extracting code from ${filePath}${lineInfo}`;
|
|
2631
|
+
} else if (toolCall.name === 'searchFiles') {
|
|
2632
|
+
const pattern = toolCall.args.pattern || '';
|
|
2633
|
+
const directory = toolCall.args.directory || '.';
|
|
2634
|
+
toolDescription = `Searching for files matching "${pattern}"${directory !== '.' ? ` in ${directory}` : ''}`;
|
|
2635
|
+
} else if (toolCall.name === 'listFiles') {
|
|
2636
|
+
const directory = toolCall.args.directory || '.';
|
|
2637
|
+
const pattern = toolCall.args.pattern || '';
|
|
2638
|
+
toolDescription = `Listing files${pattern ? ` matching "${pattern}"` : ''}${directory !== '.' ? ` in ${directory}` : ''}`;
|
|
2574
2639
|
} else {
|
|
2575
2640
|
toolDescription = `Using ${toolCall.name} tool`;
|
|
2576
2641
|
}
|
package/index.js
CHANGED
|
@@ -58,6 +58,7 @@ export function main() {
|
|
|
58
58
|
.option('-p, --port <port>', 'Port to run web server on (default: 8080)')
|
|
59
59
|
.option('-m, --message <message>', 'Send a single message and exit (non-interactive mode)')
|
|
60
60
|
.option('-s, --session-id <sessionId>', 'Specify a session ID for the chat (optional)')
|
|
61
|
+
.option('--images <urls>', 'Comma-separated list of image URLs to include in the message')
|
|
61
62
|
.option('--json', 'Output the response as JSON in non-interactive mode')
|
|
62
63
|
.option('--max-iterations <number>', 'Maximum number of tool iterations allowed (default: 30)')
|
|
63
64
|
.option('--prompt <value>', 'Use a custom prompt (values: architect, code-review, support, path to a file, or arbitrary string)')
|
|
@@ -76,6 +77,13 @@ export function main() {
|
|
|
76
77
|
const options = program.opts();
|
|
77
78
|
const pathArg = program.args[0];
|
|
78
79
|
|
|
80
|
+
// Parse image URLs if provided
|
|
81
|
+
let imageUrls = [];
|
|
82
|
+
if (options.images) {
|
|
83
|
+
imageUrls = options.images.split(',').map(url => url.trim()).filter(url => url.length > 0);
|
|
84
|
+
console.log(`Using ${imageUrls.length} image(s):`, imageUrls);
|
|
85
|
+
}
|
|
86
|
+
|
|
79
87
|
// --- Logging Configuration ---
|
|
80
88
|
const isPipedInput = process.env.PROBE_STDIN_PIPED === '1';
|
|
81
89
|
const isNonInteractive = !!options.message || isPipedInput;
|
|
@@ -315,6 +323,27 @@ export function main() {
|
|
|
315
323
|
const googleApiKey = process.env.GOOGLE_API_KEY;
|
|
316
324
|
const hasApiKeys = !!(anthropicApiKey || openaiApiKey || googleApiKey);
|
|
317
325
|
|
|
326
|
+
// --- Web Mode (check before non-interactive to override) ---
|
|
327
|
+
if (options.web) {
|
|
328
|
+
if (!hasApiKeys) {
|
|
329
|
+
// Use logWarn for web mode warning
|
|
330
|
+
logWarn(chalk.yellow('Warning: No API key provided. The web interface will show instructions on how to set up API keys.'));
|
|
331
|
+
}
|
|
332
|
+
// Import and start web server
|
|
333
|
+
import('./webServer.js')
|
|
334
|
+
.then(async module => {
|
|
335
|
+
const { startWebServer } = module;
|
|
336
|
+
logInfo(`Starting web server on port ${process.env.PORT || 8080}...`);
|
|
337
|
+
await startWebServer(version, hasApiKeys, { allowEdit: options.allowEdit });
|
|
338
|
+
})
|
|
339
|
+
.catch(error => {
|
|
340
|
+
logError(chalk.red(`Error starting web server: ${error.message}`));
|
|
341
|
+
process.exit(1);
|
|
342
|
+
});
|
|
343
|
+
return; // Exit main function
|
|
344
|
+
}
|
|
345
|
+
// --- End Web Mode ---
|
|
346
|
+
|
|
318
347
|
// --- Non-Interactive Mode ---
|
|
319
348
|
if (isNonInteractive) {
|
|
320
349
|
if (!hasApiKeys) {
|
|
@@ -370,7 +399,7 @@ export function main() {
|
|
|
370
399
|
}
|
|
371
400
|
|
|
372
401
|
logInfo('Sending message...'); // Log only if debug
|
|
373
|
-
const result = await chat.chat(message, chat.getSessionId()); // Use the chat's current session ID
|
|
402
|
+
const result = await chat.chat(message, chat.getSessionId(), null, imageUrls); // Use the chat's current session ID and pass images
|
|
374
403
|
|
|
375
404
|
if (result && typeof result === 'object' && result.response !== undefined) {
|
|
376
405
|
if (options.json) {
|
|
@@ -417,28 +446,6 @@ export function main() {
|
|
|
417
446
|
// --- End Non-Interactive Mode ---
|
|
418
447
|
|
|
419
448
|
|
|
420
|
-
// --- Web Mode ---
|
|
421
|
-
if (options.web) {
|
|
422
|
-
if (!hasApiKeys) {
|
|
423
|
-
// Use logWarn for web mode warning
|
|
424
|
-
logWarn(chalk.yellow('Warning: No API key provided. The web interface will show instructions on how to set up API keys.'));
|
|
425
|
-
}
|
|
426
|
-
// Import and start web server
|
|
427
|
-
import('./webServer.js')
|
|
428
|
-
.then(module => {
|
|
429
|
-
const { startWebServer } = module;
|
|
430
|
-
logInfo(`Starting web server on port ${process.env.PORT || 8080}...`);
|
|
431
|
-
startWebServer(version, hasApiKeys, { allowEdit: options.allowEdit });
|
|
432
|
-
})
|
|
433
|
-
.catch(error => {
|
|
434
|
-
logError(chalk.red(`Error starting web server: ${error.message}`));
|
|
435
|
-
process.exit(1);
|
|
436
|
-
});
|
|
437
|
-
return; // Exit main function
|
|
438
|
-
}
|
|
439
|
-
// --- End Web Mode ---
|
|
440
|
-
|
|
441
|
-
|
|
442
449
|
// --- Interactive CLI Mode ---
|
|
443
450
|
// (This block only runs if not non-interactive and not web mode)
|
|
444
451
|
|
|
@@ -544,7 +551,7 @@ export function main() {
|
|
|
544
551
|
|
|
545
552
|
const spinner = ora('Thinking...').start(); // Spinner is ok for interactive mode
|
|
546
553
|
try {
|
|
547
|
-
const result = await chat.chat(message); // Uses internal session ID
|
|
554
|
+
const result = await chat.chat(message, null, null, imageUrls); // Uses internal session ID and pass images
|
|
548
555
|
spinner.stop();
|
|
549
556
|
|
|
550
557
|
logInfo(chalk.green('Assistant:'));
|
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-rc58",
|
|
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,7 +32,7 @@
|
|
|
32
32
|
"@ai-sdk/anthropic": "^1.2.0",
|
|
33
33
|
"@ai-sdk/google": "^1.2.1",
|
|
34
34
|
"@ai-sdk/openai": "^1.3.0",
|
|
35
|
-
"@buger/probe": "^0.6.0-
|
|
35
|
+
"@buger/probe": "^0.6.0-rc58",
|
|
36
36
|
"@opentelemetry/api": "^1.9.0",
|
|
37
37
|
"@opentelemetry/exporter-trace-otlp-http": "^0.203.0",
|
|
38
38
|
"@opentelemetry/resources": "^2.0.1",
|
package/webServer.js
CHANGED
|
@@ -5,7 +5,7 @@ import { readFileSync, existsSync } from 'fs';
|
|
|
5
5
|
import { resolve, dirname, join } from 'path';
|
|
6
6
|
import { fileURLToPath } from 'url';
|
|
7
7
|
import { randomUUID } from 'crypto';
|
|
8
|
-
import {
|
|
8
|
+
import { ChatSessionManager } from './ChatSessionManager.js';
|
|
9
9
|
import { TokenUsageDisplay } from './tokenUsageDisplay.js';
|
|
10
10
|
import { authMiddleware, withAuth } from './auth.js';
|
|
11
11
|
import {
|
|
@@ -32,7 +32,7 @@ let globalStorage = null;
|
|
|
32
32
|
const chatSessions = new Map();
|
|
33
33
|
|
|
34
34
|
/**
|
|
35
|
-
* Retrieve or create a
|
|
35
|
+
* Retrieve or create a ChatSessionManager instance keyed by sessionId.
|
|
36
36
|
*/
|
|
37
37
|
function getOrCreateChat(sessionId, apiCredentials = null) {
|
|
38
38
|
if (!sessionId) {
|
|
@@ -54,24 +54,19 @@ function getOrCreateChat(sessionId, apiCredentials = null) {
|
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
// Create options object with sessionId and API credentials if provided
|
|
57
|
-
const options = {
|
|
57
|
+
const options = {
|
|
58
|
+
sessionId,
|
|
59
|
+
storage: globalStorage,
|
|
60
|
+
debug: process.env.DEBUG_CHAT === '1'
|
|
61
|
+
};
|
|
62
|
+
|
|
58
63
|
if (apiCredentials) {
|
|
59
64
|
options.apiProvider = apiCredentials.apiProvider;
|
|
60
65
|
options.apiKey = apiCredentials.apiKey;
|
|
61
66
|
options.apiUrl = apiCredentials.apiUrl;
|
|
62
67
|
}
|
|
63
|
-
|
|
64
|
-
// Pass storage instance for persistent storage
|
|
65
|
-
if (globalStorage) {
|
|
66
|
-
options.storage = globalStorage;
|
|
67
|
-
}
|
|
68
68
|
|
|
69
|
-
const newChat = new
|
|
70
|
-
|
|
71
|
-
// Add timestamps for session tracking
|
|
72
|
-
const now = Date.now();
|
|
73
|
-
newChat.createdAt = now;
|
|
74
|
-
newChat.lastActivity = now;
|
|
69
|
+
const newChat = new ChatSessionManager(options);
|
|
75
70
|
|
|
76
71
|
// Store in memory cache
|
|
77
72
|
chatSessions.set(sessionId, newChat);
|
|
@@ -80,8 +75,8 @@ function getOrCreateChat(sessionId, apiCredentials = null) {
|
|
|
80
75
|
if (globalStorage) {
|
|
81
76
|
globalStorage.saveSession({
|
|
82
77
|
id: sessionId,
|
|
83
|
-
createdAt:
|
|
84
|
-
lastActivity:
|
|
78
|
+
createdAt: newChat.createdAt,
|
|
79
|
+
lastActivity: newChat.lastActivity,
|
|
85
80
|
firstMessagePreview: null, // Will be updated when first message is sent
|
|
86
81
|
metadata: {
|
|
87
82
|
apiProvider: apiCredentials?.apiProvider || null
|
|
@@ -92,7 +87,7 @@ function getOrCreateChat(sessionId, apiCredentials = null) {
|
|
|
92
87
|
}
|
|
93
88
|
|
|
94
89
|
if (process.env.DEBUG_CHAT === '1') {
|
|
95
|
-
console.log(`[DEBUG] Created and stored new
|
|
90
|
+
console.log(`[DEBUG] Created and stored new ChatSessionManager instance for session: ${sessionId}. Total sessions: ${chatSessions.size}`);
|
|
96
91
|
if (apiCredentials && apiCredentials.apiKey) {
|
|
97
92
|
console.log(`[DEBUG] Chat instance created with client-provided API credentials (provider: ${apiCredentials.apiProvider})`);
|
|
98
93
|
}
|
|
@@ -107,7 +102,7 @@ function getOrCreateChat(sessionId, apiCredentials = null) {
|
|
|
107
102
|
* @param {Object} options - Additional options
|
|
108
103
|
* @param {boolean} options.allowEdit - Whether to allow editing files via the implement tool
|
|
109
104
|
*/
|
|
110
|
-
export function startWebServer(version, hasApiKeys = true, options = {}) {
|
|
105
|
+
export async function startWebServer(version, hasApiKeys = true, options = {}) {
|
|
111
106
|
const allowEdit = options?.allowEdit || false;
|
|
112
107
|
|
|
113
108
|
if (allowEdit) {
|
|
@@ -130,16 +125,14 @@ export function startWebServer(version, hasApiKeys = true, options = {}) {
|
|
|
130
125
|
verbose: process.env.DEBUG_CHAT === '1'
|
|
131
126
|
});
|
|
132
127
|
|
|
133
|
-
// Initialize storage
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
}
|
|
142
|
-
})();
|
|
128
|
+
// Initialize storage synchronously before server starts
|
|
129
|
+
try {
|
|
130
|
+
await globalStorage.initialize();
|
|
131
|
+
const stats = await globalStorage.getStats();
|
|
132
|
+
console.log(`Chat history storage: ${stats.storage_type} (${stats.session_count} sessions, ${stats.visible_message_count} messages)`);
|
|
133
|
+
} catch (error) {
|
|
134
|
+
console.warn('Failed to initialize chat history storage:', error.message);
|
|
135
|
+
}
|
|
143
136
|
|
|
144
137
|
// Map to store SSE clients by session ID
|
|
145
138
|
const sseClients = new Map();
|
|
@@ -763,9 +756,9 @@ export function startWebServer(version, hasApiKeys = true, options = {}) {
|
|
|
763
756
|
// The loop is inside chatInstance.chat now.
|
|
764
757
|
// We expect the *final* result string back.
|
|
765
758
|
try {
|
|
766
|
-
//
|
|
767
|
-
|
|
768
|
-
const result = await chatInstance.chat(message,
|
|
759
|
+
// ChatSessionManager handles session ID and API credentials internally
|
|
760
|
+
// Only pass the message and images
|
|
761
|
+
const result = await chatInstance.chat(message, images);
|
|
769
762
|
|
|
770
763
|
// Check if cancelled *during* the chat call (ProbeChat throws error)
|
|
771
764
|
// Error handled in catch block
|