/* ============================================================================= AUTO TASK JAVASCRIPT - Intelligent Self-Executing Task Interface Premium VIP Mode Functionality ============================================================================= */ // ============================================================================= // STATE MANAGEMENT // ============================================================================= const AutoTaskState = { currentFilter: "all", tasks: [], compiledPlan: null, pendingDecisions: [], pendingApprovals: [], refreshInterval: null, wsConnection: null, }; // ============================================================================= // INITIALIZATION // ============================================================================= document.addEventListener("DOMContentLoaded", function () { initAutoTask(); }); function initAutoTask() { // Initialize WebSocket for real-time updates initWebSocket(); // Start auto-refresh startAutoRefresh(); // Setup event listeners setupEventListeners(); // Load initial stats updateStats(); // Setup keyboard shortcuts setupKeyboardShortcuts(); console.log("AutoTask initialized"); } // ============================================================================= // WEBSOCKET CONNECTION // ============================================================================= function initWebSocket() { const protocol = window.location.protocol === "https:" ? "wss:" : "ws:"; const wsUrl = `${protocol}//${window.location.host}/ws/autotask`; try { AutoTaskState.wsConnection = new WebSocket(wsUrl); AutoTaskState.wsConnection.onopen = function () { console.log("AutoTask WebSocket connected"); }; AutoTaskState.wsConnection.onmessage = function (event) { handleWebSocketMessage(JSON.parse(event.data)); }; AutoTaskState.wsConnection.onclose = function () { console.log("AutoTask WebSocket disconnected, reconnecting..."); setTimeout(initWebSocket, 5000); }; AutoTaskState.wsConnection.onerror = function (error) { console.error("AutoTask WebSocket error:", error); }; } catch (e) { console.warn("WebSocket not available, using polling"); } } function handleWebSocketMessage(data) { switch (data.type) { case "task_update": updateTaskInList(data.task); break; case "step_progress": updateStepProgress(data.taskId, data.step, data.progress); break; case "decision_required": showDecisionNotification(data.decision); break; case "approval_required": showApprovalNotification(data.approval); break; case "task_completed": onTaskCompleted(data.task); break; case "task_failed": onTaskFailed(data.task, data.error); break; case "stats_update": updateStatsFromData(data.stats); break; } } // ============================================================================= // EVENT LISTENERS // ============================================================================= function setupEventListeners() { // Intent form submission const intentForm = document.getElementById("intent-form"); if (intentForm) { intentForm.addEventListener("htmx:afterSwap", function (event) { if (event.detail.target.id === "compilation-result") { onCompilationComplete(event); } }); } // Task list updates const taskList = document.getElementById("task-list"); if (taskList) { taskList.addEventListener("htmx:afterSwap", function () { updateStats(); highlightPendingItems(); }); } // Expand log entries on details open document.addEventListener( "toggle", function (event) { if ( event.target.classList.contains("execution-log") && event.target.open ) { const taskId = event.target.closest(".autotask-item")?.dataset.taskId; if (taskId) { loadExecutionLogs(taskId); } } }, true, ); } function setupKeyboardShortcuts() { document.addEventListener("keydown", function (e) { // Alt + N: Focus on intent input if (e.altKey && e.key === "n") { e.preventDefault(); document.getElementById("intent-input")?.focus(); } // Alt + R: Refresh tasks if (e.altKey && e.key === "r") { e.preventDefault(); refreshTasks(); } // Escape: Close any open modal if (e.key === "Escape") { closeAllModals(); } // Alt + 1-4: Switch filters if (e.altKey && e.key >= "1" && e.key <= "4") { e.preventDefault(); const filters = ["all", "running", "approval", "decision"]; const index = parseInt(e.key) - 1; const tabs = document.querySelectorAll(".filter-tab"); if (tabs[index]) { tabs[index].click(); } } }); } // ============================================================================= // AUTO REFRESH // ============================================================================= function startAutoRefresh() { // Refresh every 5 seconds AutoTaskState.refreshInterval = setInterval(function () { if (!document.hidden) { updateStats(); } }, 5000); } function stopAutoRefresh() { if (AutoTaskState.refreshInterval) { clearInterval(AutoTaskState.refreshInterval); AutoTaskState.refreshInterval = null; } } // ============================================================================= // STATS MANAGEMENT // ============================================================================= function updateStats() { fetch("/api/autotask/stats") .then((response) => response.json()) .then((stats) => { updateStatsFromData(stats); }) .catch((error) => { console.error("Failed to fetch stats:", error); }); } function updateStatsFromData(stats) { // Header stats document.getElementById("stat-running").textContent = stats.running || 0; document.getElementById("stat-pending").textContent = stats.pending || 0; document.getElementById("stat-completed").textContent = stats.completed || 0; document.getElementById("stat-approval").textContent = stats.pending_approval || 0; // Filter counts document.getElementById("count-all").textContent = stats.total || 0; document.getElementById("count-running").textContent = stats.running || 0; document.getElementById("count-approval").textContent = stats.pending_approval || 0; document.getElementById("count-decision").textContent = stats.pending_decision || 0; // Highlight if approvals needed const approvalStat = document.querySelector(".stat-item.highlight"); if (approvalStat && stats.pending_approval > 0) { approvalStat.classList.add("attention"); } else if (approvalStat) { approvalStat.classList.remove("attention"); } } // ============================================================================= // TASK FILTERING // ============================================================================= function filterTasks(filter, button) { AutoTaskState.currentFilter = filter; // Update active tab document.querySelectorAll(".filter-tab").forEach((tab) => { tab.classList.remove("active"); }); button.classList.add("active"); // Trigger HTMX request htmx.ajax("GET", `/api/autotask/list?filter=${filter}`, { target: "#task-list", swap: "innerHTML", }); } function refreshTasks() { const filter = AutoTaskState.currentFilter; htmx.ajax("GET", `/api/autotask/list?filter=${filter}`, { target: "#task-list", swap: "innerHTML", }); updateStats(); } // ============================================================================= // COMPILATION HANDLING // ============================================================================= function onCompilationComplete(event) { const result = event.detail.target.querySelector(".compiled-plan"); if (result) { // Scroll to result result.scrollIntoView({ behavior: "smooth", block: "start" }); // Store compiled plan const planId = result.dataset?.planId; if (planId) { AutoTaskState.compiledPlan = planId; } // Syntax highlight the code highlightBasicCode(); } } function highlightBasicCode() { const codeBlocks = document.querySelectorAll(".code-preview code"); codeBlocks.forEach((block) => { // Basic syntax highlighting for BASIC keywords let html = block.innerHTML; // Keywords const keywords = [ "PLAN_START", "PLAN_END", "STEP", "SET", "GET", "IF", "THEN", "ELSE", "END IF", "FOR EACH", "NEXT", "WHILE", "WEND", "TALK", "HEAR", "LLM", "CREATE_TASK", "RUN_PYTHON", "RUN_JAVASCRIPT", "RUN_BASH", "USE_MCP", "POST", "GET", "PUT", "PATCH", "DELETE HTTP", "REQUIRE_APPROVAL", "SIMULATE_IMPACT", "AUDIT_LOG", "SEND_MAIL", "SAVE", "UPDATE", "INSERT", "DELETE", "FIND", ]; keywords.forEach((keyword) => { const regex = new RegExp(`\\b${keyword}\\b`, "g"); html = html.replace(regex, `${keyword}`); }); // Comments html = html.replace(/(\'[^\n]*)/g, '$1'); // Strings html = html.replace(/("[^"]*")/g, '$1'); // Numbers html = html.replace(/\b(\d+)\b/g, '$1'); block.innerHTML = html; }); } function copyGeneratedCode() { const code = document.querySelector(".code-preview code")?.textContent; if (code) { navigator.clipboard .writeText(code) .then(() => { showToast("Code copied to clipboard", "success"); }) .catch(() => { showToast("Failed to copy code", "error"); }); } } function discardPlan() { if (confirm("Are you sure you want to discard this plan?")) { document.getElementById("compilation-result").innerHTML = ""; AutoTaskState.compiledPlan = null; document.getElementById("intent-input").value = ""; document.getElementById("intent-input").focus(); } } function editPlan() { // TODO: Implement plan editor showToast("Plan editor coming soon!", "info"); } // ============================================================================= // PLAN EXECUTION // ============================================================================= function simulatePlan(planId) { showSimulationModal(); fetch(`/api/autotask/simulate/${planId}`, { method: "POST", }) .then((response) => response.json()) .then((result) => { renderSimulationResult(result); }) .catch((error) => { document.getElementById("simulation-content").innerHTML = `
❌

