From b2ba3f0e693a7313f876e1e0cfe335d4784a2d02 Mon Sep 17 00:00:00 2001 From: Peter Wagstaff Date: Thu, 7 May 2026 15:40:40 -0700 Subject: [PATCH 1/5] Add /api/events endpoint for simple event ingestion --- server.js | 63 +++++++------------------------------------------------ 1 file changed, 7 insertions(+), 56 deletions(-) diff --git a/server.js b/server.js index 7b17ecd..d8f68d3 100644 --- a/server.js +++ b/server.js @@ -123,12 +123,8 @@ const server = http.createServer(async (req, res) => { attributes: body.attributes || {}, received_at: new Date().toISOString(), }; - if (ev.event_type === 'demo.reset') { - events = []; - } else { - events.push(ev); - if (events.length > MAX_EVENTS) events = events.slice(-MAX_EVENTS); - } + events.push(ev); + if (events.length > MAX_EVENTS) events = events.slice(-MAX_EVENTS); broadcastEvent(ev); return sendJson(res, 200, {}); } @@ -138,12 +134,8 @@ const server = http.createServer(async (req, res) => { const body = await parseBody(req); for (const ev of translateOtlpLogs(body)) { ev.received_at = new Date().toISOString(); - if (ev.event_type === 'demo.reset') { - events = []; - } else { - events.push(ev); - if (events.length > MAX_EVENTS) events = events.slice(-MAX_EVENTS); - } + events.push(ev); + if (events.length > MAX_EVENTS) events = events.slice(-MAX_EVENTS); broadcastEvent(ev); } return sendJson(res, 200, { partialSuccess: {} }); @@ -228,48 +220,6 @@ const server = http.createServer(async (req, res) => { } } - // POST /api/reset - proxy to demo-admin reset endpoint - if (pathName === '/api/reset' && method === 'POST') { - const adminHost = process.env.ADMIN_HOST || 'demo-admin'; - const adminPort = process.env.ADMIN_PORT || '8080'; - return new Promise((resolve) => { - let responded = false; - const proxyReq = http.request({ - hostname: adminHost, - port: parseInt(adminPort), - path: '/api/reset', - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - timeout: 30000, - }, (proxyRes) => { - let body = ''; - proxyRes.on('data', chunk => { body += chunk; }); - proxyRes.on('end', () => { - if (responded) return; - responded = true; - events = []; - res.writeHead(proxyRes.statusCode, { 'Content-Type': 'application/json' }); - res.end(body); - resolve(); - }); - }); - proxyReq.on('error', (err) => { - if (responded) return; - responded = true; - sendJson(res, 502, { error: 'Failed to reach demo-admin: ' + err.message }); - resolve(); - }); - proxyReq.on('timeout', () => { - proxyReq.destroy(); - if (responded) return; - responded = true; - sendJson(res, 504, { error: 'demo-admin request timed out' }); - resolve(); - }); - proxyReq.end(); - }); - } - // Serve static files for everything else serveStatic(req, res); } catch (e) { @@ -283,8 +233,9 @@ const wss = new WebSocketServer({ server, path: '/ws' }); wss.on('connection', (ws) => { wsClients.add(ws); - // Send event history on connect - ws.send(JSON.stringify({ type: 'history', events })); + // Send event history on connect, sorted chronologically + const sortedEvents = [...events].sort((a, b) => (a.timestamp < b.timestamp ? -1 : a.timestamp > b.timestamp ? 1 : 0)); + ws.send(JSON.stringify({ type: 'history', events: sortedEvents })); ws.on('close', () => { wsClients.delete(ws); From 9e93e336df98727906e07a71d835165d90cbcae7 Mon Sep 17 00:00:00 2001 From: Peter Wagstaff Date: Thu, 7 May 2026 15:40:50 -0700 Subject: [PATCH 2/5] Update agent lifecycle event handling in topology --- public/index.html | 67 +++++------------------------------------------ 1 file changed, 7 insertions(+), 60 deletions(-) diff --git a/public/index.html b/public/index.html index 6a82bc7..c71207f 100644 --- a/public/index.html +++ b/public/index.html @@ -42,27 +42,6 @@ margin-right: 2px; } - .btn-reset { - padding: 2px 8px; - font-size: 10px; - font-family: var(--font-sans); - font-weight: 500; - color: var(--color-muted); - background: transparent; - border: 1px solid rgba(139, 148, 158, 0.35); - border-radius: 4px; - cursor: pointer; - text-transform: none; - letter-spacing: normal; - transition: background 0.15s, border-color 0.15s, color 0.15s; - } - - .btn-reset:hover { - color: var(--color-text); - background: rgba(139, 148, 158, 0.1); - border-color: rgba(139, 148, 158, 0.4); - } - .panel-content { flex: 1; overflow-y: auto; @@ -543,7 +522,6 @@ Dashboard Status: CONNECTING -
@@ -649,13 +627,15 @@ switch (eventType) { case 'agent.created': - return 'Agent ' + (attrs['agent.id'] || '') + ' provisioned'; + return (attrs['user.id'] || '') + ' → ' + (attrs['agent.id'] || ''); case 'agent.terminated': return 'Agent ' + (attrs['agent.id'] || '') + ' terminated'; case 'request.allowed': return (attrs['http.host'] || '') + ' -- allowed'; case 'request.denied': return (attrs['http.host'] || '') + ' -- denied' + (attrs['denial.reason'] ? ': ' + attrs['denial.reason'] : ''); + case 'agent.prompted': + return (attrs['user.id'] || 'user') + ' → ' + (attrs['agent.id'] || 'agent'); default: if (attrs['agent.id']) return eventType + ' for ' + attrs['agent.id']; return eventType; @@ -694,12 +674,6 @@ const attrs = event.attributes || {}; const agentId = attrs['agent.id'] || ''; - // Demo reset — clear all local state - if (eventType === 'demo.reset') { - clearAllDashboardState(); - return; - } - // User lifecycle. Event names are speculative until the backend // settles its vocabulary; the handler shape stays the same. if (eventType === 'user.created') { @@ -711,12 +685,11 @@ if (userId) Topology.removeUserNode(userId); } - // User actions — light up the user→platform edge. - if (eventType === 'agent.requested' || eventType === 'prompt.sent' || eventType === 'permission.change_requested' || eventType === 'termination.requested') { + // agent.prompted: user→platform then platform→agent sequentially + if (eventType === 'agent.prompted') { const userId = attrs['user.id'] || ''; - if (userId) { - Topology.highlightEdge(userId, Topology.EDGE_HIGHLIGHT_MS); - } + if (userId) Topology.highlightEdge(userId, Topology.EDGE_HIGHLIGHT_MS); + if (agentId) setTimeout(() => Topology.highlightEdge('platform-' + agentId, Topology.EDGE_HIGHLIGHT_MS), 400); } // Agent lifecycle. Light up the platform→agent edge: the platform is @@ -731,11 +704,6 @@ Topology.removeAgentRuntimeNode(agentId); } - // Prompt delivered: platform exec'd into the agent via the supervisor. - if (eventType === 'agent.prompted') { - if (agentId) Topology.highlightEdge('platform-' + agentId, Topology.EDGE_HIGHLIGHT_MS); - } - // Gateway decisions if (eventType === 'auth.allowed' || eventType === 'request.allowed' || eventType === 'request.denied' || eventType === 'permission.checked') { const decision = { @@ -866,27 +834,6 @@ else startRegistryPolling(); }); - // ============================================================ - // Reset button - // ============================================================ - document.getElementById('btn-reset-demo').addEventListener('click', async () => { - const btn = document.getElementById('btn-reset-demo'); - btn.disabled = true; - btn.textContent = 'Resetting...'; - - try { - const res = await fetch('/api/reset', { method: 'POST' }); - if (res.ok) { - clearAllDashboardState(); - } - } catch (err) { - console.error('Reset failed:', err); - } finally { - btn.disabled = false; - btn.textContent = 'Reset Demo'; - } - }); - // ============================================================ // Init // ============================================================ From fcd8cdd3dc842a78a7d80b227f4a117fb8366e5e Mon Sep 17 00:00:00 2001 From: Peter Wagstaff Date: Thu, 7 May 2026 15:40:52 -0700 Subject: [PATCH 3/5] Sort events chronologically in history replay and OTLP batches --- translate.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/translate.js b/translate.js index e11a9da..0864dc0 100644 --- a/translate.js +++ b/translate.js @@ -53,7 +53,7 @@ function translateOtlpLogs(otlp) { } } } - return out; + return out.sort((a, b) => (a.timestamp < b.timestamp ? -1 : a.timestamp > b.timestamp ? 1 : 0)); } // ─── Jaeger trace export → flat events ───────────────────────────────────── @@ -198,7 +198,7 @@ function translateOtlpTraces(payload) { } } } - return out; + return out.sort((a, b) => (a.timestamp < b.timestamp ? -1 : a.timestamp > b.timestamp ? 1 : 0)); } module.exports = { translateOtlpLogs, translateJaegerTraces, translateOtlpTraces }; From 38d212f5a970df005e3a508ded22f00103ee3a9b Mon Sep 17 00:00:00 2001 From: Peter Wagstaff Date: Thu, 7 May 2026 15:40:52 -0700 Subject: [PATCH 4/5] Remove otel-collector batch processor for real-time span forwarding --- otel-collector.yaml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/otel-collector.yaml b/otel-collector.yaml index 19b3769..915a4ef 100644 --- a/otel-collector.yaml +++ b/otel-collector.yaml @@ -4,11 +4,6 @@ receivers: grpc: endpoint: 0.0.0.0:4317 -processors: - batch: - timeout: 1s - send_batch_size: 50 - exporters: otlphttp/dashboard: endpoint: http://dashboard:3000 @@ -20,7 +15,6 @@ service: pipelines: traces: receivers: [otlp] - processors: [batch] exporters: [otlphttp/dashboard] telemetry: From e38b8bdce6ca43c7dcb08c2a93315d3d8496188b Mon Sep 17 00:00:00 2001 From: Peter Wagstaff Date: Thu, 7 May 2026 16:53:45 -0700 Subject: [PATCH 5/5] Remove otel-collector.yaml (config lives in demo repo) --- otel-collector.yaml | 22 ---------------------- 1 file changed, 22 deletions(-) delete mode 100644 otel-collector.yaml diff --git a/otel-collector.yaml b/otel-collector.yaml deleted file mode 100644 index 915a4ef..0000000 --- a/otel-collector.yaml +++ /dev/null @@ -1,22 +0,0 @@ -receivers: - otlp: - protocols: - grpc: - endpoint: 0.0.0.0:4317 - -exporters: - otlphttp/dashboard: - endpoint: http://dashboard:3000 - encoding: json - tls: - insecure: true - -service: - pipelines: - traces: - receivers: [otlp] - exporters: [otlphttp/dashboard] - - telemetry: - logs: - level: info