feat: AI auto-fix bug tracker with real-time error monitoring

- Real-time error monitoring system with WebSocket
- Auto-fix agent that triggers on browser errors
- Bug tracker dashboard with floating button (🐛)
- Live activity stream showing AI thought process
- Fixed 4 JavaScript errors (SyntaxError, TypeError)
- Fixed SessionPicker API endpoint error
- Enhanced chat input with Monaco editor
- Session picker component for project management

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
uroma
2026-01-21 10:53:11 +00:00
Unverified
parent b765c537fc
commit efb3ecfb19
23 changed files with 7254 additions and 119 deletions

View File

@@ -60,7 +60,11 @@ async function loadChatView() {
}
const data = await res.json();
console.log('[loadChatView] Sessions data received:', data);
console.log('[loadChatView] Raw sessions data:', {
activeCount: (data.active || []).length,
historicalCount: (data.historical || []).length,
activeIds: (data.active || []).map(s => ({ id: s.id, status: s.status }))
});
const sessionsListEl = document.getElementById('chat-history-list');
@@ -72,12 +76,13 @@ async function loadChatView() {
// ONLY show active sessions - no historical sessions in chat view
// Historical sessions are read-only and can't receive new messages
let activeSessions = (data.active || []).filter(s => s.status === 'running');
console.log('[loadChatView] Running sessions after status filter:', activeSessions.length);
// Filter by current project if in project context
const currentProjectDir = window.currentProjectDir;
if (currentProjectDir) {
console.log('[loadChatView] Filtering sessions for project path:', currentProjectDir);
console.log('[loadChatView] Current project dir:', currentProjectDir);
// Filter sessions that belong to this project
activeSessions = activeSessions.filter(session => {
@@ -300,8 +305,10 @@ async function startNewChat() {
// Subscribe to session via WebSocket
subscribeToSession(data.session.id);
// Reload sessions list
loadChatView();
// Give backend time to persist session, then refresh sidebar
// This ensures the new session appears in the list
await new Promise(resolve => setTimeout(resolve, 150));
await loadChatView().catch(err => console.error('[startNewChat] Background refresh failed:', err));
// Hide the creation success message after a short delay
setTimeout(() => {
@@ -363,6 +370,26 @@ function subscribeToSession(sessionId) {
sessionId: sessionId
}));
console.log('Subscribed to session:', sessionId);
} else if (window.ws && window.ws.readyState === WebSocket.CONNECTING) {
// Wait for connection to open, then subscribe
console.log('[subscribeToSession] WebSocket connecting, will subscribe when ready...');
const onOpen = () => {
window.ws.send(JSON.stringify({
type: 'subscribe',
sessionId: sessionId
}));
console.log('[subscribeToSession] Subscribed after connection open:', sessionId);
window.ws.removeEventListener('open', onOpen);
};
window.ws.addEventListener('open', onOpen);
} else {
// WebSocket not connected - try to reconnect
console.warn('[subscribeToSession] WebSocket not connected, attempting to connect...');
if (typeof connectWebSocket === 'function') {
connectWebSocket();
// Retry subscription after connection
setTimeout(() => subscribeToSession(sessionId), 500);
}
}
}
@@ -503,9 +530,29 @@ async function sendChatMessage() {
if (!message) return;
// Auto-create session if none exists (OpenCode/CodeNomad hybrid approach)
if (!attachedSessionId) {
appendSystemMessage('Please start or attach to a session first.');
return;
console.log('[sendChatMessage] No session attached, auto-creating...');
appendSystemMessage('Creating new session...');
try {
await startNewChat();
// After session creation, wait a moment for attachment
await new Promise(resolve => setTimeout(resolve, 500));
// Verify session was created and attached
if (!attachedSessionId) {
appendSystemMessage('❌ Failed to create session. Please try again.');
return;
}
console.log('[sendChatMessage] Session auto-created:', attachedSessionId);
} catch (error) {
console.error('[sendChatMessage] Auto-create session failed:', error);
appendSystemMessage('❌ Failed to create session: ' + error.message);
return;
}
}
// Hide mode suggestion banner
@@ -605,7 +652,24 @@ async function sendChatMessage() {
console.log('Sending with metadata:', payload.metadata);
}
window.ws.send(JSON.stringify(payload));
// Debug logging before sending
console.log('[DEBUG] About to send command payload:', {
type: payload.type,
sessionId: payload.sessionId,
commandLength: payload.command?.length,
wsReady: window.wsReady,
wsState: window.ws?.readyState,
queueLength: window.messageQueue?.length || 0
});
// Use message queue to prevent race conditions
if (typeof queueMessage === 'function') {
queueMessage(payload);
console.log('[DEBUG] Message queued, queue length now:', window.messageQueue?.length);
} else {
window.ws.send(JSON.stringify(payload));
console.log('[DEBUG] Sent directly via WebSocket (no queue function)');
}
console.log('Sent command via WebSocket:', message.substring(0, 50));
} catch (error) {
console.error('Error sending message:', error);
@@ -624,12 +688,12 @@ function setGeneratingState(generating) {
if (generating) {
// Show stop button, hide send button
sendButton.classList.add('hidden');
stopButton.classList.remove('hidden');
if (sendButton) sendButton.classList.add('hidden');
if (stopButton) stopButton.classList.remove('hidden');
} else {
// Show send button, hide stop button
sendButton.classList.remove('hidden');
stopButton.classList.add('hidden');
if (sendButton) sendButton.classList.remove('hidden');
if (stopButton) stopButton.classList.add('hidden');
}
}
@@ -1066,10 +1130,16 @@ function clearInput() {
const wrapper = document.getElementById('chat-input-wrapper');
const charCountBadge = document.getElementById('char-count-badge');
input.value = '';
input.style.height = 'auto';
wrapper.classList.remove('typing');
charCountBadge.textContent = '0 chars';
if (input) {
input.value = '';
input.style.height = 'auto';
}
if (wrapper) {
wrapper.classList.remove('typing');
}
if (charCountBadge) {
charCountBadge.textContent = '0 chars';
}
}
// Update Token Usage