(function () {
const esc = (s) => String(s ?? '').replace(/&/g,'&').replace(//g,'>');
let state = { threadId: null, selectedAgent: 'A6' };
async function api(path, opts = {}) {
const h = { ...(opts.headers || {}) };
if (!(opts.body instanceof FormData)) h['Content-Type'] = 'application/json';
const t = window.DeskAuth?.getToken?.();
if (t) h.Authorization = `Bearer ${t}`;
const r = await fetch(`/api/v1/agents${path}`, { ...opts, headers: h });
if (!r.ok) throw new Error(`${r.status} ${(await r.text()).slice(0, 200)}`);
return r.json();
}
function agentCard(a) {
const active = state.selectedAgent === a.id ? ' agentic-agent-active' : '';
return `
${esc(a.name)} ${esc(a.id)}
${esc(a.role)}
${(a.actions || []).slice(0, 3).map(x => `- ${esc(x)}
`).join('')}
Aprovação: ${esc(a.approval)}
`;
}
function inboxRow(m) {
return `
${esc(m.agent_name || m.from_id)}
${esc(m.thread_severity || 'info')}
${esc(m.thread_subject || m.message)} · ${esc(m.created_at)}
${esc((m.body || '').slice(0, 280))}
`;
}
function threadBubble(m) {
const isHuman = m.from_type === 'human';
const cls = isHuman ? 'agentic-bubble-human' : 'agentic-bubble-agent';
return `
${esc(m.from_label || m.from_id)} · ${esc(m.created_at)}
${esc(m.body).replace(/\n/g, '
')}
`;
}
async function loadThread(el, threadId) {
state.threadId = threadId;
const box = el.querySelector('#agentic-thread-messages');
if (!box) return;
box.innerHTML = 'Carregando thread…
';
const data = await api(`/threads/${threadId}/messages`);
box.innerHTML = data.messages.map(threadBubble).join('') || 'Sem mensagens.
';
box.scrollTop = box.scrollHeight;
}
async function renderAgenticOps() {
const el = document.getElementById('agentic-ops-content');
if (!el) return;
el.innerHTML = 'Carregando Agentic Ops…
';
try {
const [health, roster, inbox, threads, findings] = await Promise.all([
api('/health'),
api('/roster'),
api('/inbox?limit=20'),
api('/threads?limit=15'),
api('/findings?limit=15'),
]);
const tier = health.tier === 't1' ? 'T1 LLM' : 'T0';
const ollama = health.ollama
? `Ollama · ${esc(health.model)}`
: 'Ollama offline';
const agents = roster.agents || [];
const inboxItems = inbox.messages || [];
const threadOpts = (threads.threads || []).map(t =>
``
).join('');
const fRows = (findings.findings || []).map(f =>
`${esc(f.title)} ${esc(f.severity)}
${f.suggested_human_action ? `
${esc(f.suggested_human_action)}` : ''}`
).join('') || 'Nenhum finding aberto.';
el.innerHTML = `
Agentes (A0–A7)
Clique para seleccionar destino do chat.
${agents.map(agentCard).join('')}
Inbox operadores
Mensagens dos agentes que exigem acção humana.
${inboxItems.length ? inboxItems.map(inboxRow).join('') : 'Inbox vazia.
'}
Findings abertos
Janela de contexto
Seleccione uma thread ou abra da inbox.
Chat Copiloto (${esc(state.selectedAgent)})
`;
el.querySelector('#btn-agentic-refresh')?.addEventListener('click', renderAgenticOps);
el.querySelectorAll('.agentic-agent-card').forEach(card => {
card.addEventListener('click', () => {
state.selectedAgent = card.dataset.agentId;
renderAgenticOps();
});
});
el.querySelectorAll('[data-open-thread]').forEach(btn => {
btn.addEventListener('click', () => loadThread(el, parseInt(btn.dataset.openThread, 10)));
});
el.querySelectorAll('[data-ack-msg]').forEach(btn => {
btn.addEventListener('click', async () => {
await api(`/messages/${btn.dataset.ackMsg}/ack`, { method: 'POST' });
await renderAgenticOps();
});
});
el.querySelector('#agentic-thread-select')?.addEventListener('change', (e) => {
const id = parseInt(e.target.value, 10);
if (id) loadThread(el, id);
});
el.querySelector('#btn-agentic-reply')?.addEventListener('click', async () => {
const input = el.querySelector('#agentic-reply-input');
const tid = state.threadId || parseInt(el.querySelector('#agentic-thread-select')?.value, 10);
const body = (input?.value || '').trim();
if (!tid || !body) return;
await api(`/threads/${tid}/reply`, {
method: 'POST',
body: JSON.stringify({ body, target_agent: state.selectedAgent }),
});
input.value = '';
await loadThread(el, tid);
});
el.querySelector('#btn-agentic-chat')?.addEventListener('click', async () => {
const input = el.querySelector('#agentic-chat-input');
const out = el.querySelector('#agentic-chat-answer');
const q = (input?.value || '').trim();
if (!q) return;
out.hidden = false;
out.innerHTML = 'A pensar…
';
try {
const res = await api('/chat', {
method: 'POST',
body: JSON.stringify({ question: q, include_findings: true, target_agent: state.selectedAgent }),
});
out.innerHTML = `${esc(state.selectedAgent)} (${esc(res.model)})
${esc(res.answer)}
`;
if (res.thread_id) {
state.threadId = res.thread_id;
await loadThread(el, res.thread_id);
}
} catch (err) {
out.innerHTML = `${esc(err.message)}
`;
}
});
if (state.threadId) await loadThread(el, state.threadId);
} catch (err) {
el.innerHTML = `Erro: ${esc(err.message)}
`;
}
}
window.renderAgenticOps = renderAgenticOps;
})();