/** * Toolbar — top bar with session info and actions */ import { state, deleteSession, toggleTheme } from '../app.js'; import { api } from '../api.js'; let sessionIdEl; let statusBadge; export function initToolbar() { const toolbar = document.getElementById('toolbar'); toolbar.innerHTML = ` Agentic Microservice No session
`; sessionIdEl = document.getElementById('toolbar-session-id'); statusBadge = document.getElementById('toolbar-status'); // Copy session ID sessionIdEl.addEventListener('click', () => { if (state.activeSessionId) { navigator.clipboard.writeText(state.activeSessionId); const orig = sessionIdEl.textContent; sessionIdEl.textContent = 'Copied!'; setTimeout(() => { sessionIdEl.textContent = orig; }, 1000); } }); // Refresh document.getElementById('btn-refresh').addEventListener('click', async () => { if (!state.activeSessionId) return; try { const session = await api.getSession(state.activeSessionId); state.sessionState = session; updateToolbar(session); // Dispatch event so inspector updates too const { updateInspector } = await import('./session-inspector.js'); updateInspector(session); } catch (e) { alert(`Refresh failed: ${e.message}`); } }); // Context debug document.getElementById('btn-context-debug').addEventListener('click', async () => { if (!state.activeSessionId) return; try { const res = await fetch(`/api/v1/sessions/${state.activeSessionId}/context-debug`); const data = await res.json(); const win = window.open('', '_blank', 'width=900,height=700'); win.document.title = 'Context Debug'; win.document.write(renderContextDebugHTML(data)); } catch (e) { alert(`Failed: ${e.message}`); } }); // Raw state document.getElementById('btn-raw-state').addEventListener('click', async () => { if (!state.activeSessionId) return; try { const session = await api.getSession(state.activeSessionId); const win = window.open('', '_blank', 'width=600,height=500'); win.document.write(`
${JSON.stringify(session, null, 2)}
`); } catch (e) { alert(`Failed: ${e.message}`); } }); // Theme document.getElementById('btn-theme').addEventListener('click', toggleTheme); // Delete document.getElementById('btn-delete-session').addEventListener('click', () => { if (!state.activeSessionId) return; if (confirm('Delete this session permanently?')) { deleteSession(state.activeSessionId); } }); } export function updateToolbar(session) { if (!session) { sessionIdEl.textContent = 'No session'; statusBadge.style.display = 'none'; return; } sessionIdEl.textContent = session.session_id; statusBadge.textContent = session.status; statusBadge.className = `badge ${session.status}`; statusBadge.style.display = ''; } function renderContextDebugHTML(data) { const css = ` `; if (!data.history || data.history.length === 0) { return css + '

Context Debug

No context builds yet. Send a message first.
'; } let html = css + ''; html += '

Context Engine Debug

'; html += '
Session: ' + data.session_id + ' — ' + data.total_builds + ' context build(s)
'; // Reverse to show most recent first const builds = [...data.history].reverse(); for (let i = 0; i < builds.length; i++) { const b = builds[i]; const time = new Date(b.timestamp * 1000).toLocaleTimeString('en-US', { hour12: false, fractionalSecondDigits: 2 }); const maxTokens = 120000; const pct = Math.min(100, (b.total_tokens / maxTokens) * 100); const barColor = pct > 80 ? '#f85149' : pct > 50 ? '#d29922' : '#3fb950'; html += '
'; html += '
'; html += '' + b.agent + ''; html += '~' + b.total_tokens.toLocaleString() + ' tokens'; html += '' + b.sections_count + ' sections'; if (b.compacted) html += 'COMPACTED'; html += '' + time + ''; html += '
'; // Token usage bar html += '
'; html += '
' + pct.toFixed(1) + '% of context window (' + b.total_tokens.toLocaleString() + ' / ' + maxTokens.toLocaleString() + ')
'; // Sections table html += '

Sections

'; html += '
TypeTokensPrioPreview
'; for (const s of b.sections) { html += '
'; html += '' + s.type + ''; html += '' + s.tokens + ''; html += '' + s.priority + ''; html += '' + escapeHtml(s.preview) + ''; html += '
'; } // User message html += '

User Message Sent to Model

'; html += '
' + escapeHtml(b.user_message_preview) + '
'; // Extra info html += '
'; html += 'Artifacts: ' + b.artifacts_count + ' | Working items: ' + b.working_items_count + ' | System prompt tokens: ' + b.system_prompt_tokens; html += '
'; html += '
'; } html += ''; return html; } function escapeHtml(str) { if (!str) return ''; return str.replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"'); }