Failed to simulate plan: ${error.message}

`; }); } function executePlan(planId) { const executionMode = document.querySelector('[name="execution_mode"]')?.value || "semi-automatic"; const priority = document.querySelector('[name="priority"]')?.value || "medium"; if (!confirm("Are you sure you want to execute this plan?")) { return; } fetch("/api/autotask/execute", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ plan_id: planId, execution_mode: executionMode, priority: priority, }), }) .then((response) => response.json()) .then((result) => { if (result.success) { showToast("Task execution started!", "success"); document.getElementById("compilation-result").innerHTML = ""; document.getElementById("intent-input").value = ""; refreshTasks(); } else { showToast(`Failed to start execution: ${result.error}`, "error"); } }) .catch((error) => { showToast(`Failed to execute plan: ${error.message}`, "error"); }); } // ============================================================================= // TASK ACTIONS // ============================================================================= function viewTaskDetails(taskId) { window.location.href = `/suite/tasks/detail/${taskId}`; } function simulateTask(taskId) { showSimulationModal(); fetch(`/api/autotask/${taskId}/simulate`, { method: "POST", }) .then((response) => response.json()) .then((result) => { result.task_id = taskId; renderSimulationResult(result); }) .catch((error) => { document.getElementById("simulation-content").innerHTML = `
❌

