/* ============================================================================= 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 = `
`; }); } 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 = ` `; }); } 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 = ` `; }); } function renderDecisions(taskId, decisions) { const container = document.getElementById("decision-content"); if (!decisions || decisions.length === 0) { container.innerHTML = 'No pending decisions.
'; return; } let html = '${decision.description}
No pending approvals.
'; return; } let html = '${approval.description}
${approval.impact_summary}
Running impact simulation...
Loading decisions...
Loading approvals...
Loading logs...
No execution logs yet.
"; } else { logContainer.innerHTML = logs .map( (log) => `