const AgentsUI = { agents: [], avatarColors: [ '#6366f1', '#8b5cf6', '#ec4899', '#f59e0b', '#10b981', '#3b82f6', '#ef4444', '#14b8a6', ], async load() { try { AgentsUI.agents = await API.agents.list(); AgentsUI.render(); } catch (err) { Toast.error(`Erro ao carregar agentes: ${err.message}`); } }, render(filteredAgents) { const grid = document.getElementById('agents-grid'); const empty = document.getElementById('agents-empty-state'); if (!grid) return; const agents = filteredAgents || AgentsUI.agents; const existingCards = grid.querySelectorAll('.agent-card'); existingCards.forEach((c) => c.remove()); if (agents.length === 0) { if (empty) empty.style.display = 'flex'; return; } if (empty) empty.style.display = 'none'; const sorted = [...agents].sort((a, b) => { const rank = (tags) => { if ((tags || []).some((t) => t.toLowerCase() === 'lider')) return 0; if ((tags || []).some((t) => t.toLowerCase() === 'po' || t.toLowerCase() === 'product-owner')) return 1; return 2; }; return rank(a.tags) - rank(b.tags); }); const fragment = document.createDocumentFragment(); sorted.forEach((agent) => { const wrapper = document.createElement('div'); wrapper.innerHTML = AgentsUI.renderCard(agent); fragment.appendChild(wrapper.firstElementChild); }); grid.appendChild(fragment); Utils.refreshIcons(grid); }, filter(searchText, statusFilter) { const search = (searchText || '').toLowerCase(); const status = statusFilter || ''; const filtered = AgentsUI.agents.filter((a) => { const name = (a.agent_name || '').toLowerCase(); const desc = (a.description || '').toLowerCase(); const tags = (a.tags || []).join(' ').toLowerCase(); const matchesSearch = !search || name.includes(search) || desc.includes(search) || tags.includes(search); const matchesStatus = !status || a.status === status; return matchesSearch && matchesStatus; }); AgentsUI.render(filtered); }, renderCard(agent) { const name = agent.agent_name || agent.name || 'Sem nome'; const color = AgentsUI.getAvatarColor(name); const initials = AgentsUI.getInitials(name); const statusLabel = agent.status === 'active' ? 'Ativo' : 'Inativo'; const statusClass = agent.status === 'active' ? 'badge-active' : 'badge-inactive'; const model = (agent.config && agent.config.model) || agent.model || 'claude-sonnet-4-6'; const updatedAt = AgentsUI.formatDate(agent.updated_at || agent.updatedAt || agent.created_at || agent.createdAt); const tags = Array.isArray(agent.tags) && agent.tags.length > 0 ? `
${agent.tags.map((t) => `${Utils.escapeHtml(t)}`).join('')}
` : ''; const isLeader = Array.isArray(agent.tags) && agent.tags.some((t) => t.toLowerCase() === 'lider'); const isPO = !isLeader && Array.isArray(agent.tags) && agent.tags.some((t) => t.toLowerCase() === 'po' || t.toLowerCase() === 'product-owner'); const roleClass = isLeader ? ' agent-card--leader' : isPO ? ' agent-card--po' : ''; const roleBadge = isLeader ? '' : isPO ? '' : ''; return `

${roleBadge}${Utils.escapeHtml(name)}

${statusLabel}
${agent.description ? `

${Utils.escapeHtml(agent.description)}

` : ''} ${tags}
${model} ${updatedAt}
`; }, openCreateModal() { const form = document.getElementById('agent-form'); if (form) form.reset(); const idField = document.getElementById('agent-form-id'); if (idField) idField.value = ''; const titleEl = document.getElementById('agent-modal-title'); if (titleEl) titleEl.textContent = 'Novo Agente'; const toggle = document.getElementById('agent-status-toggle'); if (toggle) toggle.checked = true; const tagsHidden = document.getElementById('agent-tags'); if (tagsHidden) tagsHidden.value = '[]'; const tagsChips = document.getElementById('agent-tags-chips'); if (tagsChips) tagsChips.innerHTML = ''; const allowedTools = document.getElementById('agent-allowed-tools'); if (allowedTools) allowedTools.value = ''; const maxTurns = document.getElementById('agent-max-turns'); if (maxTurns) maxTurns.value = '0'; const permissionMode = document.getElementById('agent-permission-mode'); if (permissionMode) permissionMode.value = ''; const retryToggle = document.getElementById('agent-retry-toggle'); if (retryToggle) retryToggle.checked = false; const retryMaxGroup = document.getElementById('agent-retry-max-group'); if (retryMaxGroup) retryMaxGroup.style.display = 'none'; const retryMax = document.getElementById('agent-retry-max'); if (retryMax) retryMax.value = '3'; const secretsSection = document.getElementById('agent-secrets-section'); if (secretsSection) secretsSection.hidden = true; const secretsList = document.getElementById('agent-secrets-list'); if (secretsList) secretsList.innerHTML = ''; Modal.open('agent-modal-overlay'); AgentsUI._setupModalListeners(); }, async openEditModal(agentId) { try { const agent = await API.agents.get(agentId); const titleEl = document.getElementById('agent-modal-title'); if (titleEl) titleEl.textContent = 'Editar Agente'; const fields = { 'agent-form-id': agent.id, 'agent-name': agent.agent_name || agent.name || '', 'agent-description': agent.description || '', 'agent-system-prompt': (agent.config && agent.config.systemPrompt) || '', 'agent-model': (agent.config && agent.config.model) || 'claude-sonnet-4-6', 'agent-workdir': (agent.config && agent.config.workingDirectory) || '', 'agent-allowed-tools': (agent.config && agent.config.allowedTools) || '', 'agent-max-turns': (agent.config && agent.config.maxTurns) || 0, 'agent-permission-mode': (agent.config && agent.config.permissionMode) || '', }; for (const [fieldId, value] of Object.entries(fields)) { const el = document.getElementById(fieldId); if (el) el.value = value; } const toggle = document.getElementById('agent-status-toggle'); if (toggle) toggle.checked = agent.status === 'active'; const tags = Array.isArray(agent.tags) ? agent.tags : []; const tagsHidden = document.getElementById('agent-tags'); if (tagsHidden) tagsHidden.value = JSON.stringify(tags); const tagsChips = document.getElementById('agent-tags-chips'); if (tagsChips) { tagsChips.innerHTML = tags.map((t) => `${t}` ).join(''); } const retryToggle = document.getElementById('agent-retry-toggle'); const retryOnFailure = agent.config && agent.config.retryOnFailure; if (retryToggle) retryToggle.checked = !!retryOnFailure; const retryMaxGroup = document.getElementById('agent-retry-max-group'); if (retryMaxGroup) retryMaxGroup.style.display = retryOnFailure ? '' : 'none'; const retryMax = document.getElementById('agent-retry-max'); if (retryMax) retryMax.value = (agent.config && agent.config.maxRetries) || '3'; const secretsSection = document.getElementById('agent-secrets-section'); if (secretsSection) secretsSection.hidden = false; AgentsUI._loadSecrets(agent.id); Modal.open('agent-modal-overlay'); AgentsUI._setupModalListeners(); } catch (err) { Toast.error(`Erro ao carregar agente: ${err.message}`); } }, async save() { const idEl = document.getElementById('agent-form-id'); const id = idEl ? idEl.value.trim() : ''; const nameEl = document.getElementById('agent-name'); if (!nameEl || !nameEl.value.trim()) { Toast.warning('Nome do agente é obrigatório'); return; } const tagsHidden = document.getElementById('agent-tags'); let tags = []; try { tags = JSON.parse(tagsHidden?.value || '[]'); } catch { tags = []; } const toggle = document.getElementById('agent-status-toggle'); const data = { agent_name: nameEl.value.trim(), description: document.getElementById('agent-description')?.value.trim() || '', tags, status: toggle && toggle.checked ? 'active' : 'inactive', config: { systemPrompt: document.getElementById('agent-system-prompt')?.value.trim() || '', model: document.getElementById('agent-model')?.value || 'claude-sonnet-4-6', workingDirectory: document.getElementById('agent-workdir')?.value.trim() || '', allowedTools: document.getElementById('agent-allowed-tools')?.value.trim() || '', maxTurns: parseInt(document.getElementById('agent-max-turns')?.value) || 0, permissionMode: document.getElementById('agent-permission-mode')?.value || '', retryOnFailure: !!document.getElementById('agent-retry-toggle')?.checked, maxRetries: parseInt(document.getElementById('agent-retry-max')?.value) || 3, }, }; try { if (id) { await API.agents.update(id, data); Toast.success('Agente atualizado com sucesso'); } else { await API.agents.create(data); Toast.success('Agente criado com sucesso'); } Modal.close('agent-modal-overlay'); await AgentsUI.load(); } catch (err) { Toast.error(`Erro ao salvar agente: ${err.message}`); } }, async delete(agentId) { const confirmed = await Modal.confirm( 'Excluir agente', 'Tem certeza que deseja excluir este agente? Esta ação não pode ser desfeita.' ); if (!confirmed) return; try { await API.agents.delete(agentId); Toast.success('Agente excluído com sucesso'); await AgentsUI.load(); } catch (err) { Toast.error(`Erro ao excluir agente: ${err.message}`); } }, async execute(agentId) { try { const allAgents = AgentsUI.agents.length > 0 ? AgentsUI.agents : await API.agents.list(); const selectEl = document.getElementById('execute-agent-select'); if (selectEl) { selectEl.innerHTML = '' + allAgents .filter((a) => a.status === 'active') .map((a) => ``) .join(''); selectEl.value = agentId; } const hiddenId = document.getElementById('execute-agent-id'); if (hiddenId) hiddenId.value = agentId; const taskEl = document.getElementById('execute-task-desc'); if (taskEl) taskEl.value = ''; const instructionsEl = document.getElementById('execute-instructions'); if (instructionsEl) instructionsEl.value = ''; if (App._executeDropzone) App._executeDropzone.reset(); AgentsUI._loadSavedTasks(); Modal.open('execute-modal-overlay'); } catch (err) { Toast.error(`Erro ao abrir modal de execução: ${err.message}`); } }, async _loadSavedTasks() { const savedTaskSelect = document.getElementById('execute-saved-task'); if (!savedTaskSelect) return; try { const tasks = await API.tasks.list(); savedTaskSelect.innerHTML = '' + tasks.map((t) => { const label = t.category ? `[${t.category.toUpperCase()}] ${t.name}` : t.name; return ``; }).join(''); AgentsUI._savedTasksCache = tasks; } catch { savedTaskSelect.innerHTML = ''; AgentsUI._savedTasksCache = []; } }, _savedTasksCache: [], async duplicate(agentId) { try { await API.agents.duplicate(agentId); Toast.success('Agente duplicado com sucesso'); await AgentsUI.load(); } catch (err) { Toast.error(`Erro ao duplicar agente: ${err.message}`); } }, async export(agentId) { try { const data = await API.agents.export(agentId); const jsonEl = document.getElementById('export-code-content'); if (jsonEl) jsonEl.textContent = JSON.stringify(data, null, 2); Modal.open('export-modal-overlay'); } catch (err) { Toast.error(`Erro ao exportar agente: ${err.message}`); } }, openImportModal() { const textarea = document.getElementById('import-json-content'); if (textarea) textarea.value = ''; Modal.open('import-modal-overlay'); }, async importAgent() { const textarea = document.getElementById('import-json-content'); if (!textarea || !textarea.value.trim()) { Toast.warning('Cole o JSON do agente para importar'); return; } let data; try { data = JSON.parse(textarea.value.trim()); } catch { Toast.error('JSON inválido'); return; } try { await API.agents.import(data); Toast.success('Agente importado com sucesso'); Modal.close('import-modal-overlay'); await AgentsUI.load(); } catch (err) { Toast.error(`Erro ao importar agente: ${err.message}`); } }, getAvatarColor(name) { let hash = 0; for (let i = 0; i < name.length; i++) { hash = name.charCodeAt(i) + ((hash << 5) - hash); } const index = Math.abs(hash) % AgentsUI.avatarColors.length; return AgentsUI.avatarColors[index]; }, getInitials(name) { return name .split(' ') .slice(0, 2) .map((w) => w[0]) .join('') .toUpperCase(); }, formatDate(isoString) { if (!isoString) return '—'; const date = new Date(isoString); return date.toLocaleDateString('pt-BR', { day: '2-digit', month: '2-digit', year: 'numeric', hour: '2-digit', minute: '2-digit', }); }, _setupModalListeners() { const retryToggle = document.getElementById('agent-retry-toggle'); const retryMaxGroup = document.getElementById('agent-retry-max-group'); if (retryToggle && !retryToggle._listenerAdded) { retryToggle._listenerAdded = true; retryToggle.addEventListener('change', () => { if (retryMaxGroup) retryMaxGroup.style.display = retryToggle.checked ? '' : 'none'; }); } const addSecretBtn = document.getElementById('agent-secret-add-btn'); if (addSecretBtn && !addSecretBtn._listenerAdded) { addSecretBtn._listenerAdded = true; addSecretBtn.addEventListener('click', () => { const agentId = document.getElementById('agent-form-id')?.value; if (agentId) { AgentsUI._addSecret(agentId); } else { Toast.warning('Salve o agente primeiro para adicionar secrets'); } }); } }, async _loadSecrets(agentId) { const list = document.getElementById('agent-secrets-list'); if (!list) return; try { const secrets = await API.secrets.list(agentId); const items = Array.isArray(secrets) ? secrets : (secrets?.secrets || []); if (items.length === 0) { list.innerHTML = '

Nenhum secret configurado.

'; return; } list.innerHTML = items.map(s => `
${Utils.escapeHtml(s.name || s)} ••••••••
`).join(''); Utils.refreshIcons(list); list.querySelectorAll('[data-secret-delete]').forEach(btn => { btn.addEventListener('click', () => { AgentsUI._deleteSecret(btn.dataset.agentId, btn.dataset.secretDelete); }); }); } catch { list.innerHTML = '

Erro ao carregar secrets.

'; } }, async _addSecret(agentId) { const nameEl = document.getElementById('agent-secret-name'); const valueEl = document.getElementById('agent-secret-value'); const name = nameEl?.value.trim(); const value = valueEl?.value; if (!name) { Toast.warning('Nome do secret é obrigatório'); return; } if (!value) { Toast.warning('Valor do secret é obrigatório'); return; } try { await API.secrets.create(agentId, { name, value }); Toast.success(`Secret "${name}" salvo`); if (nameEl) nameEl.value = ''; if (valueEl) valueEl.value = ''; AgentsUI._loadSecrets(agentId); } catch (err) { Toast.error(`Erro ao salvar secret: ${err.message}`); } }, async _deleteSecret(agentId, secretName) { const confirmed = await Modal.confirm( 'Remover secret', `Tem certeza que deseja remover o secret "${secretName}"?` ); if (!confirmed) return; try { await API.secrets.delete(agentId, secretName); Toast.success(`Secret "${secretName}" removido`); AgentsUI._loadSecrets(agentId); } catch (err) { Toast.error(`Erro ao remover secret: ${err.message}`); } }, async openVersionsModal(agentId) { const agent = AgentsUI.agents.find(a => a.id === agentId); const titleEl = document.getElementById('agent-versions-title'); const contentEl = document.getElementById('agent-versions-content'); if (titleEl) titleEl.textContent = `Versões — ${agent?.agent_name || agent?.name || 'Agente'}`; if (contentEl) { contentEl.innerHTML = '
Carregando versões...
'; } Modal.open('agent-versions-modal-overlay'); try { const versions = await API.versions.list(agentId); const items = Array.isArray(versions) ? versions : (versions?.versions || []); if (!contentEl) return; if (items.length === 0) { contentEl.innerHTML = `

Sem histórico de versões

As alterações neste agente serão registradas aqui automaticamente.

`; Utils.refreshIcons(contentEl); return; } contentEl.innerHTML = `
${items.map((v, i) => { const date = v.changedAt ? new Date(v.changedAt).toLocaleString('pt-BR') : '—'; const changedFields = AgentsUI._getChangedFields(v); const isLatest = i === 0; return `
${i < items.length - 1 ? '
' : ''}
v${v.version || items.length - i} ${date} ${!isLatest ? `` : 'Atual'}
${changedFields ? `
${changedFields}
` : ''} ${v.changelog ? `

${Utils.escapeHtml(v.changelog)}

` : ''}
`; }).join('')}
`; Utils.refreshIcons(contentEl); contentEl.querySelectorAll('[data-restore-version]').forEach(btn => { btn.addEventListener('click', async () => { const version = btn.dataset.restoreVersion; const aid = btn.dataset.agentId; const confirmed = await Modal.confirm( 'Restaurar versão', `Deseja restaurar a versão v${version} deste agente? A configuração atual será substituída.` ); if (!confirmed) return; try { await API.versions.restore(aid, version); Toast.success(`Versão v${version} restaurada`); Modal.close('agent-versions-modal-overlay'); await AgentsUI.load(); } catch (err) { Toast.error(`Erro ao restaurar versão: ${err.message}`); } }); }); } catch (err) { if (contentEl) { contentEl.innerHTML = `

Erro ao carregar versões

${Utils.escapeHtml(err.message)}

`; Utils.refreshIcons(contentEl); } } }, _getChangedFields(version) { if (!version.config) return ''; const fieldLabels = { systemPrompt: 'System Prompt', model: 'Modelo', workingDirectory: 'Diretório', allowedTools: 'Ferramentas', maxTurns: 'Max Turns', permissionMode: 'Permission Mode', retryOnFailure: 'Retry', }; const fields = Object.keys(version.config || {}).filter(k => fieldLabels[k]); if (fields.length === 0) return ''; return fields.map(f => `${fieldLabels[f] || f}` ).join(''); }, }; window.AgentsUI = AgentsUI;