Failed to simulate task: ${error.message}

`; }); } function pauseTask(taskId) { fetch(`/api/autotask/${taskId}/pause`, { method: "POST", }) .then((response) => response.json()) .then((result) => { if (result.success) { showToast("Task paused", "success"); refreshTasks(); } else { showToast(`Failed to pause task: ${result.error}`, "error"); } }); } function resumeTask(taskId) { fetch(`/api/autotask/${taskId}/resume`, { method: "POST", }) .then((response) => response.json()) .then((result) => { if (result.success) { showToast("Task resumed", "success"); refreshTasks(); } else { showToast(`Failed to resume task: ${result.error}`, "error"); } }); } function cancelTask(taskId) { if ( !confirm( "Are you sure you want to cancel this task? This may not be reversible.", ) ) { return; } fetch(`/api/autotask/${taskId}/cancel`, { method: "POST", }) .then((response) => response.json()) .then((result) => { if (result.success) { showToast("Task cancelled", "success"); refreshTasks(); } else { showToast(`Failed to cancel task: ${result.error}`, "error"); } }); } function updateTaskInList(task) { const taskElement = document.querySelector(`[data-task-id="${task.id}"]`); if (taskElement) { // Update status badge const statusBadge = taskElement.querySelector(".task-status-badge"); if (statusBadge) { statusBadge.className = `task-status-badge status-${task.status}`; statusBadge.textContent = task.status.replace(/-/g, " "); } // Update progress const progressFill = taskElement.querySelector(".progress-fill"); const progressText = taskElement.querySelector(".progress-text"); if (progressFill && progressText) { progressFill.style.width = `${task.progress}%`; progressText.textContent = `${task.current_step}/${task.total_steps} steps (${Math.round(task.progress)}%)`; } // Update data attribute taskElement.dataset.status = task.status; } } function updateStepProgress(taskId, step, progress) { const taskElement = document.querySelector(`[data-task-id="${taskId}"]`); if (taskElement) { const currentStep = taskElement.querySelector(".current-step"); if (currentStep) { currentStep.querySelector(".step-name").textContent = `Step ${step.order}: ${step.name}`; currentStep.querySelector(".step-status").textContent = `${Math.round(progress)}%`; } } } // ============================================================================= // DECISIONS // ============================================================================= function viewDecisions(taskId) { showDecisionModal(); fetch(`/api/autotask/${taskId}/decisions`) .then((response) => response.json()) .then((decisions) => { renderDecisions(taskId, decisions); }) .catch((error) => { document.getElementById("decision-content").innerHTML = `
❌

