/** * Semantic Error Detection Layer * Detects intent/behavior mismatches, conversational messages executed as commands, * and confusing UX messages in the chat interface. */ (function() { 'use strict'; // ============================================================ // CONVERSATIONAL PATTERN DETECTION // ============================================================ const CONVERSATIONAL_PATTERNS = [ // Question words (strong indicators) /^(if|when|where|what|how|why|who|which|whose|can|could|would|should|will|do|does|did|is|are|was|were|am|have|has|had)\s/i, // Pronouns (very strong conversational indicators) /^(i|you|he|she|it|we|they)\s/i, // Agreement/disagreement phrases /^(yes|no|yeah|yep|nope|maybe|ok|okay|sure|alright|fine|go ahead)\s/i, // Politeness markers /^(please|thank|thanks|hey|hello|hi|greetings)\s/i, // Ellipsis (conversational pause) /^\.\.\./, // Conversational transitions with commas /^[a-z]+,\s/i, // Thinking/believing/wanting verbs (indicate opinion, not command) /\b(think|believe|want|need|like|prefer|mean|wish|hope|feel)\b/i, // Context markers (explanatory, not imperative) /\b(because|although|however|therefore|unless|until|since|while)\b/i, // Second-person references to AI's actions /\byou\b.*(ask|tell|want|need|like|prefer|said|mentioned)/i, // First-person expressions /\b(I'm|i am|i'd|i will|i can|i should|i would)\s/i ]; const APPROVAL_PATTERNS = [ // Direct approval /^(yes|yeah|yep|sure|okay|ok|go ahead|please do|do it)\b/i, // Direct rejection /^(no|nope|don't|do not|stop|wait)\b/i, // Polite approval /^please\s+(go ahead|proceed|continue|do it)/i, // Casual approval /^(cool|great|awesome|perfect|good|fine)\b/i ]; const CONTEXTUAL_CONVERSATIONAL_PATTERNS = [ // Conditional statements about commands /if (i|you|we) asked/i, /when (i|you|we) said/i, /as (i|you|we) mentioned/i, /since (i|you|we) asked/i, // References to previous conversation /like (i|you) said/i, /as (i|you) requested/i ]; // ============================================================ // COMMAND PATTERN DETECTION (Existing patterns + enhancements) // ============================================================ const COMMAND_PATTERNS = [ // Shell built-ins /^(cd|ls|pwd|echo|cat|grep|find|rm|cp|mv|mkdir|rmdir|touch|chmod|chown|ln|head|tail|less|more|sort|uniq|wc|tar|zip|unzip|gzip|gunzip|df|du|ps|top|kill|killall|nohup|bg|fg|jobs|export|unset|env|source|\.|alias|unalias|history|clear|reset|ping|ssh|scp|rsync|curl|wget|file|which|whereis|man|info|traceroute|nslookup|dig|host|netstat|ifconfig|ip)(\s|$)/, // Package managers /^(npm|yarn|pnpm|pip|pip3|conda|brew|apt|apt-get|yum|dnf|pacman)(\s|$)/, // Node.js commands /^(node|npx)(\s|$)/, // Python commands /^(python|python3|pip|pip3|python3-m)(\s|$)/, // Git commands /^git(\s|$)/, // Docker commands /^docker(\s|$)/, // File operations with pipes or redirects (must look technical) /^[a-z0-9_\-./]+\s*[\|>]/, // Build tools /^(make|cmake|cargo|go|rust|ruby|gem|java|javac|gradle|maven|mvn|gcc|g\+\+|clang)(\s|$)/, // Absolute paths /^\//, // Scripts /^(sh|bash|zsh|fish|powershell|pwsh)(\s|$)/ ]; // ============================================================ // CONFUSING UX MESSAGE PATTERNS // ============================================================ const CONFUSING_OUTPUT_PATTERNS = [ { pattern: /exited with code (undefined|null)/i, issue: 'Exit code is undefined/null', suggestion: 'Show actual exit code or success/failure message' }, { pattern: /command (succeeded|failed) but output is (empty|undefined|null)/i, issue: 'No output shown to user', suggestion: 'Always show command output or "No output" message' }, { pattern: /error:.*undefined/i, issue: 'Generic undefined error', suggestion: 'Provide specific error message' }, { pattern: /null.*error/i, issue: 'Null error reference', suggestion: 'Provide meaningful error details' }, { pattern: /cannot (read|access).*undefined/i, issue: 'Undefined property access', suggestion: 'Validate properties before access' } ]; // ============================================================ // CONVERSATION STATE TRACKING // ============================================================ const conversationState = { lastAssistantMessages: [], lastUserMessage: null, intentState: 'IDLE', // IDLE, AWAITING_APPROVAL, EXECUTING_COMMAND, CONVERSING addAssistantMessage(message) { this.lastAssistantMessages.push({ content: message, timestamp: Date.now() }); // Keep only last 5 messages if (this.lastAssistantMessages.length > 5) { this.lastAssistantMessages.shift(); } this.updateIntentState(); }, addUserMessage(message) { this.lastUserMessage = { content: message, timestamp: Date.now() }; }, updateIntentState() { const lastMsg = this.getLastAssistantMessage(); if (!lastMsg) { this.intentState = 'IDLE'; return; } // Check if AI is asking for approval const approvalRequest = /\byou want me to|shall i|should i|do you want|would you like|shall i proceed|should i run/i.test(lastMsg.content); if (approvalRequest) { this.intentState = 'AWAITING_APPROVAL'; return; } this.intentState = 'CONVERSING'; }, getLastAssistantMessage() { return this.lastAssistantMessages[this.lastAssistantMessages.length - 1]; }, getLastNMessages(n) { return this.lastAssistantMessages.slice(-n); } }; // ============================================================ // PUBLIC API // ============================================================ /** * Check if a message appears to be conversational (not a shell command) * @param {string} message - User's message * @returns {boolean} - True if conversational */ function isConversational(message) { const trimmed = message.trim(); if (!trimmed) return false; const lower = trimmed.toLowerCase(); // Check all conversational patterns return CONVERSATIONAL_PATTERNS.some(pattern => pattern.test(lower)); } /** * Check if a message is an approval response (yes/no/sure/etc) * @param {string} message - User's message * @returns {boolean} - True if approval response */ function isApprovalResponse(message) { const trimmed = message.trim(); if (!trimmed) return false; const lower = trimmed.toLowerCase(); return APPROVAL_PATTERNS.some(pattern => pattern.test(lower)); } /** * Check if a message looks like a contextual conversational message * (e.g., "if I asked you to ping google.com...") * @param {string} message - User's message * @returns {boolean} - True if contextual conversational */ function isContextualConversational(message) { const trimmed = message.trim(); if (!trimmed) return false; const lower = trimmed.toLowerCase(); return CONTEXTUAL_CONVERSATIONAL_PATTERNS.some(pattern => pattern.test(lower)); } /** * Enhanced shell command detection * @param {string} message - User's message * @returns {boolean} - True if it looks like a shell command */ function isShellCommand(message) { const trimmed = message.trim(); if (!trimmed) return false; const lower = trimmed.toLowerCase(); // FIRST: Check for command requests in conversational language // Pattern: "run ping google.com", "execute ls -la", "can you run npm install?" const commandRequestPatterns = [ /\b(run|execute|exec|do|can you run|please run|execute this|run this)\s+(.+?)\b/i, /\b(start|launch|begin|kick off)\s+(.+?)\b/i, /\b(go ahead and\s+)?(run|execute)\b/i ]; for (const pattern of commandRequestPatterns) { const match = lower.match(pattern); if (match && match[2]) { // Extract the command and check if it's valid const extractedCommand = match[2].trim(); if (extractedCommand && COMMAND_PATTERNS.some(p => p.test(extractedCommand))) { console.log('[SemanticValidator] Detected command request:', extractedCommand); return true; } } } // SECOND: Check if it's conversational (catch intent errors) if (isConversational(lower)) { return false; } // THIRD: Check for contextual conversational patterns if (isContextualConversational(lower)) { return false; } // FOURTH: Check if it matches command patterns return COMMAND_PATTERNS.some(pattern => pattern.test(lower)); } /** * Extract actual command from conversational request * @param {string} message - User's message * @returns {string|null} - Extracted command or null */ function extractCommand(message) { const trimmed = message.trim(); if (!trimmed) return null; const lower = trimmed.toLowerCase(); // Pattern: "run ping google.com" → "ping google.com" // Pattern: "execute ls -la" → "ls -la" // FIXED: Capture everything after command verb, then trim only trailing sentence punctuation const commandRequestPatterns = [ // Match "run/execute [command]" - capture everything to end, trim punctuation later /\b(run|execute|exec|do|can you run|please run|execute this|run this)\s+(.+)/i, /\b(start|launch|begin|kick off)\s+(.+)/i ]; for (const pattern of commandRequestPatterns) { const match = lower.match(pattern); if (match && match[2]) { let extractedCommand = match[2].trim(); // Remove trailing sentence punctuation (. ! ?) ONLY if at end (not in middle like .com) // Only remove if the punctuation is at the very end after trimming extractedCommand = extractedCommand.replace(/[.!?]+$/g, '').trim(); // Validate that it's actually a command (check first word) const firstWord = extractedCommand.split(/\s+/)[0]; if (firstWord && COMMAND_PATTERNS.some(p => p.test(firstWord))) { // Preserve original case for the command const originalMatch = trimmed.match(pattern); if (originalMatch && originalMatch[2]) { let originalCommand = originalMatch[2].trim(); // Remove trailing punctuation from original too originalCommand = originalCommand.replace(/[.!?]+$/, '').trim(); console.log('[SemanticValidator] Extracted command:', originalCommand, 'from:', trimmed); return originalCommand; } } } } // If no command request pattern found, return original message return trimmed; } /** * Detect intent mismatch when user responds to approval request * @param {string} userMessage - User's message * @param {string} currentMode - Current chat mode * @returns {object|null} - Error object if mismatch detected, null otherwise */ function detectApprovalIntentMismatch(userMessage, currentMode) { // Only check in Terminal/WebContainer mode if (currentMode !== 'webcontainer') { return null; } // Check if we're awaiting approval if (conversationState.intentState !== 'AWAITING_APPROVAL') { return null; } // Check if user message is an approval response if (isApprovalResponse(userMessage)) { return { type: 'intent_error', subtype: 'approval_loop', message: 'User responded with approval in Terminal mode, but system may execute it as a command', details: { lastAssistantMessage: conversationState.getLastAssistantMessage()?.content?.substring(0, 100) || '', userMessage: userMessage, mode: currentMode, suggestedAction: 'Interpret approval responses contextually instead of executing as commands' } }; } return null; } /** * Detect conversational messages being sent to Terminal mode * @param {string} message - User's message * @param {string} mode - Current chat mode * @returns {object|null} - Error object if detected, null otherwise */ function detectConversationalCommand(message, mode) { // Only check in Terminal/WebContainer mode if (mode !== 'webcontainer') { return null; } if (isConversational(message) || isContextualConversational(message)) { return { type: 'intent_error', subtype: 'conversational_as_command', message: 'Conversational message sent to Terminal mode', details: { userMessage: message, mode: mode, suggestedMode: 'chat', suggestedAction: 'Switch to Chat mode or rephrase as shell command' } }; } return null; } /** * Detect confusing UX messages in command output * @param {string} output - Command output * @returns {object|null} - Error object if detected, null otherwise */ function detectConfusingOutput(output) { if (!output || typeof output !== 'string') { return null; } for (const confusing of CONFUSING_OUTPUT_PATTERNS) { if (confusing.pattern.test(output)) { return { type: 'ux_issue', subtype: 'confusing_message', message: 'Confusing error message displayed to user', details: { originalOutput: output.substring(0, 200), issue: confusing.issue, suggestedImprovement: confusing.suggestion } }; } } return null; } /** * Validate user intent before executing command * @param {string} message - User's message * @param {string} mode - Current chat mode * @returns {object} - Validation result {valid: boolean, error: object|null} */ function validateIntentBeforeExecution(message, mode) { // Check for approval intent mismatch const approvalMismatch = detectApprovalIntentMismatch(message, mode); if (approvalMismatch) { return { valid: false, error: approvalMismatch }; } // Check for conversational message in command mode const conversationalCmd = detectConversationalCommand(message, mode); if (conversationalCmd) { return { valid: false, error: conversationalCmd }; } return { valid: true, error: null }; } /** * Track assistant message for context * @param {string} message - Assistant's message */ function trackAssistantMessage(message) { conversationState.addAssistantMessage(message); } /** * Track user message for context * @param {string} message - User's message */ function trackUserMessage(message) { conversationState.addUserMessage(message); } /** * Get current conversation state * @returns {string} - Current intent state */ function getIntentState() { return conversationState.intentState; } /** * Report a semantic error to the bug tracker * @param {object} errorData - Error details */ function reportSemanticError(errorData) { const semanticError = { type: 'semantic', subtype: errorData.subtype || errorData.type, message: errorData.message, details: errorData.details || {}, timestamp: new Date().toISOString(), url: window.location.href, context: { chatMode: window.currentChatMode || 'unknown', sessionId: window.attachedSessionId || window.chatSessionId, intentState: conversationState.intentState, recentMessages: conversationState.getLastNMessages(3).map(m => ({ content: m.content?.substring(0, 50), timestamp: m.timestamp })) } }; // Report to bug tracker if (window.bugTracker) { const errorId = window.bugTracker.addError(semanticError); // Only add activity if error was actually added (not skipped) if (errorId) { window.bugTracker.addActivity(errorId, '🔍', `Semantic: ${errorData.subtype}`, 'warning'); } } // Report to server for auto-fixer fetch('/claude/api/log-error', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(semanticError) }).catch(err => console.error('[SemanticError] Failed to report:', err)); // Log for debugging console.log('[SemanticError] Detected:', semanticError); return semanticError; } // Export to global scope window.semanticValidator = { isConversational, isApprovalResponse, isContextualConversational, isShellCommand, extractCommand, detectApprovalIntentMismatch, detectConversationalCommand, detectConfusingOutput, validateIntentBeforeExecution, trackAssistantMessage, trackUserMessage, getIntentState, reportSemanticError }; console.log('[SemanticValidator] Initialized with enhanced error detection'); })();