const HistoryUI = { executions: [], total: 0, page: 0, pageSize: 20, _currentSearch: '', _currentType: '', _currentStatus: '', async load() { const params = { limit: HistoryUI.pageSize, offset: HistoryUI.page * HistoryUI.pageSize }; if (HistoryUI._currentType) params.type = HistoryUI._currentType; if (HistoryUI._currentStatus) params.status = HistoryUI._currentStatus; if (HistoryUI._currentSearch) params.search = HistoryUI._currentSearch; try { const data = await API.executions.history(params); HistoryUI.executions = data.items || []; HistoryUI.total = data.total || 0; HistoryUI.render(); HistoryUI._renderPagination(); } catch (err) { Toast.error(`Erro ao carregar histórico: ${err.message}`); } }, render() { const container = document.getElementById('history-list'); if (!container) return; if (HistoryUI.executions.length === 0) { container.innerHTML = `

Nenhuma execução encontrada

O histórico de execuções aparecerá aqui.

`; if (window.lucide) lucide.createIcons({ nodes: [container] }); return; } container.innerHTML = HistoryUI.executions.map((exec) => HistoryUI._renderCard(exec)).join(''); if (window.lucide) lucide.createIcons({ nodes: [container] }); }, _renderCard(exec) { const typeBadge = exec.type === 'pipeline' ? 'Pipeline' : 'Agente'; const statusBadge = HistoryUI._statusBadge(exec.status); const name = exec.type === 'pipeline' ? (exec.pipelineName || 'Pipeline') : (exec.agentName || 'Agente'); const task = exec.type === 'pipeline' ? (exec.input || '') : (exec.task || ''); const date = HistoryUI._formatDate(exec.startedAt); const duration = HistoryUI._formatDuration(exec.startedAt, exec.endedAt); const cost = exec.costUsd || exec.totalCostUsd || 0; const costHtml = cost > 0 ? `$${cost.toFixed(4)}` : ''; return `
${typeBadge} ${HistoryUI._escapeHtml(name)}
${statusBadge} ${date}
${HistoryUI._escapeHtml(task)} ${duration} ${costHtml}
`; }, _renderPagination() { const container = document.getElementById('history-pagination'); if (!container) return; const totalPages = Math.ceil(HistoryUI.total / HistoryUI.pageSize); if (totalPages <= 1) { container.innerHTML = ''; return; } const hasPrev = HistoryUI.page > 0; const hasNext = HistoryUI.page < totalPages - 1; const start = HistoryUI.page * HistoryUI.pageSize + 1; const end = Math.min((HistoryUI.page + 1) * HistoryUI.pageSize, HistoryUI.total); container.innerHTML = ` `; if (window.lucide) lucide.createIcons({ nodes: [container] }); document.getElementById('history-prev-btn')?.addEventListener('click', () => { HistoryUI.page--; HistoryUI.load(); }); document.getElementById('history-next-btn')?.addEventListener('click', () => { HistoryUI.page++; HistoryUI.load(); }); }, filter(search, type, status) { HistoryUI._currentSearch = search || ''; HistoryUI._currentType = type || ''; HistoryUI._currentStatus = status || ''; HistoryUI.page = 0; HistoryUI.load(); }, async viewDetail(id) { try { const exec = await API.executions.get(id); const modal = document.getElementById('execution-detail-modal-overlay'); const title = document.getElementById('execution-detail-title'); const content = document.getElementById('execution-detail-content'); if (!modal || !title || !content) return; const name = exec.type === 'pipeline' ? (exec.pipelineName || 'Pipeline') : (exec.agentName || 'Agente'); title.textContent = name; content.innerHTML = exec.type === 'pipeline' ? HistoryUI._renderPipelineDetail(exec) : HistoryUI._renderAgentDetail(exec); Modal.open('execution-detail-modal-overlay'); if (window.lucide) lucide.createIcons({ nodes: [content] }); content.querySelectorAll('.pipeline-step-prompt-toggle').forEach((btn) => { btn.addEventListener('click', () => { const stepCard = btn.closest('.pipeline-step-detail'); const promptBody = stepCard?.querySelector('.pipeline-step-prompt-body'); if (!promptBody) return; const isHidden = promptBody.hidden; promptBody.hidden = !isHidden; btn.setAttribute('aria-expanded', String(isHidden)); }); }); } catch (err) { Toast.error(`Erro ao carregar execução: ${err.message}`); } }, _renderAgentDetail(exec) { const duration = HistoryUI._formatDuration(exec.startedAt, exec.endedAt); const startDate = HistoryUI._formatDate(exec.startedAt); const endDate = exec.endedAt ? HistoryUI._formatDate(exec.endedAt) : '—'; const resultBlock = exec.result ? `
${HistoryUI._escapeHtml(exec.result)}
` : ''; const errorBlock = exec.error ? `` : ''; return `
Agente ${HistoryUI._escapeHtml(exec.agentName || exec.agentId || '—')}
Status ${HistoryUI._statusBadge(exec.status)}
Início ${startDate}
Fim ${endDate}
Duração ${duration}
${exec.exitCode !== undefined && exec.exitCode !== null ? `
Exit Code ${exec.exitCode}
` : ''} ${exec.costUsd || exec.totalCostUsd ? `
Custo $${(exec.costUsd || exec.totalCostUsd || 0).toFixed(4)}
` : ''} ${exec.numTurns ? `
Turnos ${exec.numTurns}
` : ''}
${exec.task ? `

Tarefa

${HistoryUI._escapeHtml(exec.task)}

` : ''} ${resultBlock ? `

Resultado

${resultBlock}
` : ''} ${errorBlock ? `

Erro

${errorBlock}
` : ''} `; }, _renderPipelineDetail(exec) { const duration = HistoryUI._formatDuration(exec.startedAt, exec.endedAt); const startDate = HistoryUI._formatDate(exec.startedAt); const endDate = exec.endedAt ? HistoryUI._formatDate(exec.endedAt) : '—'; const steps = Array.isArray(exec.steps) ? exec.steps : []; const stepsHtml = steps.map((step, index) => { const stepDuration = HistoryUI._formatDuration(step.startedAt, step.endedAt); const isLast = index === steps.length - 1; return `
${step.stepIndex + 1}
${isLast ? '' : ''}
${HistoryUI._escapeHtml(step.agentName || step.agentId || 'Agente')} ${HistoryUI._statusBadge(step.status)}
${stepDuration} ${step.costUsd ? `$${step.costUsd.toFixed(4)}` : ''}
${step.prompt ? `
` : ''} ${step.result ? `
Resultado
${HistoryUI._escapeHtml(step.result)}
` : ''} ${step.status === 'error' ? `
Passo falhou.
` : ''}
`; }).join(''); return `
Pipeline ${HistoryUI._escapeHtml(exec.pipelineName || exec.pipelineId || '—')}
Status ${HistoryUI._statusBadge(exec.status)}
Início ${startDate}
Fim ${endDate}
Duração ${duration}
${exec.totalCostUsd ? `
Custo Total $${(exec.totalCostUsd || 0).toFixed(4)}
` : ''}
${exec.input ? `

Input Inicial

${HistoryUI._escapeHtml(exec.input)}

` : ''} ${steps.length > 0 ? `

Passos do Pipeline

${stepsHtml}
` : ''} ${exec.error ? `

Erro

${HistoryUI._escapeHtml(exec.error)}
` : ''} `; }, async deleteExecution(id) { const confirmed = await Modal.confirm( 'Excluir execução', 'Tem certeza que deseja excluir esta execução do histórico? Esta ação não pode ser desfeita.' ); if (!confirmed) return; try { await API.executions.delete(id); Toast.success('Execução excluída do histórico'); await HistoryUI.load(); } catch (err) { Toast.error(`Erro ao excluir execução: ${err.message}`); } }, async clearHistory() { const confirmed = await Modal.confirm( 'Limpar histórico', 'Tem certeza que deseja excluir todo o histórico de execuções? Esta ação não pode ser desfeita.' ); if (!confirmed) return; try { await API.executions.clearAll(); Toast.success('Histórico limpo com sucesso'); HistoryUI.page = 0; await HistoryUI.load(); } catch (err) { Toast.error(`Erro ao limpar histórico: ${err.message}`); } }, _statusBadge(status) { const map = { running: ['badge-running', 'Em execução'], completed: ['badge-active', 'Concluído'], error: ['badge-error', 'Erro'], awaiting_approval: ['badge-warning', 'Aguardando'], rejected: ['badge-error', 'Rejeitado'], canceled: ['badge-inactive', 'Cancelado'], }; const [cls, label] = map[status] || ['badge-inactive', status || 'Desconhecido']; return `${label}`; }, _formatDuration(start, end) { if (!start) return '—'; const startMs = new Date(start).getTime(); const endMs = end ? new Date(end).getTime() : Date.now(); const totalSeconds = Math.floor((endMs - startMs) / 1000); if (totalSeconds < 0) return '—'; if (totalSeconds < 60) return `${totalSeconds}s`; const hours = Math.floor(totalSeconds / 3600); const minutes = Math.floor((totalSeconds % 3600) / 60); const seconds = totalSeconds % 60; if (hours > 0) return `${hours}h ${minutes}m`; return `${minutes}m ${seconds}s`; }, _formatDate(iso) { if (!iso) return '—'; const date = new Date(iso); return date.toLocaleString('pt-BR', { day: '2-digit', month: '2-digit', year: 'numeric', hour: '2-digit', minute: '2-digit', }); }, _escapeHtml(str) { if (!str) return ''; return String(str) .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"') .replace(/'/g, '''); }, }; window.HistoryUI = HistoryUI;