Failed to load decisions: ${error.message}

`; }); } function renderDecisions(taskId, decisions) { const container = document.getElementById("decision-content"); if (!decisions || decisions.length === 0) { container.innerHTML = '

No pending decisions.

'; return; } let html = '
'; decisions.forEach((decision) => { html += `

${decision.title}

${decision.description}

${decision.options .map( (opt) => `

${opt.description}

πŸ’° ${opt.estimated_impact.cost_change >= 0 ? "+" : ""}$${opt.estimated_impact.cost_change} ⏱️ ${opt.estimated_impact.time_change_minutes >= 0 ? "+" : ""}${opt.estimated_impact.time_change_minutes}m ⚠️ ${opt.risk_level}
`, ) .join("")}
`; }); html += "
"; container.innerHTML = html; } function submitDecision(taskId, decisionId) { const selectedOption = document.querySelector( `input[name="decision_${decisionId}"]:checked`, )?.value; if (!selectedOption) { showToast("Please select an option", "warning"); return; } fetch(`/api/autotask/${taskId}/decide`, { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ decision_id: decisionId, option_id: selectedOption, }), }) .then((response) => response.json()) .then((result) => { if (result.success) { showToast("Decision submitted", "success"); closeDecisionModal(); refreshTasks(); } else { showToast(`Failed to submit decision: ${result.error}`, "error"); } }); } function skipDecision(taskId, decisionId) { if ( !confirm( "Are you sure you want to skip this decision? The default option will be used.", ) ) { return; } fetch(`/api/autotask/${taskId}/decide`, { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ decision_id: decisionId, skip: true, }), }) .then((response) => response.json()) .then((result) => { if (result.success) { showToast("Decision skipped", "info"); closeDecisionModal(); refreshTasks(); } else { showToast(`Failed to skip decision: ${result.error}`, "error"); } }); } function showDecisionNotification(decision) { showToast(`Decision required: ${decision.title}`, "warning", 10000); updateStats(); } // ============================================================================= // APPROVALS // ============================================================================= function viewApprovals(taskId) { showApprovalModal(); fetch(`/api/autotask/${taskId}/approvals`) .then((response) => response.json()) .then((approvals) => { renderApprovals(taskId, approvals); }) .catch((error) => { document.getElementById("approval-content").innerHTML = `
❌

Failed to load approvals: ${error.message}

`; }); } function renderApprovals(taskId, approvals) { const container = document.getElementById("approval-content"); if (!approvals || approvals.length === 0) { container.innerHTML = '

No pending approvals.

'; return; } let html = '
'; approvals.forEach((approval) => { html += `
${approval.approval_type.replace(/_/g, " ")} ${approval.risk_level} Risk

${approval.title}

${approval.description}

Impact Summary

${approval.impact_summary}

${ approval.simulation_result ? `
Simulation Result
Risk: ${approval.simulation_result.risk_level} Confidence: ${Math.round(approval.simulation_result.confidence * 100)}%
` : "" }
Step: ${approval.step_name || "N/A"} Expires: ${formatRelativeTime(approval.expires_at)} Default: ${approval.default_action}
`; }); html += "
"; container.innerHTML = html; } function approveApproval(taskId, approvalId) { submitApprovalDecision(taskId, approvalId, "approve"); } function rejectApproval(taskId, approvalId) { if (!confirm("Are you sure you want to reject this action?")) { return; } submitApprovalDecision(taskId, approvalId, "reject"); } function deferApproval(taskId, approvalId) { submitApprovalDecision(taskId, approvalId, "defer"); } function submitApprovalDecision(taskId, approvalId, action) { fetch(`/api/autotask/${taskId}/approve`, { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ approval_id: approvalId, action: action, }), }) .then((response) => response.json()) .then((result) => { if (result.success) { const messages = { approve: "Approval granted", reject: "Approval rejected", defer: "Approval deferred", }; showToast(messages[action], "success"); closeApprovalModal(); refreshTasks(); } else { showToast(`Failed to ${action}: ${result.error}`, "error"); } }); } function showApprovalNotification(approval) { showToast(`Approval required: ${approval.title}`, "warning", 10000); updateStats(); } // ============================================================================= // SIMULATION // ============================================================================= function renderSimulationResult(result) { const container = document.getElementById("simulation-content"); const statusIcon = result.success ? "βœ…" : "⚠️"; const statusText = result.success ? "Simulation Successful" : "Simulation Found Issues"; let html = `
${statusIcon} ${statusText}
Confidence: ${Math.round(result.confidence * 100)}%

Impact Assessment

πŸ’Ύ Data Impact ${result.impact.data_impact.records_modified} records modified
πŸ’° Cost Impact $${result.impact.cost_impact.total_estimated_cost.toFixed(2)}
⏱️ Time Impact ${formatDuration(result.impact.time_impact.estimated_duration_seconds)}
πŸ”’ Security Impact ${result.impact.security_impact.risk_level}

Step-by-Step Predictions

${result.step_outcomes .map( (step) => `
${step.would_succeed ? "βœ…" : "⚠️"} ${step.step_name} ${Math.round(step.success_probability * 100)}% success
`, ) .join("")}
${ result.side_effects.length > 0 ? `

⚠️ Potential Side Effects

${result.side_effects .map( (effect) => `
${effect.description} ${effect.mitigation ? `Mitigation: ${effect.mitigation}` : ""}
`, ) .join("")}
` : "" } ${ result.recommendations.length > 0 ? `

πŸ’‘ Recommendations

${result.recommendations .map( (rec) => `
${rec.description} ${rec.action ? `` : ""}
`, ) .join("")}
` : "" }
`; container.innerHTML = html; } // ============================================================================= // MODAL FUNCTIONS // ============================================================================= function showSimulationModal() { const modal = document.getElementById("simulation-modal"); if (modal) { modal.style.display = "flex"; document.body.classList.add("modal-open"); // Show loading state document.getElementById("simulation-content").innerHTML = `

Running impact simulation...

`; } } function closeSimulationModal() { const modal = document.getElementById("simulation-modal"); if (modal) { modal.style.display = "none"; document.body.classList.remove("modal-open"); } } function showDecisionModal() { const modal = document.getElementById("decision-modal"); if (modal) { modal.style.display = "flex"; document.body.classList.add("modal-open"); // Show loading state document.getElementById("decision-content").innerHTML = `

Loading decisions...

`; } } function closeDecisionModal() { const modal = document.getElementById("decision-modal"); if (modal) { modal.style.display = "none"; document.body.classList.remove("modal-open"); } } function showApprovalModal() { const modal = document.getElementById("approval-modal"); if (modal) { modal.style.display = "flex"; document.body.classList.add("modal-open"); // Show loading state document.getElementById("approval-content").innerHTML = `

Loading approvals...

`; } } function closeApprovalModal() { const modal = document.getElementById("approval-modal"); if (modal) { modal.style.display = "none"; document.body.classList.remove("modal-open"); } } function closeAllModals() { closeSimulationModal(); closeDecisionModal(); closeApprovalModal(); } // ============================================================================= // SIMULATION ACTIONS // ============================================================================= function proceedAfterSimulation(taskId) { closeSimulationModal(); if (!taskId) { showToast("No task ID provided", "error"); return; } fetch(`/api/autotask/${taskId}/execute`, { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ confirmed: true, }), }) .then((response) => response.json()) .then((result) => { if (result.success) { showToast("Task execution started!", "success"); refreshTasks(); } else { showToast(result.error || "Failed to start execution", "error"); } }) .catch((error) => { console.error("Failed to proceed after simulation:", error); showToast("Failed to start execution", "error"); }); } function applyRecommendation(recId) { fetch(`/api/autotask/recommendations/${recId}/apply`, { method: "POST", }) .then((response) => response.json()) .then((result) => { if (result.success) { showToast("Recommendation applied", "success"); // Re-run simulation to show updated results const taskId = document.querySelector(".simulation-result")?.dataset?.taskId; if (taskId) { simulateTask(taskId); } } else { showToast(result.error || "Failed to apply recommendation", "error"); } }) .catch((error) => { console.error("Failed to apply recommendation:", error); showToast("Failed to apply recommendation", "error"); }); } // ============================================================================= // TOAST NOTIFICATIONS // ============================================================================= function showToast(message, type = "info") { // Get or create toast container let container = document.getElementById("toast-container"); if (!container) { container = document.createElement("div"); container.id = "toast-container"; container.className = "toast-container"; document.body.appendChild(container); } // Create toast element const toast = document.createElement("div"); toast.className = `toast toast-${type}`; const icons = { success: "βœ…", error: "❌", warning: "⚠️", info: "ℹ️", }; toast.innerHTML = ` ${icons[type] || icons.info} ${message} `; container.appendChild(toast); // Auto-remove after 5 seconds setTimeout(() => { toast.classList.add("toast-fade-out"); setTimeout(() => { toast.remove(); }, 300); }, 5000); } // ============================================================================= // UTILITY FUNCTIONS // ============================================================================= function formatDuration(seconds) { if (seconds < 60) { return `${Math.round(seconds)}s`; } else if (seconds < 3600) { const minutes = Math.floor(seconds / 60); const secs = Math.round(seconds % 60); return `${minutes}m ${secs}s`; } else { const hours = Math.floor(seconds / 3600); const minutes = Math.floor((seconds % 3600) / 60); return `${hours}h ${minutes}m`; } } function formatRelativeTime(dateString) { if (!dateString) return "N/A"; const date = new Date(dateString); const now = new Date(); const diffMs = date - now; const diffSeconds = Math.floor(diffMs / 1000); const diffMinutes = Math.floor(diffSeconds / 60); const diffHours = Math.floor(diffMinutes / 60); const diffDays = Math.floor(diffHours / 24); if (diffMs < 0) { // Past const absDays = Math.abs(diffDays); const absHours = Math.abs(diffHours); const absMinutes = Math.abs(diffMinutes); if (absDays > 0) return `${absDays}d ago`; if (absHours > 0) return `${absHours}h ago`; if (absMinutes > 0) return `${absMinutes}m ago`; return "just now"; } else { // Future if (diffDays > 0) return `in ${diffDays}d`; if (diffHours > 0) return `in ${diffHours}h`; if (diffMinutes > 0) return `in ${diffMinutes}m`; return "soon"; } } // ============================================================================= // TASK LIFECYCLE HANDLERS // ============================================================================= function onTaskCompleted(task) { showToast(`Task completed: ${task.title || task.id}`, "success"); updateTaskInList(task); updateStats(); } function onTaskFailed(task, error) { showToast(`Task failed: ${task.title || task.id} - ${error}`, "error"); updateTaskInList(task); updateStats(); } function highlightPendingItems() { // Highlight tasks requiring attention document.querySelectorAll(".autotask-item").forEach((item) => { const status = item.dataset.status; if (status === "pending-approval" || status === "pending-decision") { item.classList.add("attention-required"); } else { item.classList.remove("attention-required"); } }); } function loadExecutionLogs(taskId) { const logContainer = document.querySelector( `[data-task-id="${taskId}"] .log-entries`, ); if (!logContainer || logContainer.dataset.loaded === "true") { return; } logContainer.innerHTML = `

Loading logs...

`; fetch(`/api/autotask/${taskId}/logs`) .then((response) => response.json()) .then((logs) => { if (!logs || logs.length === 0) { logContainer.innerHTML = "

No execution logs yet.

"; } else { logContainer.innerHTML = logs .map( (log) => `
${new Date(log.timestamp).toLocaleTimeString()} ${log.level} ${log.message}
`, ) .join(""); } logContainer.dataset.loaded = "true"; }) .catch((error) => { logContainer.innerHTML = `
❌

Failed to load logs: ${error.message}

`; }); }