@agent-e/server 1.7.3 → 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ import {
2
+ AgentEServer
3
+ } from "./chunk-XFI27OM4.mjs";
4
+ export {
5
+ AgentEServer
6
+ };
7
+ //# sourceMappingURL=AgentEServer-O3DNPERG.mjs.map
@@ -699,7 +699,7 @@ function getDashboardHtml() {
699
699
  <header class="header" id="header">
700
700
  <div class="header-left">
701
701
  <span class="header-logo">AgentE</span>
702
- <span class="header-version">v1.7.2</span>
702
+ <span class="header-version">v1.8.0</span>
703
703
  </div>
704
704
  <div class="header-right">
705
705
  <div class="kpi-pill">
@@ -2206,4 +2206,4 @@ var AgentEServer = class {
2206
2206
  export {
2207
2207
  AgentEServer
2208
2208
  };
2209
- //# sourceMappingURL=chunk-AWYCK646.mjs.map
2209
+ //# sourceMappingURL=chunk-XFI27OM4.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/AgentEServer.ts","../src/routes.ts","../src/dashboard.ts","../src/websocket.ts"],"sourcesContent":["// AgentEServer — HTTP + WebSocket transport for AgentE\n\nimport * as http from 'node:http';\nimport {\n AgentE,\n Observer,\n Diagnoser,\n ALL_PRINCIPLES,\n DEFAULT_THRESHOLDS,\n type AgentEConfig,\n type EconomyAdapter,\n type EconomyState,\n type EconomicEvent,\n type Diagnosis,\n type AgentEMode,\n type Thresholds,\n} from '@agent-e/core';\nimport { createRouteHandler } from './routes.js';\nimport { createWebSocketHandler, type WebSocketHandle } from './websocket.js';\n\nexport interface ServerConfig {\n port?: number;\n host?: string;\n agentE?: Partial<Omit<AgentEConfig, 'adapter'>>;\n validateState?: boolean;\n corsOrigin?: string;\n serveDashboard?: boolean;\n /** API key for authenticating mutation routes. When set, POST routes and WebSocket require `Authorization: Bearer <key>`. */\n apiKey?: string;\n}\n\nexport interface EnrichedAdjustment {\n parameter: string;\n value: number;\n scope?: import('@agent-e/core').ParameterScope;\n reasoning: string;\n}\n\ninterface QueuedAdjustment {\n key: string;\n value: number;\n scope: import('@agent-e/core').ParameterScope | undefined;\n}\n\nexport class AgentEServer {\n private readonly agentE: AgentE;\n private readonly server: http.Server;\n private lastState: EconomyState | null = null;\n private adjustmentQueue: QueuedAdjustment[] = [];\n private alerts: Diagnosis[] = [];\n readonly port: number;\n private readonly host: string;\n private readonly thresholds: Thresholds;\n private readonly startedAt = Date.now();\n private wsHandle: WebSocketHandle | null = null;\n readonly validateState: boolean;\n readonly corsOrigin: string;\n readonly serveDashboard: boolean;\n readonly apiKey: string | undefined;\n\n constructor(config: ServerConfig = {}) {\n this.port = config.port ?? 3100;\n this.host = config.host ?? '127.0.0.1';\n this.apiKey = config.apiKey;\n this.validateState = config.validateState ?? true;\n this.corsOrigin = config.corsOrigin ?? 'http://localhost:3100';\n this.serveDashboard = config.serveDashboard ?? true;\n\n // Build a \"remote\" adapter — state comes from HTTP/WS, not polled\n const adapter: EconomyAdapter = {\n getState: () => {\n if (!this.lastState) {\n return {\n tick: 0,\n roles: [],\n resources: [],\n currencies: ['default'],\n agentBalances: {},\n agentRoles: {},\n agentInventories: {},\n marketPrices: {},\n recentTransactions: [],\n };\n }\n return this.lastState;\n },\n setParam: (key: string, value: number, scope?: import('@agent-e/core').ParameterScope) => {\n this.adjustmentQueue.push({ key, value, scope });\n },\n };\n\n const agentECfg = config.agentE ?? {};\n const agentEConfig: AgentEConfig = {\n adapter,\n mode: agentECfg.mode ?? 'autonomous',\n gracePeriod: agentECfg.gracePeriod ?? 0,\n checkInterval: agentECfg.checkInterval ?? 1,\n ...(agentECfg.dominantRoles ? { dominantRoles: agentECfg.dominantRoles } : {}),\n ...(agentECfg.idealDistribution ? { idealDistribution: agentECfg.idealDistribution } : {}),\n ...(agentECfg.maxAdjustmentPercent !== undefined ? { maxAdjustmentPercent: agentECfg.maxAdjustmentPercent } : {}),\n ...(agentECfg.cooldownTicks !== undefined ? { cooldownTicks: agentECfg.cooldownTicks } : {}),\n ...(agentECfg.thresholds ? { thresholds: agentECfg.thresholds } : {}),\n };\n\n this.thresholds = {\n ...DEFAULT_THRESHOLDS,\n ...(agentECfg.thresholds ?? {}),\n ...(agentECfg.maxAdjustmentPercent !== undefined ? { maxAdjustmentPercent: agentECfg.maxAdjustmentPercent } : {}),\n ...(agentECfg.cooldownTicks !== undefined ? { cooldownTicks: agentECfg.cooldownTicks } : {}),\n };\n this.agentE = new AgentE(agentEConfig);\n\n // Capture alerts during tick\n this.agentE.on('alert', (diagnosis: unknown) => {\n this.alerts.push(diagnosis as Diagnosis);\n });\n\n this.agentE.connect(adapter).start();\n\n // Create HTTP server\n const routeHandler = createRouteHandler(this);\n this.server = http.createServer(routeHandler);\n }\n\n async start(): Promise<void> {\n // Wire up WebSocket upgrade\n this.wsHandle = createWebSocketHandler(this.server, this);\n\n return new Promise((resolve) => {\n this.server.listen(this.port, this.host, () => {\n const addr = this.getAddress();\n console.log(`[AgentE Server] Listening on http://${addr.host}:${addr.port}`);\n resolve();\n });\n });\n }\n\n async stop(): Promise<void> {\n this.agentE.stop();\n if (this.wsHandle) this.wsHandle.cleanup();\n return new Promise((resolve, reject) => {\n this.server.close((err) => {\n if (err) reject(err);\n else resolve();\n });\n });\n }\n\n getAgentE(): AgentE {\n return this.agentE;\n }\n\n getAddress(): { port: number; host: string } {\n const addr = this.server.address();\n if (addr && typeof addr === 'object') {\n return { port: addr.port, host: addr.address };\n }\n return { port: this.port, host: this.host };\n }\n\n getUptime(): number {\n return Date.now() - this.startedAt;\n }\n\n /**\n * Process a tick with the given state.\n * 1. Clear adjustment queue\n * 2. Set state\n * 3. Ingest events\n * 4. Run agentE.tick(state)\n * 5. Drain adjustment queue, enrich with reasoning from decisions\n * 6. Return response\n */\n async processTick(\n state: EconomyState,\n events?: EconomicEvent[],\n ): Promise<{\n adjustments: EnrichedAdjustment[];\n alerts: Diagnosis[];\n health: number;\n tick: number;\n decisions: ReturnType<AgentE['getDecisions']>;\n }> {\n // Clear queues\n this.adjustmentQueue = [];\n this.alerts = [];\n\n // Set state\n this.lastState = state;\n\n // Ingest events\n if (events) {\n for (const event of events) {\n this.agentE.ingest(event);\n }\n }\n\n // Run tick\n await this.agentE.tick(state);\n\n // Drain adjustments\n const rawAdj = [...this.adjustmentQueue];\n this.adjustmentQueue = [];\n\n // Cross-reference with decision log to attach reasoning\n const decisions = this.agentE.getDecisions({ since: state.tick, until: state.tick });\n\n const adjustments: EnrichedAdjustment[] = rawAdj.map(adj => {\n const decision = decisions.find(d =>\n d.plan.parameter === adj.key && d.result === 'applied',\n );\n return {\n parameter: adj.key,\n value: adj.value,\n ...(adj.scope ? { scope: adj.scope } : {}),\n reasoning: decision?.diagnosis.violation.suggestedAction.reasoning ?? '',\n };\n });\n\n return {\n adjustments,\n alerts: [...this.alerts],\n health: this.agentE.getHealth(),\n tick: state.tick,\n decisions,\n };\n }\n\n /**\n * Run Observer + Diagnoser on the given state without side effects (no execution).\n * Computes fresh metrics from the state rather than reading stored metrics.\n */\n diagnoseOnly(state: EconomyState): {\n diagnoses: ReturnType<AgentE['diagnoseNow']>;\n health: number;\n } {\n const observer = new Observer();\n const diagnoser = new Diagnoser(ALL_PRINCIPLES);\n const metrics = observer.compute(state, []);\n const diagnoses = diagnoser.diagnose(metrics, this.thresholds);\n\n // Mirrors AgentE.getHealth() — keep in sync if that logic changes\n let health = 100;\n if (metrics.avgSatisfaction < 65) health -= 15;\n if (metrics.avgSatisfaction < 50) health -= 10;\n if (metrics.giniCoefficient > 0.45) health -= 15;\n if (metrics.giniCoefficient > 0.60) health -= 10;\n if (Math.abs(metrics.netFlow) > 10) health -= 15;\n if (Math.abs(metrics.netFlow) > 20) health -= 10;\n if (metrics.churnRate > 0.05) health -= 15;\n health = Math.max(0, Math.min(100, health));\n\n return { diagnoses, health };\n }\n\n setMode(mode: AgentEMode): void {\n this.agentE.setMode(mode);\n }\n\n lock(param: string): void {\n this.agentE.lock(param);\n }\n\n unlock(param: string): void {\n this.agentE.unlock(param);\n }\n\n constrain(param: string, bounds: { min: number; max: number }): void {\n this.agentE.constrain(param, bounds);\n }\n\n broadcast(data: Record<string, unknown>): void {\n if (this.wsHandle) this.wsHandle.broadcast(data);\n }\n}\n","// HTTP routes for AgentE Server\n// Node http module with manual body parsing. CORS on all responses.\n\nimport type * as http from 'node:http';\nimport { timingSafeEqual } from 'node:crypto';\nimport { validateEconomyState } from '@agent-e/core';\nimport type { AgentEServer } from './AgentEServer.js';\nimport { getDashboardHtml } from './dashboard.js';\n\nfunction setSecurityHeaders(res: http.ServerResponse): void {\n res.setHeader('X-Content-Type-Options', 'nosniff');\n res.setHeader('X-Frame-Options', 'DENY');\n res.setHeader('Referrer-Policy', 'strict-origin-when-cross-origin');\n}\n\nfunction setCorsHeaders(res: http.ServerResponse, allowedOrigin: string, requestOrigin?: string): void {\n setSecurityHeaders(res);\n // If configured as '*', allow all.\n // Otherwise, only reflect the origin if it matches the configured allowedOrigin.\n // If there's no request origin (non-browser / server-to-server), return allowedOrigin directly.\n let origin: string;\n if (allowedOrigin === '*') {\n origin = '*';\n } else if (requestOrigin === undefined) {\n origin = allowedOrigin; // non-browser request, return configured origin\n } else {\n // Reflect origin only if it matches (case-insensitive) — otherwise don't include a matching CORS header\n origin = requestOrigin.toLowerCase() === allowedOrigin.toLowerCase() ? requestOrigin : '';\n }\n if (origin) {\n res.setHeader('Access-Control-Allow-Origin', origin);\n }\n res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');\n res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');\n}\n\n/** Strips prototype-polluting keys from parsed JSON objects (recursive). */\nfunction sanitizeJson(obj: unknown): unknown {\n if (obj === null || typeof obj !== 'object') return obj;\n if (Array.isArray(obj)) return obj.map(sanitizeJson);\n const clean: Record<string, unknown> = {};\n for (const [key, val] of Object.entries(obj as Record<string, unknown>)) {\n if (key === '__proto__' || key === 'constructor' || key === 'prototype') continue;\n clean[key] = sanitizeJson(val);\n }\n return clean;\n}\n\nfunction checkAuth(req: http.IncomingMessage, apiKey: string | undefined): boolean {\n if (!apiKey) return true; // no key configured = open\n const header = req.headers['authorization'];\n if (typeof header !== 'string') return false;\n const expected = `Bearer ${apiKey}`;\n if (header.length !== expected.length) return false;\n return timingSafeEqual(Buffer.from(header), Buffer.from(expected));\n}\n\nfunction json(res: http.ServerResponse, status: number, data: unknown, origin: string, reqOrigin?: string): void {\n setCorsHeaders(res, origin, reqOrigin);\n res.writeHead(status, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify(data));\n}\n\nconst MAX_BODY_BYTES = 1_048_576; // 1 MB\n\nfunction readBody(req: http.IncomingMessage): Promise<string> {\n return new Promise((resolve, reject) => {\n const chunks: Buffer[] = [];\n let totalBytes = 0;\n req.on('data', (chunk: Buffer) => {\n totalBytes += chunk.length;\n if (totalBytes > MAX_BODY_BYTES) {\n req.destroy();\n reject(new Error('Request body too large'));\n return;\n }\n chunks.push(chunk);\n });\n req.on('end', () => resolve(Buffer.concat(chunks).toString('utf-8')));\n req.on('error', reject);\n });\n}\n\nexport function createRouteHandler(\n server: AgentEServer,\n): (req: http.IncomingMessage, res: http.ServerResponse) => void {\n const cors = server.corsOrigin;\n const apiKey = server.apiKey;\n\n return async (req, res) => {\n const url = new URL(req.url ?? '/', `http://${req.headers.host ?? 'localhost'}`);\n const path = url.pathname;\n const method = req.method?.toUpperCase() ?? 'GET';\n const reqOrigin = req.headers['origin'] as string | undefined;\n\n // Scoped json helper — captures cors + reqOrigin for this request\n const respond = (status: number, data: unknown) => json(res, status, data, cors, reqOrigin);\n\n // CORS preflight\n if (method === 'OPTIONS') {\n setCorsHeaders(res, cors, reqOrigin);\n res.writeHead(204);\n res.end();\n return;\n }\n\n try {\n // POST /tick — validate state, run tick, return adjustments/alerts/health\n if (path === '/tick' && method === 'POST') {\n if (!checkAuth(req, apiKey)) {\n respond(401, { error: 'Unauthorized' });\n return;\n }\n const body = await readBody(req);\n let parsed: unknown;\n try {\n parsed = sanitizeJson(JSON.parse(body));\n } catch {\n respond(400, { error: 'Invalid JSON' });\n return;\n }\n\n if (!parsed || typeof parsed !== 'object') {\n respond(400, { error: 'Body must be a JSON object' });\n return;\n }\n\n const payload = parsed as Record<string, unknown>;\n const state = payload['state'] ?? parsed;\n const events = payload['events'];\n\n // Validate state (if enabled)\n const validation = server.validateState ? validateEconomyState(state) : null;\n if (validation && !validation.valid) {\n respond(400, {\n error: 'invalid_state',\n validationErrors: validation.errors,\n });\n return;\n }\n\n const result = await server.processTick(\n state as import('@agent-e/core').EconomyState,\n Array.isArray(events) ? events as import('@agent-e/core').EconomicEvent[] : undefined,\n );\n\n const warnings = validation?.warnings ?? [];\n\n respond(200, {\n adjustments: result.adjustments,\n alerts: result.alerts.map(a => ({\n principleId: a.principle.id,\n principleName: a.principle.name,\n severity: a.violation.severity,\n evidence: a.violation.evidence,\n reasoning: a.violation.suggestedAction.reasoning,\n })),\n health: result.health,\n tick: result.tick,\n ...(warnings.length > 0 ? { validationWarnings: warnings } : {}),\n });\n return;\n }\n\n // GET /health — health, tick, mode, activePlans, uptime\n if (path === '/health' && method === 'GET') {\n const agentE = server.getAgentE();\n respond(200, {\n health: agentE.getHealth(),\n tick: agentE.metrics.latest()?.tick ?? 0,\n mode: agentE.getMode(),\n activePlans: agentE.getActivePlans().length,\n uptime: server.getUptime(),\n });\n return;\n }\n\n // GET /decisions — decision log with optional ?limit and ?since\n if (path === '/decisions' && method === 'GET') {\n const rawLimit = parseInt(url.searchParams.get('limit') ?? '100', 10);\n const limit = Math.min(Math.max(Number.isNaN(rawLimit) ? 100 : rawLimit, 1), 1000);\n const sinceParam = url.searchParams.get('since');\n const agentE = server.getAgentE();\n\n let decisions;\n if (sinceParam) {\n const since = parseInt(sinceParam, 10);\n if (Number.isNaN(since)) {\n respond(400, { error: 'Invalid \"since\" parameter — must be a number' });\n return;\n }\n decisions = agentE.getDecisions({ since });\n } else {\n decisions = agentE.log.latest(limit);\n }\n\n respond(200, { decisions });\n return;\n }\n\n // POST /config — batch lock/unlock/constrain/mode\n if (path === '/config' && method === 'POST') {\n if (!checkAuth(req, apiKey)) {\n respond(401, { error: 'Unauthorized' });\n return;\n }\n const body = await readBody(req);\n let parsed: unknown;\n try {\n parsed = sanitizeJson(JSON.parse(body));\n } catch {\n respond(400, { error: 'Invalid JSON' });\n return;\n }\n\n const config = parsed as Record<string, unknown>;\n\n // Lock parameters\n if (Array.isArray(config['lock'])) {\n for (const param of config['lock']) {\n if (typeof param === 'string') server.lock(param);\n }\n }\n\n // Unlock parameters\n if (Array.isArray(config['unlock'])) {\n for (const param of config['unlock']) {\n if (typeof param === 'string') server.unlock(param);\n }\n }\n\n // Constrain parameters — validate ALL before applying any\n if (Array.isArray(config['constrain'])) {\n const validated: { param: string; min: number; max: number }[] = [];\n for (const c of config['constrain'] as unknown[]) {\n if (\n c && typeof c === 'object' &&\n typeof (c as Record<string, unknown>)['param'] === 'string' &&\n typeof (c as Record<string, unknown>)['min'] === 'number' &&\n typeof (c as Record<string, unknown>)['max'] === 'number'\n ) {\n const constraint = c as { param: string; min: number; max: number };\n if (!Number.isFinite(constraint.min) || !Number.isFinite(constraint.max)) {\n respond(400, { error: 'Constraint bounds must be finite numbers' });\n return;\n }\n if (constraint.min > constraint.max) {\n respond(400, { error: 'Constraint min cannot exceed max' });\n return;\n }\n validated.push(constraint);\n }\n }\n for (const constraint of validated) {\n server.constrain(constraint.param, { min: constraint.min, max: constraint.max });\n }\n }\n\n // Mode switch\n if (config['mode'] === 'autonomous' || config['mode'] === 'advisor') {\n server.setMode(config['mode']);\n }\n\n respond(200, { ok: true });\n return;\n }\n\n // GET /principles — list all principles\n if (path === '/principles' && method === 'GET') {\n const principles = server.getAgentE().getPrinciples();\n respond(200, {\n count: principles.length,\n principles: principles.map(p => ({\n id: p.id,\n name: p.name,\n category: p.category,\n description: p.description,\n })),\n });\n return;\n }\n\n // POST /diagnose — standalone Observer+Diagnoser (no side effects)\n if (path === '/diagnose' && method === 'POST') {\n if (!checkAuth(req, apiKey)) {\n respond(401, { error: 'Unauthorized' });\n return;\n }\n const body = await readBody(req);\n let parsed: unknown;\n try {\n parsed = sanitizeJson(JSON.parse(body));\n } catch {\n respond(400, { error: 'Invalid JSON' });\n return;\n }\n\n const payload = parsed as Record<string, unknown>;\n const state = payload['state'] ?? parsed;\n\n if (server.validateState) {\n const validation = validateEconomyState(state);\n if (!validation.valid) {\n respond(400, { error: 'invalid_state', validationErrors: validation.errors });\n return;\n }\n }\n\n const result = server.diagnoseOnly(state as import('@agent-e/core').EconomyState);\n\n respond(200, {\n health: result.health,\n diagnoses: result.diagnoses.map(d => ({\n principleId: d.principle.id,\n principleName: d.principle.name,\n severity: d.violation.severity,\n evidence: d.violation.evidence,\n suggestedAction: d.violation.suggestedAction,\n })),\n });\n return;\n }\n\n // GET / — Dashboard HTML\n if (path === '/' && method === 'GET' && server.serveDashboard) {\n setCorsHeaders(res, cors, reqOrigin);\n res.setHeader('Content-Security-Policy', \"default-src 'self'; script-src 'unsafe-inline' https://cdn.jsdelivr.net; style-src 'unsafe-inline' https://fonts.googleapis.com; font-src https://fonts.gstatic.com; connect-src 'self' ws: wss:; img-src 'self' data:\");\n res.setHeader('Cache-Control', 'public, max-age=60');\n res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });\n res.end(getDashboardHtml());\n return;\n }\n\n // GET /metrics — Latest metrics + history for dashboard charts\n if (path === '/metrics' && method === 'GET') {\n const agentE = server.getAgentE();\n const latest = agentE.store.latest();\n const history = agentE.store.recentHistory(100);\n respond(200, { latest, history });\n return;\n }\n\n // GET /metrics/personas — Persona distribution\n if (path === '/metrics/personas' && method === 'GET') {\n const agentE = server.getAgentE();\n const latest = agentE.store.latest();\n const dist = latest.personaDistribution || {};\n const total = Object.values(dist).reduce((s: number, v) => s + (v as number), 0);\n respond(200, { distribution: dist, total });\n return;\n }\n\n // POST /approve — Approve advisor recommendation\n if (path === '/approve' && method === 'POST') {\n if (!checkAuth(req, apiKey)) {\n respond(401, { error: 'Unauthorized' });\n return;\n }\n const body = await readBody(req);\n let parsed: unknown;\n try { parsed = sanitizeJson(JSON.parse(body)); } catch {\n respond(400, { error: 'Invalid JSON' });\n return;\n }\n const payload = parsed as Record<string, unknown>;\n const decisionId = payload['decisionId'] as string;\n if (!decisionId) {\n respond(400, { error: 'missing_decision_id' });\n return;\n }\n\n const agentE = server.getAgentE();\n if (agentE.getMode() !== 'advisor') {\n respond(400, { error: 'not_in_advisor_mode' });\n return;\n }\n\n const entry = agentE.log.getById(decisionId);\n if (!entry) {\n respond(404, { error: 'decision_not_found' });\n return;\n }\n if (entry.result !== 'skipped_override') {\n respond(409, { error: 'decision_not_pending', currentResult: entry.result });\n return;\n }\n\n await agentE.apply(entry.plan);\n agentE.log.updateResult(decisionId, 'applied');\n server.broadcast({ type: 'advisor_action', action: 'approved', decisionId });\n respond(200, {\n ok: true,\n parameter: entry.plan.parameter,\n value: entry.plan.targetValue,\n });\n return;\n }\n\n // POST /reject — Reject advisor recommendation\n if (path === '/reject' && method === 'POST') {\n if (!checkAuth(req, apiKey)) {\n respond(401, { error: 'Unauthorized' });\n return;\n }\n const body = await readBody(req);\n let parsed: unknown;\n try { parsed = sanitizeJson(JSON.parse(body)); } catch {\n respond(400, { error: 'Invalid JSON' });\n return;\n }\n const payload = parsed as Record<string, unknown>;\n const decisionId = payload['decisionId'] as string;\n const reason = (payload['reason'] as string) || undefined;\n if (!decisionId) {\n respond(400, { error: 'missing_decision_id' });\n return;\n }\n\n const agentE = server.getAgentE();\n if (agentE.getMode() !== 'advisor') {\n respond(400, { error: 'not_in_advisor_mode' });\n return;\n }\n\n const entry = agentE.log.getById(decisionId);\n if (!entry) {\n respond(404, { error: 'decision_not_found' });\n return;\n }\n if (entry.result !== 'skipped_override') {\n respond(409, { error: 'decision_not_pending', currentResult: entry.result });\n return;\n }\n\n agentE.log.updateResult(decisionId, 'rejected', reason);\n server.broadcast({ type: 'advisor_action', action: 'rejected', decisionId, reason });\n respond(200, { ok: true, decisionId });\n return;\n }\n\n // GET /pending — List pending advisor recommendations\n if (path === '/pending' && method === 'GET') {\n const agentE = server.getAgentE();\n const pending = agentE.log.query({ result: 'skipped_override' });\n respond(200, {\n mode: agentE.getMode(),\n pending,\n count: pending.length,\n });\n return;\n }\n\n // 404\n respond(404, { error: 'Not found' });\n } catch (err) {\n console.error('[AgentE Server] Unhandled route error:', err);\n respond(500, { error: 'Internal server error' });\n }\n };\n}\n","// Dashboard HTML — self-contained single-page dashboard served at GET /\n// Inline CSS + Chart.js CDN + WebSocket real-time updates\n\nexport function getDashboardHtml(): string {\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"UTF-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n<title>AgentE Dashboard</title>\n<link rel=\"preconnect\" href=\"https://fonts.googleapis.com\">\n<link rel=\"preconnect\" href=\"https://fonts.gstatic.com\" crossorigin>\n<link href=\"https://fonts.googleapis.com/css2?family=Inter:opsz,wght@14..32,400;14..32,500;14..32,600;14..32,700&family=JetBrains+Mono:wght@400;500&display=swap\" rel=\"stylesheet\">\n<script src=\"https://cdn.jsdelivr.net/npm/chart.js@4.5.1/dist/chart.umd.min.js\"></script>\n<style>\n *, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }\n\n :root {\n --bg-root: #09090b;\n --bg-panel: #18181b;\n --bg-terminal: #09090b;\n --border: #27272a;\n --text-primary: #ffffff;\n --text-secondary: #52525b;\n --text-tertiary: #a1a1aa;\n --text-value: #d4d4d8;\n --accent: #22c55e;\n --warning: #eab308;\n --danger: #ef4444;\n --info: #71717a;\n }\n\n html { scroll-behavior: smooth; }\n\n body {\n background: var(--bg-root);\n color: var(--text-primary);\n font-family: 'Inter', system-ui, sans-serif;\n -webkit-font-smoothing: antialiased;\n line-height: 1.5;\n min-height: 100dvh;\n overflow-y: auto;\n }\n\n /* -- Header -- */\n .header {\n position: sticky;\n top: 0;\n z-index: 50;\n background: var(--bg-panel);\n border-bottom: 1px solid var(--border);\n padding: 12px 24px;\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 16px;\n }\n\n .header-left {\n display: flex;\n align-items: center;\n gap: 10px;\n }\n\n .header-logo {\n font-family: 'JetBrains Mono', monospace;\n font-size: 15px;\n font-weight: 500;\n color: var(--text-primary);\n }\n\n .header-version {\n font-family: 'JetBrains Mono', monospace;\n font-size: 11px;\n color: var(--text-secondary);\n background: var(--bg-root);\n padding: 2px 8px;\n border-radius: 4px;\n }\n\n .header-right {\n display: flex;\n align-items: center;\n gap: 20px;\n }\n\n .kpi-pill {\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 12px;\n font-family: 'JetBrains Mono', monospace;\n }\n\n .kpi-pill .label {\n color: var(--text-secondary);\n text-transform: uppercase;\n letter-spacing: 0.05em;\n }\n\n .kpi-pill .value {\n color: var(--text-value);\n font-variant-numeric: tabular-nums;\n }\n\n .kpi-pill .value.health-good { color: var(--accent); }\n .kpi-pill .value.health-warn { color: var(--warning); }\n .kpi-pill .value.health-bad { color: var(--danger); }\n\n .live-dot {\n width: 6px;\n height: 6px;\n border-radius: 50%;\n background: var(--accent);\n flex-shrink: 0;\n animation: pulse 2s ease-in-out infinite;\n }\n\n .live-dot.disconnected {\n background: var(--danger);\n animation: none;\n }\n\n @keyframes pulse {\n 0%, 100% { opacity: 1; }\n 50% { opacity: 0.4; }\n }\n\n /* -- Advisor Banner -- */\n .advisor-banner {\n display: none;\n background: rgba(234,179,8,0.08);\n border-bottom: 1px solid rgba(234,179,8,0.2);\n padding: 8px 24px;\n font-family: 'JetBrains Mono', monospace;\n font-size: 11px;\n color: var(--warning);\n text-align: center;\n letter-spacing: 0.03em;\n }\n\n .advisor-mode .advisor-banner { display: block; }\n\n /* -- Advisor-specific: pending pill -- */\n .pending-pill {\n display: none;\n background: rgba(234,179,8,0.15);\n border-radius: 4px;\n padding: 2px 8px;\n cursor: pointer;\n }\n\n .pending-pill:hover { background: rgba(234,179,8,0.25); }\n\n .advisor-mode .pending-pill { display: flex; }\n\n /* -- Mode value color switching -- */\n .mode-value-auto { color: var(--accent); }\n .mode-value-advisor { color: var(--warning); }\n\n /* -- Layout -- */\n .dashboard {\n max-width: 1440px;\n margin: 0 auto;\n padding: 16px;\n display: flex;\n flex-direction: column;\n gap: 12px;\n }\n\n /* -- Panels -- */\n .panel {\n background: var(--bg-panel);\n border: 1px solid var(--border);\n border-radius: 8px;\n padding: 16px 20px;\n min-width: 0;\n display: flex;\n flex-direction: column;\n }\n\n .panel-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-bottom: 12px;\n flex-shrink: 0;\n }\n\n .panel-title {\n font-size: 11px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.08em;\n color: var(--info);\n }\n\n .panel-meta {\n font-size: 11px;\n font-family: 'JetBrains Mono', monospace;\n color: var(--text-secondary);\n display: flex;\n align-items: center;\n gap: 8px;\n }\n\n .panel-meta .live-label {\n color: var(--accent);\n display: flex;\n align-items: center;\n gap: 4px;\n }\n\n .panel-body {\n flex: 1;\n min-height: 0;\n overflow: hidden;\n }\n\n /* -- Health Charts -- */\n .charts-panel .chart-row {\n display: grid;\n grid-template-columns: repeat(4, 1fr);\n gap: 16px;\n }\n\n .mini-chart { position: relative; }\n .mini-chart-label {\n font-size: 10px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.06em;\n color: var(--text-secondary);\n margin-bottom: 4px;\n }\n .mini-chart canvas { width: 100% !important; height: 64px !important; }\n\n @media (max-width: 900px) {\n .charts-panel .chart-row { grid-template-columns: 1fr 1fr; }\n }\n\n @media (max-width: 500px) {\n .charts-panel .chart-row { grid-template-columns: 1fr; }\n }\n\n /* -- Terminal Feed -- */\n .terminal-panel {\n border-left: 2px solid var(--accent);\n height: 380px;\n overflow: hidden;\n }\n\n .advisor-mode .terminal-panel {\n border-left-color: var(--warning);\n }\n\n .terminal {\n background: var(--bg-terminal);\n border-radius: 6px;\n padding: 12px 16px;\n height: 100%;\n overflow-y: auto;\n font-family: 'JetBrains Mono', monospace;\n font-size: 12.5px;\n line-height: 1.7;\n display: flex;\n flex-direction: column;\n }\n\n .terminal-inner {\n margin-top: auto;\n }\n\n .terminal::-webkit-scrollbar { width: 4px; }\n .terminal::-webkit-scrollbar-track { background: transparent; }\n .terminal::-webkit-scrollbar-thumb { background: var(--border); border-radius: 2px; }\n\n .term-line {\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n opacity: 0;\n transform: translateY(4px);\n animation: termIn 0.3s ease-out forwards;\n }\n\n @keyframes termIn {\n to { opacity: 1; transform: translateY(0); }\n }\n\n .t-tick { color: var(--text-secondary); }\n .t-ok { color: var(--accent); }\n .t-check { color: var(--accent); }\n .t-skip { color: var(--warning); }\n .t-fail { color: var(--danger); }\n .t-principle { color: var(--text-primary); font-weight: 500; }\n .t-param { color: var(--text-tertiary); }\n .t-old { color: var(--text-value); font-variant-numeric: tabular-nums; }\n .t-arrow { color: var(--info); }\n .t-new { color: var(--accent); font-variant-numeric: tabular-nums; }\n .t-meta { color: var(--text-secondary); }\n .t-violation-id { color: var(--warning); }\n .t-violation-desc { color: var(--text-tertiary); }\n .t-status-label { color: var(--text-tertiary); }\n .t-status-value { color: var(--accent); font-variant-numeric: tabular-nums; }\n .t-dim { color: var(--text-secondary); }\n .t-white { color: var(--text-primary); }\n .t-separator { color: var(--info); }\n .t-pending-icon { color: var(--warning); }\n .t-pending-val { color: var(--warning); font-variant-numeric: tabular-nums; }\n\n /* -- Advisor Inline Buttons -- */\n .advisor-btn {\n display: none;\n font-family: 'JetBrains Mono', monospace;\n font-size: 10px;\n border-radius: 3px;\n padding: 2px 8px;\n cursor: pointer;\n border: 1px solid;\n margin-left: 6px;\n vertical-align: middle;\n line-height: 1.4;\n transition: background 0.15s;\n }\n\n .advisor-mode .advisor-btn { display: inline-flex; align-items: center; }\n\n .advisor-btn.approve-btn {\n background: rgba(34,197,94,0.15);\n color: var(--accent);\n border-color: rgba(34,197,94,0.3);\n }\n .advisor-btn.approve-btn:hover { background: rgba(34,197,94,0.25); }\n\n .advisor-btn.reject-btn {\n background: rgba(239,68,68,0.1);\n color: var(--danger);\n border-color: rgba(239,68,68,0.2);\n }\n .advisor-btn.reject-btn:hover { background: rgba(239,68,68,0.2); }\n\n /* Approved/Rejected flash labels */\n .action-flash {\n font-family: 'JetBrains Mono', monospace;\n font-size: 10px;\n margin-left: 8px;\n animation: flashIn 0.3s ease-out;\n }\n\n .action-flash.approved { color: var(--accent); }\n .action-flash.rejected { color: var(--info); }\n\n @keyframes flashIn {\n from { opacity: 0; transform: translateX(-4px); }\n to { opacity: 1; transform: translateX(0); }\n }\n\n /* Dimmed line after rejection */\n .term-line.rejected-line { opacity: 0.5; }\n\n /* -- Alerts -- */\n .alert-card {\n background: var(--bg-root);\n border-radius: 6px;\n padding: 10px 14px;\n margin-bottom: 8px;\n border-left: 3px solid transparent;\n overflow: hidden;\n }\n\n .alert-card.sev-high { border-left-color: var(--danger); }\n .alert-card.sev-med { border-left-color: var(--warning); }\n .alert-card.sev-low { border-left-color: var(--accent); }\n\n .alert-top {\n display: flex;\n align-items: center;\n gap: 8px;\n margin-bottom: 4px;\n }\n\n .sev-badge {\n width: 20px;\n height: 20px;\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 10px;\n font-weight: 700;\n font-family: 'JetBrains Mono', monospace;\n color: var(--bg-root);\n flex-shrink: 0;\n }\n\n .sev-badge.high { background: var(--danger); }\n .sev-badge.med { background: var(--warning); }\n .sev-badge.low { background: var(--accent); }\n\n .alert-principle-id { color: var(--warning); font-family: 'JetBrains Mono', monospace; font-size: 11px; }\n .alert-principle-name { color: var(--text-primary); font-size: 12px; font-weight: 500; }\n .alert-evidence { color: var(--text-tertiary); font-size: 10px; margin-top: 2px; }\n .alert-suggestion { color: var(--accent); font-size: 10px; margin-top: 2px; }\n\n .alert-approve-btn {\n display: none;\n font-family: 'JetBrains Mono', monospace;\n font-size: 10px;\n background: rgba(34,197,94,0.15);\n color: var(--accent);\n border: 1px solid rgba(34,197,94,0.3);\n border-radius: 3px;\n padding: 3px 10px;\n cursor: pointer;\n margin-top: 8px;\n transition: background 0.15s;\n }\n\n .alert-approve-btn:hover { background: rgba(34,197,94,0.25); }\n\n .alert-reject-btn {\n display: none;\n font-family: 'JetBrains Mono', monospace;\n font-size: 10px;\n background: rgba(239,68,68,0.1);\n color: var(--danger);\n border: 1px solid rgba(239,68,68,0.2);\n border-radius: 3px;\n padding: 3px 10px;\n cursor: pointer;\n margin-top: 8px;\n margin-left: 6px;\n transition: background 0.15s;\n }\n\n .alert-reject-btn:hover { background: rgba(239,68,68,0.2); }\n\n .advisor-mode .alert-approve-btn { display: inline-block; }\n .advisor-mode .alert-reject-btn { display: inline-block; }\n\n /* Alert resolved state */\n .alert-card.resolved {\n opacity: 0;\n max-height: 0;\n padding: 0 14px;\n margin-bottom: 0;\n overflow: hidden;\n transition: opacity 0.4s ease-out, max-height 0.5s ease-out 0.1s, padding 0.5s ease-out 0.1s, margin 0.5s ease-out 0.1s;\n }\n\n .alerts-scroll {\n overflow-y: auto;\n max-height: 300px;\n }\n\n .alerts-scroll::-webkit-scrollbar { width: 4px; }\n .alerts-scroll::-webkit-scrollbar-track { background: transparent; }\n .alerts-scroll::-webkit-scrollbar-thumb { background: var(--border); border-radius: 2px; }\n\n /* -- Persona Bars -- */\n .persona-row {\n display: flex;\n align-items: center;\n gap: 8px;\n margin-bottom: 5px;\n font-size: 11px;\n font-family: 'JetBrains Mono', monospace;\n }\n\n .persona-label {\n width: 100px;\n text-align: right;\n color: var(--text-tertiary);\n flex-shrink: 0;\n font-variant-numeric: tabular-nums;\n }\n\n .persona-bar-track {\n flex: 1;\n height: 12px;\n background: var(--bg-root);\n border-radius: 3px;\n overflow: hidden;\n }\n\n .persona-bar-fill {\n height: 100%;\n border-radius: 3px;\n transition: width 0.6s ease-out;\n }\n\n .persona-pct {\n width: 32px;\n text-align: right;\n color: var(--text-secondary);\n font-variant-numeric: tabular-nums;\n }\n\n /* -- Parameter Registry -- */\n .param-row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 6px 0;\n border-bottom: 1px solid rgba(39,39,42,0.4);\n font-family: 'JetBrains Mono', monospace;\n font-size: 11px;\n }\n\n .param-row:last-child { border-bottom: none; }\n\n .param-name {\n color: var(--text-tertiary);\n }\n\n .param-val {\n color: var(--accent);\n font-variant-numeric: tabular-nums;\n }\n\n .param-changed {\n color: var(--text-secondary);\n font-size: 9px;\n margin-left: 6px;\n }\n\n /* Ghost preview for pending recommendations */\n .param-ghost {\n display: none;\n color: var(--warning);\n font-size: 9px;\n margin-left: 4px;\n font-variant-numeric: tabular-nums;\n }\n\n .advisor-mode .param-ghost { display: inline; }\n\n .param-pending-label {\n display: none;\n color: var(--warning);\n font-size: 9px;\n margin-left: 6px;\n }\n\n .advisor-mode .param-pending-label { display: inline; }\n\n .params-scroll {\n overflow-y: auto;\n max-height: 300px;\n }\n\n .params-scroll::-webkit-scrollbar { width: 4px; }\n .params-scroll::-webkit-scrollbar-track { background: transparent; }\n .params-scroll::-webkit-scrollbar-thumb { background: var(--border); border-radius: 2px; }\n\n /* -- Violations Table -- */\n .violations-table {\n width: 100%;\n border-collapse: collapse;\n font-size: 11px;\n font-family: 'JetBrains Mono', monospace;\n }\n\n .violations-table thead th {\n text-align: left;\n padding: 7px 8px;\n border-bottom: 1px solid var(--border);\n color: var(--text-secondary);\n font-weight: 600;\n font-size: 10px;\n text-transform: uppercase;\n letter-spacing: 0.06em;\n cursor: pointer;\n user-select: none;\n white-space: nowrap;\n position: sticky;\n top: 0;\n background: var(--bg-panel);\n }\n\n .violations-table thead th:hover { color: var(--text-tertiary); }\n\n .violations-table tbody td {\n padding: 6px 8px;\n border-bottom: 1px solid rgba(39,39,42,0.4);\n color: var(--text-value);\n font-variant-numeric: tabular-nums;\n }\n\n .violations-table tbody tr:hover td { background: rgba(39,39,42,0.3); }\n\n .table-scroll {\n overflow-y: auto;\n max-height: 240px;\n }\n\n .table-scroll::-webkit-scrollbar { width: 4px; }\n .table-scroll::-webkit-scrollbar-track { background: transparent; }\n .table-scroll::-webkit-scrollbar-thumb { background: var(--border); border-radius: 2px; }\n\n .badge-applied { color: var(--accent); font-size: 10px; }\n .badge-skipped { color: var(--text-secondary); font-size: 10px; }\n .badge-rejected { color: var(--danger); font-size: 10px; opacity: 0.6; }\n\n /* Pending badge in violations table (advisor mode) */\n .badge-pending {\n display: none;\n color: var(--warning);\n font-size: 10px;\n cursor: pointer;\n position: relative;\n }\n\n .advisor-mode .badge-pending { display: inline-flex; align-items: center; gap: 4px; }\n\n .badge-pending:hover { text-decoration: underline; }\n\n /* Pending dropdown in violations table */\n .pending-dropdown {\n display: none;\n position: absolute;\n bottom: 100%;\n left: 0;\n background: var(--bg-panel);\n border: 1px solid var(--border);\n border-radius: 4px;\n padding: 4px 0;\n z-index: 20;\n min-width: 120px;\n box-shadow: 0 -4px 12px rgba(0,0,0,0.4);\n }\n\n .pending-dropdown.open { display: block; }\n\n .pending-dropdown-item {\n padding: 5px 12px;\n font-family: 'JetBrains Mono', monospace;\n font-size: 10px;\n cursor: pointer;\n display: flex;\n align-items: center;\n gap: 6px;\n white-space: nowrap;\n }\n\n .pending-dropdown-item:hover { background: rgba(39,39,42,0.5); }\n .pending-dropdown-item.approve-item { color: var(--accent); }\n .pending-dropdown-item.reject-item { color: var(--danger); }\n\n /* -- Bottom split row -- */\n .split-row {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 12px;\n }\n\n /* -- Empty state -- */\n .empty-state {\n color: var(--text-secondary);\n font-family: 'JetBrains Mono', monospace;\n font-size: 11px;\n text-align: center;\n padding: 20px;\n }\n\n /* -- Responsive -- */\n @media (max-width: 768px) {\n .split-row { grid-template-columns: 1fr; }\n .header { flex-direction: column; align-items: flex-start; gap: 8px; }\n .header-right { flex-wrap: wrap; gap: 12px; }\n .dashboard { padding: 8px; gap: 8px; }\n }\n\n /* -- Reduced motion -- */\n @media (prefers-reduced-motion: reduce) {\n .term-line { animation: none; opacity: 1; transform: none; }\n .live-dot { animation: none; }\n .persona-bar-fill { transition: none; }\n .alert-card.resolved { transition: none; }\n }\n</style>\n</head>\n<body>\n\n<!-- Header -->\n<header class=\"header\" id=\"header\">\n <div class=\"header-left\">\n <span class=\"header-logo\">AgentE</span>\n <span class=\"header-version\">v1.7.2</span>\n </div>\n <div class=\"header-right\">\n <div class=\"kpi-pill\">\n <span class=\"label\">Health</span>\n <span class=\"value health-good\" id=\"h-health\">--</span>\n </div>\n <div class=\"kpi-pill\">\n <span class=\"label\">Mode</span>\n <span class=\"value mode-value-auto\" id=\"h-mode\">--</span>\n </div>\n <div class=\"kpi-pill\">\n <span class=\"label\">Tick</span>\n <span class=\"value\" id=\"h-tick\">0</span>\n </div>\n <div class=\"kpi-pill pending-pill\" id=\"pending-pill\">\n <span class=\"label\" style=\"color: var(--warning);\">Pending</span>\n <span class=\"value\" style=\"color: var(--warning);\" id=\"h-pending\">0</span>\n </div>\n <div class=\"kpi-pill\">\n <span class=\"label\">Uptime</span>\n <span class=\"value\" id=\"h-uptime\">0s</span>\n </div>\n <div class=\"kpi-pill\">\n <div class=\"live-dot\" id=\"live-dot\" title=\"WebSocket connected\"></div>\n <span style=\"color: var(--accent); font-size: 11px;\">LIVE</span>\n </div>\n </div>\n</header>\n\n<!-- Advisor Banner -->\n<div class=\"advisor-banner\" id=\"advisor-banner\">\n ADVISOR MODE \\\\u2014 AgentE is waiting for your approval before applying changes\n</div>\n\n<!-- Dashboard -->\n<main class=\"dashboard\" id=\"dashboard-root\">\n\n <!-- Health & Metrics -->\n <div class=\"panel charts-panel\">\n <div class=\"panel-header\">\n <span class=\"panel-title\">Health & Metrics</span>\n </div>\n <div class=\"chart-row\">\n <div class=\"mini-chart\">\n <div class=\"mini-chart-label\">Health Score</div>\n <canvas id=\"chart-health\"></canvas>\n </div>\n <div class=\"mini-chart\">\n <div class=\"mini-chart-label\">Gini Coefficient</div>\n <canvas id=\"chart-gini\"></canvas>\n </div>\n <div class=\"mini-chart\">\n <div class=\"mini-chart-label\">Net Flow</div>\n <canvas id=\"chart-flow\"></canvas>\n </div>\n <div class=\"mini-chart\">\n <div class=\"mini-chart-label\">Avg Satisfaction</div>\n <canvas id=\"chart-satisfaction\"></canvas>\n </div>\n </div>\n </div>\n\n <!-- Decision Feed -->\n <div class=\"panel terminal-panel\">\n <div class=\"panel-header\">\n <span class=\"panel-title\">Decision Feed</span>\n <div class=\"panel-meta\">\n <span id=\"decision-count\">0 decisions</span>\n <span class=\"live-label\"><div class=\"live-dot\" style=\"width:5px;height:5px;\"></div> LIVE</span>\n </div>\n </div>\n <div class=\"panel-body\">\n <div class=\"terminal\" id=\"terminal\">\n <div id=\"terminal-inner\"></div>\n </div>\n </div>\n </div>\n\n <!-- Active Alerts -->\n <div class=\"panel alerts-panel\">\n <div class=\"panel-header\">\n <span class=\"panel-title\">Active Alerts</span>\n <span class=\"panel-meta\" id=\"alerts-count\">All clear</span>\n </div>\n <div class=\"alerts-scroll\" id=\"alerts\"></div>\n </div>\n\n <!-- Violation History -->\n <div class=\"panel\">\n <div class=\"panel-header\">\n <span class=\"panel-title\">Violation History</span>\n <span class=\"panel-meta\">Last 100 decisions</span>\n </div>\n <div class=\"table-scroll\">\n <table class=\"violations-table\" id=\"violations-table\">\n <thead>\n <tr>\n <th data-sort=\"tick\">Tick</th>\n <th data-sort=\"principle\">Principle</th>\n <th data-sort=\"severity\">Sev</th>\n <th data-sort=\"parameter\">Parameter</th>\n <th data-sort=\"result\">Action</th>\n <th>Change</th>\n </tr>\n </thead>\n <tbody id=\"violations-body\"></tbody>\n </table>\n </div>\n </div>\n\n <!-- Persona Distribution + Parameters -->\n <div class=\"split-row\">\n <div class=\"panel persona-panel\">\n <div class=\"panel-header\">\n <span class=\"panel-title\">Persona Distribution</span>\n <span class=\"panel-meta\" id=\"persona-count\"></span>\n </div>\n <div id=\"persona-bars\">\n <div class=\"empty-state\">No persona data yet</div>\n </div>\n </div>\n\n <div class=\"panel params-panel\">\n <div class=\"panel-header\">\n <span class=\"panel-title\">Parameters</span>\n <span class=\"panel-meta\" id=\"params-count\"></span>\n </div>\n <div class=\"params-scroll\" id=\"params-list\">\n <div class=\"empty-state\">No parameters registered</div>\n </div>\n </div>\n </div>\n\n</main>\n\n<script>\n(function() {\n 'use strict';\n\n // -- State --\n var ws = null;\n var reconnectDelay = 1000;\n var MAX_RECONNECT = 30000;\n var isAdvisor = false;\n var pendingDecisions = [];\n var MAX_TERMINAL_LINES = 80;\n var MAX_VIOLATIONS = 100;\n var violationSortKey = 'tick';\n var violationSortAsc = false;\n var violations = [];\n var decisionCount = 0;\n\n // Chart instances\n var chartHealth, chartGini, chartFlow, chartSatisfaction;\n\n // Param state for registry display\n var paramRegistry = [];\n var paramValues = {};\n var paramLastTick = {};\n var paramPending = {};\n var currentTick = 0;\n\n // -- DOM refs --\n var $hHealth = document.getElementById('h-health');\n var $hMode = document.getElementById('h-mode');\n var $hTick = document.getElementById('h-tick');\n var $hUptime = document.getElementById('h-uptime');\n var $hPending = document.getElementById('h-pending');\n var $liveDot = document.getElementById('live-dot');\n var $terminal = document.getElementById('terminal');\n var $terminalInner = document.getElementById('terminal-inner');\n var $alerts = document.getElementById('alerts');\n var $alertsCount = document.getElementById('alerts-count');\n var $violationsBody = document.getElementById('violations-body');\n var $personaBars = document.getElementById('persona-bars');\n var $paramsList = document.getElementById('params-list');\n var $paramsCount = document.getElementById('params-count');\n var $personaCount = document.getElementById('persona-count');\n var $decisionCount = document.getElementById('decision-count');\n var $dashboardRoot = document.getElementById('dashboard-root');\n\n // -- Helpers --\n function esc(s) { return String(s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/\"/g,'&quot;').replace(/'/g,'&#39;').replace(/\\\\\\\\/g,'&#92;'); }\n function pad(n, w) { return String(n).padStart(w || 4, ' '); }\n function fmt(n) { return typeof n === 'number' ? n.toFixed(3) : '\\\\u2014'; }\n function pct(n) { return typeof n === 'number' ? (n * 100).toFixed(0) + '%' : '\\\\u2014'; }\n\n function formatUptime(ms) {\n var s = Math.floor(ms / 1000);\n if (s < 60) return s + 's';\n if (s < 3600) return Math.floor(s / 60) + 'm ' + (s % 60) + 's';\n var h = Math.floor(s / 3600);\n return h + 'h ' + Math.floor((s % 3600) / 60) + 'm';\n }\n\n function healthClass(h) {\n if (h >= 70) return 'health-good';\n if (h >= 40) return 'health-warn';\n return 'health-bad';\n }\n\n function sevClass(s) {\n if (s >= 7) return 'high';\n if (s >= 4) return 'med';\n return 'low';\n }\n\n function sevCardClass(s) {\n if (s >= 7) return 'sev-high';\n if (s >= 4) return 'sev-med';\n return 'sev-low';\n }\n\n function personaColor(name) {\n var n = (name || '').toLowerCase();\n if (n === 'atrisk' || n === 'at_risk' || n === 'dormant') return 'var(--danger)';\n if (n === 'spender' || n === 'newentrant' || n === 'new_entrant' || n === 'passive') return 'var(--warning)';\n return 'var(--accent)';\n }\n\n // -- Chart setup --\n var chartOpts = {\n responsive: true,\n maintainAspectRatio: false,\n animation: false,\n plugins: {\n legend: { display: false },\n tooltip: {\n backgroundColor: '#18181b',\n titleColor: '#a1a1aa',\n bodyColor: '#d4d4d8',\n titleFont: { family: 'JetBrains Mono', size: 10 },\n bodyFont: { family: 'JetBrains Mono', size: 11 },\n borderColor: '#27272a',\n borderWidth: 1,\n padding: 8,\n }\n },\n scales: {\n x: { display: false },\n y: {\n grid: { color: 'rgba(39,39,42,0.5)', drawBorder: false },\n ticks: {\n color: '#52525b',\n font: { family: 'JetBrains Mono', size: 9 },\n maxTicksLimit: 3,\n },\n border: { display: false },\n }\n },\n elements: {\n point: { radius: 0, hoverRadius: 3, backgroundColor: '#22c55e' },\n line: { borderWidth: 1.5, tension: 0.3 },\n }\n };\n\n function makeChart(id, color, minY, maxY) {\n var ctx = document.getElementById(id).getContext('2d');\n var opts = JSON.parse(JSON.stringify(chartOpts));\n if (minY !== undefined) opts.scales.y.min = minY;\n if (maxY !== undefined) opts.scales.y.max = maxY;\n return new Chart(ctx, {\n type: 'line',\n data: {\n labels: [],\n datasets: [{\n data: [],\n borderColor: color,\n backgroundColor: color + '18',\n fill: true,\n }]\n },\n options: opts,\n });\n }\n\n function initCharts() {\n chartHealth = makeChart('chart-health', '#22c55e', 0, 100);\n chartGini = makeChart('chart-gini', '#eab308', 0, 1);\n chartFlow = makeChart('chart-flow', '#3b82f6');\n chartSatisfaction = makeChart('chart-satisfaction', '#22c55e', 0, 100);\n }\n\n function updateChart(chart, labels, data) {\n chart.data.labels = labels;\n chart.data.datasets[0].data = data;\n chart.update('none');\n }\n\n // -- Terminal --\n function addTerminalLine(html) {\n var el = document.createElement('div');\n el.className = 'term-line';\n el.innerHTML = html;\n $terminalInner.appendChild(el);\n while ($terminalInner.children.length > MAX_TERMINAL_LINES) {\n $terminalInner.removeChild($terminalInner.firstChild);\n }\n $terminal.scrollTop = $terminal.scrollHeight;\n }\n\n function decisionToTerminal(d) {\n var resultIcon = d.result === 'applied'\n ? '<span class=\"t-check\">\\\\u2705 </span>'\n : d.result === 'rejected'\n ? '<span class=\"t-fail\">\\\\u274c </span>'\n : d.result === 'skipped_override'\n ? '<span class=\"t-pending-icon\">\\\\u23f3 </span>'\n : '<span class=\"t-skip\">\\\\u23f8 </span>';\n\n var principle = d.diagnosis?.principle || {};\n var plan = d.plan || {};\n var severity = d.diagnosis?.violation?.severity ?? '?';\n var confidence = d.diagnosis?.violation?.confidence;\n var confStr = confidence != null ? (confidence * 100).toFixed(0) + '%' : '?';\n\n var advisorBtns = '';\n if (isAdvisor && d.result === 'skipped_override') {\n advisorBtns = '<span class=\"advisor-btn-group\" data-id=\"' + esc(d.id) + '\">'\n + '<button class=\"advisor-btn approve-btn\" data-action=\"approve\" data-id=\"' + esc(d.id) + '\">&#10003; Approve</button>'\n + '<button class=\"advisor-btn reject-btn\" data-action=\"reject\" data-id=\"' + esc(d.id) + '\">&#10005; Reject</button>'\n + '</span>';\n }\n\n return '<span class=\"t-tick\">[Tick ' + pad(d.tick) + ']</span> '\n + resultIcon\n + '<span class=\"t-principle\">' + esc(principle.name || '') + ':</span> '\n + '<span class=\"t-param\">' + esc(plan.parameter || '\\\\u2014') + ' </span>'\n + '<span class=\"t-old\">' + fmt(plan.currentValue) + '</span>'\n + '<span class=\"t-arrow\"> \\\\u2192 </span>'\n + (d.result === 'skipped_override'\n ? '<span class=\"t-pending-val\">' + fmt(plan.targetValue) + '</span>'\n : '<span class=\"t-new\">' + fmt(plan.targetValue) + '</span>')\n + '<span class=\"t-meta\"> sev ' + severity + ', conf ' + confStr + '</span>'\n + advisorBtns;\n }\n\n // -- Alerts --\n function renderAlerts(alerts) {\n if (!alerts || alerts.length === 0) {\n $alerts.innerHTML = '<div class=\"empty-state\">No active violations. Economy is healthy.</div>';\n $alertsCount.textContent = 'All clear';\n return;\n }\n var sorted = alerts.slice().sort(function(a, b) { return (b.severity || 0) - (a.severity || 0); });\n $alertsCount.textContent = sorted.length + ' violation' + (sorted.length !== 1 ? 's' : '');\n\n $alerts.innerHTML = sorted.map(function(a) {\n var sev = a.severity || a.violation?.severity || 0;\n var sc = sevClass(sev);\n var cardCls = sevCardClass(sev);\n var name = a.principleName || a.principle?.name || '?';\n var pid = a.principleId || a.principle?.id || '?';\n var reason = a.reasoning || a.violation?.suggestedAction?.reasoning || '';\n var suggestion = a.suggestion || '';\n\n var hasPending = isAdvisor && pendingDecisions.some(function(pd) {\n return pd.principleId === pid || (pd.diagnosis?.principle?.id === pid);\n });\n\n var btns = '';\n if (hasPending) {\n btns = '<button class=\"alert-approve-btn\" data-action=\"approve-alert\" data-principle=\"' + esc(pid) + '\">&#10003; Approve Fix</button>'\n + '<button class=\"alert-reject-btn\" data-action=\"reject-alert\" data-principle=\"' + esc(pid) + '\">&#10007; Reject</button>';\n }\n\n return '<div class=\"alert-card ' + cardCls + '\" data-principle-id=\"' + esc(pid) + '\">'\n + '<div class=\"alert-top\">'\n + '<span class=\"sev-badge ' + sc + '\">' + sev + '</span>'\n + '<span class=\"alert-principle-name\">[' + esc(pid) + '] ' + esc(name) + '</span>'\n + '</div>'\n + (reason ? '<div class=\"alert-evidence\">' + esc(reason) + '</div>' : '')\n + (suggestion ? '<div class=\"alert-suggestion\">Suggested: ' + esc(suggestion) + '</div>' : '')\n + btns\n + '</div>';\n }).join('');\n }\n\n // -- Violations table --\n function addViolation(d) {\n var plan = d.plan || {};\n violations.push({\n tick: d.tick,\n principle: (d.diagnosis?.principle?.id || '?') + ' ' + (d.diagnosis?.principle?.name || ''),\n severity: d.diagnosis?.violation?.severity || 0,\n parameter: plan.parameter || '\\\\u2014',\n result: d.result,\n currentValue: plan.currentValue,\n targetValue: plan.targetValue,\n decisionId: d.id,\n });\n if (violations.length > MAX_VIOLATIONS) violations.shift();\n decisionCount = violations.length;\n $decisionCount.textContent = decisionCount + ' decisions';\n renderViolations();\n }\n\n function renderViolations() {\n var sorted = violations.slice().sort(function(a, b) {\n var va = a[violationSortKey], vb = b[violationSortKey];\n if (va < vb) return violationSortAsc ? -1 : 1;\n if (va > vb) return violationSortAsc ? 1 : -1;\n return 0;\n });\n $violationsBody.innerHTML = sorted.map(function(v) {\n var isPending = v.result === 'skipped_override';\n\n var actionHtml;\n if (isPending && isAdvisor) {\n actionHtml = '<span class=\"badge-pending\" data-action=\"toggle-pending\" data-id=\"' + esc(v.decisionId || '') + '\">'\n + 'Pending \\\\u25BE'\n + '<div class=\"pending-dropdown\">'\n + '<div class=\"pending-dropdown-item approve-item\" data-action=\"approve\" data-id=\"' + esc(v.decisionId || '') + '\">&#10003; Approve</div>'\n + '<div class=\"pending-dropdown-item reject-item\" data-action=\"reject\" data-id=\"' + esc(v.decisionId || '') + '\">&#10007; Reject</div>'\n + '</div>'\n + '</span>';\n } else if (v.result === 'applied') {\n actionHtml = '<span class=\"badge-applied\">Applied</span>';\n } else if (v.result === 'rejected') {\n actionHtml = '<span class=\"badge-rejected\">Rejected</span>';\n } else {\n actionHtml = '<span class=\"badge-skipped\">' + esc(v.result || 'Skipped') + '</span>';\n }\n\n var changeHtml = '';\n if (v.currentValue != null && v.targetValue != null) {\n var valColor = isPending && isAdvisor ? 'var(--warning)' : 'var(--accent)';\n changeHtml = '<span style=\"color:var(--text-value)\">' + fmt(v.currentValue) + '</span>'\n + '<span style=\"color:var(--info)\"> \\\\u2192 </span>'\n + '<span style=\"color:' + valColor + '\">' + fmt(v.targetValue) + '</span>';\n }\n\n var sevColor = v.severity >= 7 ? 'var(--danger)' : v.severity >= 4 ? 'var(--warning)' : 'var(--accent)';\n\n return '<tr>'\n + '<td style=\"color:var(--text-secondary)\">' + v.tick + '</td>'\n + '<td style=\"color:var(--text-value)\">' + esc(v.principle) + '</td>'\n + '<td style=\"color:' + sevColor + '\">' + v.severity + '</td>'\n + '<td style=\"color:var(--text-tertiary)\">' + esc(v.parameter) + '</td>'\n + '<td class=\"action-cell\">' + actionHtml + '</td>'\n + '<td>' + changeHtml + '</td>'\n + '</tr>';\n }).join('');\n }\n\n // Table sorting\n document.querySelectorAll('.violations-table th[data-sort]').forEach(function(th) {\n th.addEventListener('click', function() {\n var key = th.dataset.sort;\n if (violationSortKey === key) violationSortAsc = !violationSortAsc;\n else { violationSortKey = key; violationSortAsc = true; }\n renderViolations();\n });\n });\n\n // -- Personas --\n function renderPersonas(dist) {\n if (!dist || Object.keys(dist).length === 0) {\n $personaBars.innerHTML = '<div class=\"empty-state\">No persona data yet</div>';\n $personaCount.textContent = '';\n return;\n }\n var total = Object.values(dist).reduce(function(s, v) { return s + v; }, 0);\n $personaCount.textContent = total + ' agents';\n var entries = Object.entries(dist).sort(function(a, b) { return b[1] - a[1]; });\n $personaBars.innerHTML = entries.map(function(e) {\n var pctVal = total > 0 ? (e[1] / total * 100) : 0;\n var color = personaColor(e[0]);\n return '<div class=\"persona-row\">'\n + '<span class=\"persona-label\">' + esc(e[0]) + '</span>'\n + '<div class=\"persona-bar-track\"><div class=\"persona-bar-fill\" style=\"width:' + pctVal.toFixed(0) + '%;background:' + color + ';\"></div></div>'\n + '<span class=\"persona-pct\">' + pctVal.toFixed(0) + '%</span>'\n + '</div>';\n }).join('');\n }\n\n // -- Parameters --\n function renderParams(principles, registryValues) {\n if ((!principles || principles.length === 0) && Object.keys(paramValues).length === 0) {\n $paramsList.innerHTML = '<div class=\"empty-state\">No parameters registered</div>';\n $paramsCount.textContent = '';\n return;\n }\n\n // If we have actual parameter values from API, show those\n if (registryValues && Object.keys(registryValues).length > 0) {\n var entries = Object.entries(registryValues);\n $paramsCount.textContent = entries.length + ' tracked';\n $paramsList.innerHTML = entries.map(function(e) {\n var key = e[0];\n var val = e[1];\n var ticksAgo = currentTick - (paramLastTick[key] || 0);\n var agoText = ticksAgo <= 0 ? '' : ticksAgo <= 5 ? 'just now' : ticksAgo + ' ticks ago';\n var pending = paramPending[key];\n\n if (pending && isAdvisor) {\n return '<div class=\"param-row\">'\n + '<span class=\"param-name\">' + esc(key) + '</span>'\n + '<span>'\n + '<span class=\"param-val\">' + fmt(val) + '</span>'\n + '<span class=\"param-ghost\" style=\"display:inline;\"> \\\\u2192 ' + fmt(pending.proposedVal) + '?</span>'\n + '<span class=\"param-pending-label\" style=\"display:inline;\">pending</span>'\n + '</span>'\n + '</div>';\n }\n return '<div class=\"param-row\">'\n + '<span class=\"param-name\">' + esc(key) + '</span>'\n + '<span><span class=\"param-val\">' + fmt(val) + '</span>'\n + (agoText ? '<span class=\"param-changed\">' + agoText + '</span>' : '')\n + '</span>'\n + '</div>';\n }).join('');\n return;\n }\n\n // Fallback: show principle names (legacy behavior)\n if (principles && principles.length > 0) {\n $paramsCount.textContent = principles.length + ' registered';\n $paramsList.innerHTML = principles.slice(0, 30).map(function(p) {\n return '<div class=\"param-row\">'\n + '<span class=\"param-name\">[' + esc(p.id) + ']</span>'\n + '<span class=\"param-val\">' + esc(p.name) + '</span>'\n + '</div>';\n }).join('');\n }\n }\n\n // -- KPI update --\n function updateKPIs(data) {\n if (data.health != null) {\n $hHealth.textContent = data.health + '/100';\n $hHealth.className = 'value ' + healthClass(data.health);\n }\n if (data.mode != null) {\n $hMode.textContent = data.mode;\n isAdvisor = data.mode === 'advisor';\n $hMode.className = 'value ' + (isAdvisor ? 'mode-value-advisor' : 'mode-value-auto');\n document.body.classList.toggle('advisor-mode', isAdvisor);\n }\n if (data.tick != null) {\n $hTick.textContent = data.tick;\n currentTick = data.tick;\n }\n if (data.uptime != null) $hUptime.textContent = formatUptime(data.uptime);\n if (data.pendingCount != null || data.activePlans != null) {\n var count = data.pendingCount || data.activePlans || 0;\n $hPending.textContent = count;\n }\n }\n\n // -- Metrics history --\n function updateChartsFromHistory(history) {\n if (!history || history.length === 0) return;\n var ticks = history.map(function(h) { return h.tick; });\n updateChart(chartHealth, ticks, history.map(function(h) { return h.health; }));\n updateChart(chartGini, ticks, history.map(function(h) { return h.giniCoefficient; }));\n updateChart(chartFlow, ticks, history.map(function(h) { return h.netFlow; }));\n updateChart(chartSatisfaction, ticks, history.map(function(h) { return h.avgSatisfaction; }));\n }\n\n // -- API calls --\n function fetchJSON(path) {\n return fetch(path).then(function(r) { return r.json(); });\n }\n\n function postJSON(path, body) {\n return fetch(path, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(body),\n }).then(function(r) { return r.json(); });\n }\n\n function loadInitialData() {\n fetchJSON('/health').then(function(data) {\n updateKPIs(data);\n }).catch(function() {});\n\n fetchJSON('/decisions?limit=50').then(function(data) {\n if (data.decisions) {\n data.decisions.reverse().forEach(function(d) {\n addTerminalLine(decisionToTerminal(d));\n addViolation(d);\n });\n }\n }).catch(function() {});\n\n fetchJSON('/metrics').then(function(data) {\n if (data.history) updateChartsFromHistory(data.history);\n if (data.latest) {\n renderPersonas(data.latest.personaDistribution);\n }\n }).catch(function() {});\n\n fetchJSON('/principles').then(function(data) {\n if (data.principles) {\n paramRegistry = data.principles;\n renderParams(data.principles, paramValues);\n }\n }).catch(function() {});\n\n fetchJSON('/pending').then(function(data) {\n if (data.pending) {\n pendingDecisions = data.pending;\n $hPending.textContent = data.count || 0;\n }\n }).catch(function() {});\n }\n\n // -- Polling fallback --\n var pollInterval = null;\n\n function startPolling() {\n if (pollInterval) return;\n pollInterval = setInterval(function() {\n fetchJSON('/health').then(updateKPIs).catch(function() {});\n fetchJSON('/metrics').then(function(data) {\n if (data.history) updateChartsFromHistory(data.history);\n if (data.latest) renderPersonas(data.latest.personaDistribution);\n }).catch(function() {});\n }, 5000);\n }\n\n function stopPolling() {\n if (pollInterval) { clearInterval(pollInterval); pollInterval = null; }\n }\n\n // -- WebSocket --\n function connectWS() {\n var proto = location.protocol === 'https:' ? 'wss:' : 'ws:';\n ws = new WebSocket(proto + '//' + location.host);\n\n ws.onopen = function() {\n reconnectDelay = 1000;\n $liveDot.classList.remove('disconnected');\n $liveDot.title = 'WebSocket connected';\n stopPolling();\n ws.send(JSON.stringify({ type: 'health' }));\n };\n\n ws.onclose = function() {\n $liveDot.classList.add('disconnected');\n $liveDot.title = 'WebSocket disconnected \\\\u2014 reconnecting...';\n startPolling();\n setTimeout(connectWS, reconnectDelay);\n reconnectDelay = Math.min(reconnectDelay * 1.5, MAX_RECONNECT);\n };\n\n ws.onerror = function() { ws.close(); };\n\n ws.onmessage = function(ev) {\n var msg;\n try { msg = JSON.parse(ev.data); } catch(e) { return; }\n\n switch (msg.type) {\n case 'tick_result':\n updateKPIs({ health: msg.health, tick: msg.tick });\n if (msg.alerts) renderAlerts(msg.alerts);\n fetchJSON('/metrics').then(function(data) {\n if (data.history) updateChartsFromHistory(data.history);\n if (data.latest) renderPersonas(data.latest.personaDistribution);\n }).catch(function() {});\n break;\n\n case 'health_result':\n updateKPIs(msg);\n break;\n\n case 'advisor_action':\n if (msg.action === 'approved' || msg.action === 'rejected') {\n pendingDecisions = pendingDecisions.filter(function(d) {\n return d.id !== msg.decisionId;\n });\n $hPending.textContent = pendingDecisions.length;\n }\n break;\n }\n };\n }\n\n // -- Advisor actions (event delegation) --\n document.addEventListener('click', function(e) {\n var btn = e.target.closest('[data-action]');\n if (!btn) return;\n var action = btn.getAttribute('data-action');\n var id = btn.getAttribute('data-id');\n var principleId = btn.getAttribute('data-principle');\n\n // Toggle pending dropdown\n if (action === 'toggle-pending') {\n var dropdown = btn.querySelector('.pending-dropdown');\n if (!dropdown) return;\n document.querySelectorAll('.pending-dropdown.open').forEach(function(d) {\n if (d !== dropdown) d.classList.remove('open');\n });\n dropdown.classList.toggle('open');\n e.stopPropagation();\n return;\n }\n\n // Approve from alert card\n if (action === 'approve-alert' && principleId) {\n var pd = pendingDecisions.find(function(d) {\n return d.principleId === principleId || (d.diagnosis?.principle?.id === principleId);\n });\n if (pd) {\n postJSON('/approve', { decisionId: pd.id }).then(function(data) {\n if (data.ok) {\n addTerminalLine('<span class=\"t-tick\">[Advisor]</span> <span class=\"t-check\">\\\\u2705 Approved ' + esc(pd.id) + '</span>');\n }\n }).catch(function() {});\n }\n return;\n }\n\n // Reject from alert card\n if (action === 'reject-alert' && principleId) {\n var pd2 = pendingDecisions.find(function(d) {\n return d.principleId === principleId || (d.diagnosis?.principle?.id === principleId);\n });\n if (pd2) {\n var reason = prompt('Rejection reason (optional):');\n postJSON('/reject', { decisionId: pd2.id, reason: reason || undefined }).then(function(data) {\n if (data.ok) {\n addTerminalLine('<span class=\"t-tick\">[Advisor]</span> <span class=\"t-fail\">\\\\u274c Rejected ' + esc(pd2.id) + '</span>');\n }\n }).catch(function() {});\n }\n return;\n }\n\n if (!id) return;\n\n if (action === 'approve') {\n postJSON('/approve', { decisionId: id }).then(function(data) {\n if (data.ok) {\n addTerminalLine('<span class=\"t-tick\">[Advisor]</span> <span class=\"t-check\">\\\\u2705 Approved ' + esc(id) + '</span>');\n }\n }).catch(function() {});\n } else if (action === 'reject') {\n var reason2 = prompt('Rejection reason (optional):');\n postJSON('/reject', { decisionId: id, reason: reason2 || undefined }).then(function(data) {\n if (data.ok) {\n addTerminalLine('<span class=\"t-tick\">[Advisor]</span> <span class=\"t-fail\">\\\\u274c Rejected ' + esc(id) + '</span>');\n }\n }).catch(function() {});\n }\n });\n\n // Close dropdowns on outside click\n document.addEventListener('click', function(e) {\n if (!e.target.closest('.badge-pending')) {\n document.querySelectorAll('.pending-dropdown.open').forEach(function(d) { d.classList.remove('open'); });\n }\n });\n\n // -- Init --\n initCharts();\n loadInitialData();\n connectWS();\n\n})();\n</script>\n</body>\n</html>`;\n}\n","// WebSocket handler for AgentE Server\n// Same port via HTTP upgrade. JSON messages with `type` field.\n\nimport type * as http from 'node:http';\nimport { timingSafeEqual } from 'node:crypto';\nimport { WebSocketServer, WebSocket } from 'ws';\nimport { validateEconomyState, type EconomyState, type EconomicEvent } from '@agent-e/core';\nimport type { AgentEServer } from './AgentEServer.js';\n\ninterface IncomingMessage {\n type: string;\n [key: string]: unknown;\n}\n\nfunction send(ws: WebSocket, data: Record<string, unknown>): void {\n if (ws.readyState === WebSocket.OPEN) {\n ws.send(JSON.stringify(data));\n }\n}\n\nexport interface WebSocketHandle {\n cleanup: () => void;\n broadcast: (data: Record<string, unknown>) => void;\n}\n\nconst MAX_WS_PAYLOAD = 1_048_576; // 1 MB\nconst MAX_WS_CONNECTIONS = 100;\nconst MIN_TICK_INTERVAL_MS = 100; // rate limit: max 10 ticks/sec per connection\n\n/** Strips prototype-polluting keys from parsed JSON objects (recursive). */\nfunction sanitizeJson(obj: unknown): unknown {\n if (obj === null || typeof obj !== 'object') return obj;\n if (Array.isArray(obj)) return obj.map(sanitizeJson);\n const clean: Record<string, unknown> = {};\n for (const [key, val] of Object.entries(obj as Record<string, unknown>)) {\n if (key === '__proto__' || key === 'constructor' || key === 'prototype') continue;\n clean[key] = sanitizeJson(val);\n }\n return clean;\n}\n\nexport function createWebSocketHandler(\n httpServer: http.Server,\n server: AgentEServer,\n): WebSocketHandle {\n const wss = new WebSocketServer({ server: httpServer, maxPayload: MAX_WS_PAYLOAD });\n\n // Heartbeat: ping every 30s, disconnect if no pong within 10s\n const aliveMap = new WeakMap<WebSocket, boolean>();\n\n const heartbeatInterval = setInterval(() => {\n for (const ws of wss.clients) {\n if (ws.readyState === WebSocket.OPEN) {\n if (aliveMap.get(ws) === false) {\n // No pong received since last ping — terminate\n ws.terminate();\n continue;\n }\n aliveMap.set(ws, false);\n ws.ping();\n }\n }\n }, 30_000);\n\n wss.on('connection', (ws, req) => {\n if (wss.clients.size > MAX_WS_CONNECTIONS) {\n ws.close(1013, 'Server at capacity');\n return;\n }\n\n // Origin check: validate against CORS policy (skip for non-browser / missing origin)\n const wsOrigin = req.headers['origin'];\n if (wsOrigin && server.corsOrigin !== '*') {\n if (wsOrigin.toLowerCase() !== server.corsOrigin.toLowerCase()) {\n ws.close(1008, 'Origin not allowed');\n return;\n }\n }\n\n // Auth check: if apiKey is configured, require it via query param or protocol header\n if (server.apiKey) {\n const url = new URL(req.url ?? '/', `http://${req.headers.host ?? 'localhost'}`);\n const token = url.searchParams.get('token') ?? req.headers['authorization']?.replace('Bearer ', '');\n if (!token || token.length !== server.apiKey.length || !timingSafeEqual(Buffer.from(token), Buffer.from(server.apiKey))) {\n ws.close(1008, 'Unauthorized');\n return;\n }\n }\n\n console.log('[AgentE Server] Client connected');\n aliveMap.set(ws, true);\n\n let lastTickTime = 0;\n\n ws.on('pong', () => {\n aliveMap.set(ws, true);\n });\n\n ws.on('close', () => {\n console.log('[AgentE Server] Client disconnected');\n });\n\n ws.on('message', async (raw) => {\n let msg: IncomingMessage;\n try {\n msg = sanitizeJson(JSON.parse(raw.toString())) as IncomingMessage;\n } catch {\n send(ws, { type: 'error', message: 'Malformed JSON' });\n return;\n }\n\n if (!msg.type || typeof msg.type !== 'string') {\n send(ws, { type: 'error', message: 'Missing \"type\" field' });\n return;\n }\n\n switch (msg.type) {\n case 'tick': {\n const now = Date.now();\n if (now - lastTickTime < MIN_TICK_INTERVAL_MS) {\n send(ws, { type: 'error', message: 'Rate limited — min 100ms between ticks' });\n break;\n }\n lastTickTime = now;\n\n const state = msg['state'];\n const events = msg['events'];\n\n if (server.validateState) {\n const validation = validateEconomyState(state);\n if (!validation.valid) {\n send(ws, { type: 'validation_error', validationErrors: validation.errors });\n return;\n }\n\n // Forward warnings even if valid\n if (validation.warnings.length > 0) {\n send(ws, { type: 'validation_warning', validationWarnings: validation.warnings });\n }\n }\n\n try {\n const result = await server.processTick(\n state as EconomyState,\n Array.isArray(events) ? events as EconomicEvent[] : undefined,\n );\n\n send(ws, {\n type: 'tick_result',\n adjustments: result.adjustments,\n alerts: result.alerts.map(a => ({\n principleId: a.principle.id,\n principleName: a.principle.name,\n severity: a.violation.severity,\n reasoning: a.violation.suggestedAction.reasoning,\n })),\n health: result.health,\n tick: result.tick,\n });\n } catch (_err) {\n send(ws, { type: 'error', message: 'Tick processing failed' });\n }\n break;\n }\n\n case 'event': {\n const event = msg['event'] as EconomicEvent | undefined;\n if (event) {\n server.getAgentE().ingest(event);\n send(ws, { type: 'event_ack' });\n } else {\n send(ws, { type: 'error', message: 'Missing \"event\" field' });\n }\n break;\n }\n\n case 'health': {\n const agentE = server.getAgentE();\n send(ws, {\n type: 'health_result',\n health: agentE.getHealth(),\n tick: agentE.metrics.latest()?.tick ?? 0,\n mode: agentE.getMode(),\n activePlans: agentE.getActivePlans().length,\n uptime: server.getUptime(),\n });\n break;\n }\n\n case 'diagnose': {\n const state = msg['state'];\n\n if (server.validateState) {\n const validation = validateEconomyState(state);\n if (!validation.valid) {\n send(ws, { type: 'validation_error', validationErrors: validation.errors });\n return;\n }\n }\n\n const result = server.diagnoseOnly(state as EconomyState);\n send(ws, {\n type: 'diagnose_result',\n health: result.health,\n diagnoses: result.diagnoses.map(d => ({\n principleId: d.principle.id,\n principleName: d.principle.name,\n severity: d.violation.severity,\n suggestedAction: d.violation.suggestedAction,\n })),\n });\n break;\n }\n\n default:\n send(ws, { type: 'error', message: `Unknown message type: \"${String(msg.type).slice(0, 100)}\"` });\n }\n });\n });\n\n function broadcast(data: Record<string, unknown>): void {\n const payload = JSON.stringify(data);\n for (const ws of wss.clients) {\n if (ws.readyState === WebSocket.OPEN) {\n ws.send(payload);\n }\n }\n }\n\n return {\n cleanup: () => {\n clearInterval(heartbeatInterval);\n wss.close();\n },\n broadcast,\n };\n}\n"],"mappings":";AAEA,YAAY,UAAU;AACtB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAQK;;;ACZP,SAAS,uBAAuB;AAChC,SAAS,4BAA4B;;;ACF9B,SAAS,mBAA2B;AACzC,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;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","names":["timingSafeEqual","validateEconomyState","sanitizeJson"]}
1
+ {"version":3,"sources":["../src/AgentEServer.ts","../src/routes.ts","../src/dashboard.ts","../src/websocket.ts"],"sourcesContent":["// AgentEServer — HTTP + WebSocket transport for AgentE\n\nimport * as http from 'node:http';\nimport {\n AgentE,\n Observer,\n Diagnoser,\n ALL_PRINCIPLES,\n DEFAULT_THRESHOLDS,\n type AgentEConfig,\n type EconomyAdapter,\n type EconomyState,\n type EconomicEvent,\n type Diagnosis,\n type AgentEMode,\n type Thresholds,\n} from '@agent-e/core';\nimport { createRouteHandler } from './routes.js';\nimport { createWebSocketHandler, type WebSocketHandle } from './websocket.js';\n\nexport interface ServerConfig {\n port?: number;\n host?: string;\n agentE?: Partial<Omit<AgentEConfig, 'adapter'>>;\n validateState?: boolean;\n corsOrigin?: string;\n serveDashboard?: boolean;\n /** API key for authenticating mutation routes. When set, POST routes and WebSocket require `Authorization: Bearer <key>`. */\n apiKey?: string;\n}\n\nexport interface EnrichedAdjustment {\n parameter: string;\n value: number;\n scope?: import('@agent-e/core').ParameterScope;\n reasoning: string;\n}\n\ninterface QueuedAdjustment {\n key: string;\n value: number;\n scope: import('@agent-e/core').ParameterScope | undefined;\n}\n\nexport class AgentEServer {\n private readonly agentE: AgentE;\n private readonly server: http.Server;\n private lastState: EconomyState | null = null;\n private adjustmentQueue: QueuedAdjustment[] = [];\n private alerts: Diagnosis[] = [];\n readonly port: number;\n private readonly host: string;\n private readonly thresholds: Thresholds;\n private readonly startedAt = Date.now();\n private wsHandle: WebSocketHandle | null = null;\n readonly validateState: boolean;\n readonly corsOrigin: string;\n readonly serveDashboard: boolean;\n readonly apiKey: string | undefined;\n\n constructor(config: ServerConfig = {}) {\n this.port = config.port ?? 3100;\n this.host = config.host ?? '127.0.0.1';\n this.apiKey = config.apiKey;\n this.validateState = config.validateState ?? true;\n this.corsOrigin = config.corsOrigin ?? 'http://localhost:3100';\n this.serveDashboard = config.serveDashboard ?? true;\n\n // Build a \"remote\" adapter — state comes from HTTP/WS, not polled\n const adapter: EconomyAdapter = {\n getState: () => {\n if (!this.lastState) {\n return {\n tick: 0,\n roles: [],\n resources: [],\n currencies: ['default'],\n agentBalances: {},\n agentRoles: {},\n agentInventories: {},\n marketPrices: {},\n recentTransactions: [],\n };\n }\n return this.lastState;\n },\n setParam: (key: string, value: number, scope?: import('@agent-e/core').ParameterScope) => {\n this.adjustmentQueue.push({ key, value, scope });\n },\n };\n\n const agentECfg = config.agentE ?? {};\n const agentEConfig: AgentEConfig = {\n adapter,\n mode: agentECfg.mode ?? 'autonomous',\n gracePeriod: agentECfg.gracePeriod ?? 0,\n checkInterval: agentECfg.checkInterval ?? 1,\n ...(agentECfg.dominantRoles ? { dominantRoles: agentECfg.dominantRoles } : {}),\n ...(agentECfg.idealDistribution ? { idealDistribution: agentECfg.idealDistribution } : {}),\n ...(agentECfg.maxAdjustmentPercent !== undefined ? { maxAdjustmentPercent: agentECfg.maxAdjustmentPercent } : {}),\n ...(agentECfg.cooldownTicks !== undefined ? { cooldownTicks: agentECfg.cooldownTicks } : {}),\n ...(agentECfg.thresholds ? { thresholds: agentECfg.thresholds } : {}),\n };\n\n this.thresholds = {\n ...DEFAULT_THRESHOLDS,\n ...(agentECfg.thresholds ?? {}),\n ...(agentECfg.maxAdjustmentPercent !== undefined ? { maxAdjustmentPercent: agentECfg.maxAdjustmentPercent } : {}),\n ...(agentECfg.cooldownTicks !== undefined ? { cooldownTicks: agentECfg.cooldownTicks } : {}),\n };\n this.agentE = new AgentE(agentEConfig);\n\n // Capture alerts during tick\n this.agentE.on('alert', (diagnosis: unknown) => {\n this.alerts.push(diagnosis as Diagnosis);\n });\n\n this.agentE.connect(adapter).start();\n\n // Create HTTP server\n const routeHandler = createRouteHandler(this);\n this.server = http.createServer(routeHandler);\n }\n\n async start(): Promise<void> {\n // Wire up WebSocket upgrade\n this.wsHandle = createWebSocketHandler(this.server, this);\n\n return new Promise((resolve) => {\n this.server.listen(this.port, this.host, () => {\n const addr = this.getAddress();\n console.log(`[AgentE Server] Listening on http://${addr.host}:${addr.port}`);\n resolve();\n });\n });\n }\n\n async stop(): Promise<void> {\n this.agentE.stop();\n if (this.wsHandle) this.wsHandle.cleanup();\n return new Promise((resolve, reject) => {\n this.server.close((err) => {\n if (err) reject(err);\n else resolve();\n });\n });\n }\n\n getAgentE(): AgentE {\n return this.agentE;\n }\n\n getAddress(): { port: number; host: string } {\n const addr = this.server.address();\n if (addr && typeof addr === 'object') {\n return { port: addr.port, host: addr.address };\n }\n return { port: this.port, host: this.host };\n }\n\n getUptime(): number {\n return Date.now() - this.startedAt;\n }\n\n /**\n * Process a tick with the given state.\n * 1. Clear adjustment queue\n * 2. Set state\n * 3. Ingest events\n * 4. Run agentE.tick(state)\n * 5. Drain adjustment queue, enrich with reasoning from decisions\n * 6. Return response\n */\n async processTick(\n state: EconomyState,\n events?: EconomicEvent[],\n ): Promise<{\n adjustments: EnrichedAdjustment[];\n alerts: Diagnosis[];\n health: number;\n tick: number;\n decisions: ReturnType<AgentE['getDecisions']>;\n }> {\n // Clear queues\n this.adjustmentQueue = [];\n this.alerts = [];\n\n // Set state\n this.lastState = state;\n\n // Ingest events\n if (events) {\n for (const event of events) {\n this.agentE.ingest(event);\n }\n }\n\n // Run tick\n await this.agentE.tick(state);\n\n // Drain adjustments\n const rawAdj = [...this.adjustmentQueue];\n this.adjustmentQueue = [];\n\n // Cross-reference with decision log to attach reasoning\n const decisions = this.agentE.getDecisions({ since: state.tick, until: state.tick });\n\n const adjustments: EnrichedAdjustment[] = rawAdj.map(adj => {\n const decision = decisions.find(d =>\n d.plan.parameter === adj.key && d.result === 'applied',\n );\n return {\n parameter: adj.key,\n value: adj.value,\n ...(adj.scope ? { scope: adj.scope } : {}),\n reasoning: decision?.diagnosis.violation.suggestedAction.reasoning ?? '',\n };\n });\n\n return {\n adjustments,\n alerts: [...this.alerts],\n health: this.agentE.getHealth(),\n tick: state.tick,\n decisions,\n };\n }\n\n /**\n * Run Observer + Diagnoser on the given state without side effects (no execution).\n * Computes fresh metrics from the state rather than reading stored metrics.\n */\n diagnoseOnly(state: EconomyState): {\n diagnoses: ReturnType<AgentE['diagnoseNow']>;\n health: number;\n } {\n const observer = new Observer();\n const diagnoser = new Diagnoser(ALL_PRINCIPLES);\n const metrics = observer.compute(state, []);\n const diagnoses = diagnoser.diagnose(metrics, this.thresholds);\n\n // Mirrors AgentE.getHealth() — keep in sync if that logic changes\n let health = 100;\n if (metrics.avgSatisfaction < 65) health -= 15;\n if (metrics.avgSatisfaction < 50) health -= 10;\n if (metrics.giniCoefficient > 0.45) health -= 15;\n if (metrics.giniCoefficient > 0.60) health -= 10;\n if (Math.abs(metrics.netFlow) > 10) health -= 15;\n if (Math.abs(metrics.netFlow) > 20) health -= 10;\n if (metrics.churnRate > 0.05) health -= 15;\n health = Math.max(0, Math.min(100, health));\n\n return { diagnoses, health };\n }\n\n setMode(mode: AgentEMode): void {\n this.agentE.setMode(mode);\n }\n\n lock(param: string): void {\n this.agentE.lock(param);\n }\n\n unlock(param: string): void {\n this.agentE.unlock(param);\n }\n\n constrain(param: string, bounds: { min: number; max: number }): void {\n this.agentE.constrain(param, bounds);\n }\n\n broadcast(data: Record<string, unknown>): void {\n if (this.wsHandle) this.wsHandle.broadcast(data);\n }\n}\n","// HTTP routes for AgentE Server\n// Node http module with manual body parsing. CORS on all responses.\n\nimport type * as http from 'node:http';\nimport { timingSafeEqual } from 'node:crypto';\nimport { validateEconomyState } from '@agent-e/core';\nimport type { AgentEServer } from './AgentEServer.js';\nimport { getDashboardHtml } from './dashboard.js';\n\nfunction setSecurityHeaders(res: http.ServerResponse): void {\n res.setHeader('X-Content-Type-Options', 'nosniff');\n res.setHeader('X-Frame-Options', 'DENY');\n res.setHeader('Referrer-Policy', 'strict-origin-when-cross-origin');\n}\n\nfunction setCorsHeaders(res: http.ServerResponse, allowedOrigin: string, requestOrigin?: string): void {\n setSecurityHeaders(res);\n // If configured as '*', allow all.\n // Otherwise, only reflect the origin if it matches the configured allowedOrigin.\n // If there's no request origin (non-browser / server-to-server), return allowedOrigin directly.\n let origin: string;\n if (allowedOrigin === '*') {\n origin = '*';\n } else if (requestOrigin === undefined) {\n origin = allowedOrigin; // non-browser request, return configured origin\n } else {\n // Reflect origin only if it matches (case-insensitive) — otherwise don't include a matching CORS header\n origin = requestOrigin.toLowerCase() === allowedOrigin.toLowerCase() ? requestOrigin : '';\n }\n if (origin) {\n res.setHeader('Access-Control-Allow-Origin', origin);\n }\n res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');\n res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');\n}\n\n/** Strips prototype-polluting keys from parsed JSON objects (recursive). */\nfunction sanitizeJson(obj: unknown): unknown {\n if (obj === null || typeof obj !== 'object') return obj;\n if (Array.isArray(obj)) return obj.map(sanitizeJson);\n const clean: Record<string, unknown> = {};\n for (const [key, val] of Object.entries(obj as Record<string, unknown>)) {\n if (key === '__proto__' || key === 'constructor' || key === 'prototype') continue;\n clean[key] = sanitizeJson(val);\n }\n return clean;\n}\n\nfunction checkAuth(req: http.IncomingMessage, apiKey: string | undefined): boolean {\n if (!apiKey) return true; // no key configured = open\n const header = req.headers['authorization'];\n if (typeof header !== 'string') return false;\n const expected = `Bearer ${apiKey}`;\n if (header.length !== expected.length) return false;\n return timingSafeEqual(Buffer.from(header), Buffer.from(expected));\n}\n\nfunction json(res: http.ServerResponse, status: number, data: unknown, origin: string, reqOrigin?: string): void {\n setCorsHeaders(res, origin, reqOrigin);\n res.writeHead(status, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify(data));\n}\n\nconst MAX_BODY_BYTES = 1_048_576; // 1 MB\n\nfunction readBody(req: http.IncomingMessage): Promise<string> {\n return new Promise((resolve, reject) => {\n const chunks: Buffer[] = [];\n let totalBytes = 0;\n req.on('data', (chunk: Buffer) => {\n totalBytes += chunk.length;\n if (totalBytes > MAX_BODY_BYTES) {\n req.destroy();\n reject(new Error('Request body too large'));\n return;\n }\n chunks.push(chunk);\n });\n req.on('end', () => resolve(Buffer.concat(chunks).toString('utf-8')));\n req.on('error', reject);\n });\n}\n\nexport function createRouteHandler(\n server: AgentEServer,\n): (req: http.IncomingMessage, res: http.ServerResponse) => void {\n const cors = server.corsOrigin;\n const apiKey = server.apiKey;\n\n return async (req, res) => {\n const url = new URL(req.url ?? '/', `http://${req.headers.host ?? 'localhost'}`);\n const path = url.pathname;\n const method = req.method?.toUpperCase() ?? 'GET';\n const reqOrigin = req.headers['origin'] as string | undefined;\n\n // Scoped json helper — captures cors + reqOrigin for this request\n const respond = (status: number, data: unknown) => json(res, status, data, cors, reqOrigin);\n\n // CORS preflight\n if (method === 'OPTIONS') {\n setCorsHeaders(res, cors, reqOrigin);\n res.writeHead(204);\n res.end();\n return;\n }\n\n try {\n // POST /tick — validate state, run tick, return adjustments/alerts/health\n if (path === '/tick' && method === 'POST') {\n if (!checkAuth(req, apiKey)) {\n respond(401, { error: 'Unauthorized' });\n return;\n }\n const body = await readBody(req);\n let parsed: unknown;\n try {\n parsed = sanitizeJson(JSON.parse(body));\n } catch {\n respond(400, { error: 'Invalid JSON' });\n return;\n }\n\n if (!parsed || typeof parsed !== 'object') {\n respond(400, { error: 'Body must be a JSON object' });\n return;\n }\n\n const payload = parsed as Record<string, unknown>;\n const state = payload['state'] ?? parsed;\n const events = payload['events'];\n\n // Validate state (if enabled)\n const validation = server.validateState ? validateEconomyState(state) : null;\n if (validation && !validation.valid) {\n respond(400, {\n error: 'invalid_state',\n validationErrors: validation.errors,\n });\n return;\n }\n\n const result = await server.processTick(\n state as import('@agent-e/core').EconomyState,\n Array.isArray(events) ? events as import('@agent-e/core').EconomicEvent[] : undefined,\n );\n\n const warnings = validation?.warnings ?? [];\n\n respond(200, {\n adjustments: result.adjustments,\n alerts: result.alerts.map(a => ({\n principleId: a.principle.id,\n principleName: a.principle.name,\n severity: a.violation.severity,\n evidence: a.violation.evidence,\n reasoning: a.violation.suggestedAction.reasoning,\n })),\n health: result.health,\n tick: result.tick,\n ...(warnings.length > 0 ? { validationWarnings: warnings } : {}),\n });\n return;\n }\n\n // GET /health — health, tick, mode, activePlans, uptime\n if (path === '/health' && method === 'GET') {\n const agentE = server.getAgentE();\n respond(200, {\n health: agentE.getHealth(),\n tick: agentE.metrics.latest()?.tick ?? 0,\n mode: agentE.getMode(),\n activePlans: agentE.getActivePlans().length,\n uptime: server.getUptime(),\n });\n return;\n }\n\n // GET /decisions — decision log with optional ?limit and ?since\n if (path === '/decisions' && method === 'GET') {\n const rawLimit = parseInt(url.searchParams.get('limit') ?? '100', 10);\n const limit = Math.min(Math.max(Number.isNaN(rawLimit) ? 100 : rawLimit, 1), 1000);\n const sinceParam = url.searchParams.get('since');\n const agentE = server.getAgentE();\n\n let decisions;\n if (sinceParam) {\n const since = parseInt(sinceParam, 10);\n if (Number.isNaN(since)) {\n respond(400, { error: 'Invalid \"since\" parameter — must be a number' });\n return;\n }\n decisions = agentE.getDecisions({ since });\n } else {\n decisions = agentE.log.latest(limit);\n }\n\n respond(200, { decisions });\n return;\n }\n\n // POST /config — batch lock/unlock/constrain/mode\n if (path === '/config' && method === 'POST') {\n if (!checkAuth(req, apiKey)) {\n respond(401, { error: 'Unauthorized' });\n return;\n }\n const body = await readBody(req);\n let parsed: unknown;\n try {\n parsed = sanitizeJson(JSON.parse(body));\n } catch {\n respond(400, { error: 'Invalid JSON' });\n return;\n }\n\n const config = parsed as Record<string, unknown>;\n\n // Lock parameters\n if (Array.isArray(config['lock'])) {\n for (const param of config['lock']) {\n if (typeof param === 'string') server.lock(param);\n }\n }\n\n // Unlock parameters\n if (Array.isArray(config['unlock'])) {\n for (const param of config['unlock']) {\n if (typeof param === 'string') server.unlock(param);\n }\n }\n\n // Constrain parameters — validate ALL before applying any\n if (Array.isArray(config['constrain'])) {\n const validated: { param: string; min: number; max: number }[] = [];\n for (const c of config['constrain'] as unknown[]) {\n if (\n c && typeof c === 'object' &&\n typeof (c as Record<string, unknown>)['param'] === 'string' &&\n typeof (c as Record<string, unknown>)['min'] === 'number' &&\n typeof (c as Record<string, unknown>)['max'] === 'number'\n ) {\n const constraint = c as { param: string; min: number; max: number };\n if (!Number.isFinite(constraint.min) || !Number.isFinite(constraint.max)) {\n respond(400, { error: 'Constraint bounds must be finite numbers' });\n return;\n }\n if (constraint.min > constraint.max) {\n respond(400, { error: 'Constraint min cannot exceed max' });\n return;\n }\n validated.push(constraint);\n }\n }\n for (const constraint of validated) {\n server.constrain(constraint.param, { min: constraint.min, max: constraint.max });\n }\n }\n\n // Mode switch\n if (config['mode'] === 'autonomous' || config['mode'] === 'advisor') {\n server.setMode(config['mode']);\n }\n\n respond(200, { ok: true });\n return;\n }\n\n // GET /principles — list all principles\n if (path === '/principles' && method === 'GET') {\n const principles = server.getAgentE().getPrinciples();\n respond(200, {\n count: principles.length,\n principles: principles.map(p => ({\n id: p.id,\n name: p.name,\n category: p.category,\n description: p.description,\n })),\n });\n return;\n }\n\n // POST /diagnose — standalone Observer+Diagnoser (no side effects)\n if (path === '/diagnose' && method === 'POST') {\n if (!checkAuth(req, apiKey)) {\n respond(401, { error: 'Unauthorized' });\n return;\n }\n const body = await readBody(req);\n let parsed: unknown;\n try {\n parsed = sanitizeJson(JSON.parse(body));\n } catch {\n respond(400, { error: 'Invalid JSON' });\n return;\n }\n\n const payload = parsed as Record<string, unknown>;\n const state = payload['state'] ?? parsed;\n\n if (server.validateState) {\n const validation = validateEconomyState(state);\n if (!validation.valid) {\n respond(400, { error: 'invalid_state', validationErrors: validation.errors });\n return;\n }\n }\n\n const result = server.diagnoseOnly(state as import('@agent-e/core').EconomyState);\n\n respond(200, {\n health: result.health,\n diagnoses: result.diagnoses.map(d => ({\n principleId: d.principle.id,\n principleName: d.principle.name,\n severity: d.violation.severity,\n evidence: d.violation.evidence,\n suggestedAction: d.violation.suggestedAction,\n })),\n });\n return;\n }\n\n // GET / — Dashboard HTML\n if (path === '/' && method === 'GET' && server.serveDashboard) {\n setCorsHeaders(res, cors, reqOrigin);\n res.setHeader('Content-Security-Policy', \"default-src 'self'; script-src 'unsafe-inline' https://cdn.jsdelivr.net; style-src 'unsafe-inline' https://fonts.googleapis.com; font-src https://fonts.gstatic.com; connect-src 'self' ws: wss:; img-src 'self' data:\");\n res.setHeader('Cache-Control', 'public, max-age=60');\n res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });\n res.end(getDashboardHtml());\n return;\n }\n\n // GET /metrics — Latest metrics + history for dashboard charts\n if (path === '/metrics' && method === 'GET') {\n const agentE = server.getAgentE();\n const latest = agentE.store.latest();\n const history = agentE.store.recentHistory(100);\n respond(200, { latest, history });\n return;\n }\n\n // GET /metrics/personas — Persona distribution\n if (path === '/metrics/personas' && method === 'GET') {\n const agentE = server.getAgentE();\n const latest = agentE.store.latest();\n const dist = latest.personaDistribution || {};\n const total = Object.values(dist).reduce((s: number, v) => s + (v as number), 0);\n respond(200, { distribution: dist, total });\n return;\n }\n\n // POST /approve — Approve advisor recommendation\n if (path === '/approve' && method === 'POST') {\n if (!checkAuth(req, apiKey)) {\n respond(401, { error: 'Unauthorized' });\n return;\n }\n const body = await readBody(req);\n let parsed: unknown;\n try { parsed = sanitizeJson(JSON.parse(body)); } catch {\n respond(400, { error: 'Invalid JSON' });\n return;\n }\n const payload = parsed as Record<string, unknown>;\n const decisionId = payload['decisionId'] as string;\n if (!decisionId) {\n respond(400, { error: 'missing_decision_id' });\n return;\n }\n\n const agentE = server.getAgentE();\n if (agentE.getMode() !== 'advisor') {\n respond(400, { error: 'not_in_advisor_mode' });\n return;\n }\n\n const entry = agentE.log.getById(decisionId);\n if (!entry) {\n respond(404, { error: 'decision_not_found' });\n return;\n }\n if (entry.result !== 'skipped_override') {\n respond(409, { error: 'decision_not_pending', currentResult: entry.result });\n return;\n }\n\n await agentE.apply(entry.plan);\n agentE.log.updateResult(decisionId, 'applied');\n server.broadcast({ type: 'advisor_action', action: 'approved', decisionId });\n respond(200, {\n ok: true,\n parameter: entry.plan.parameter,\n value: entry.plan.targetValue,\n });\n return;\n }\n\n // POST /reject — Reject advisor recommendation\n if (path === '/reject' && method === 'POST') {\n if (!checkAuth(req, apiKey)) {\n respond(401, { error: 'Unauthorized' });\n return;\n }\n const body = await readBody(req);\n let parsed: unknown;\n try { parsed = sanitizeJson(JSON.parse(body)); } catch {\n respond(400, { error: 'Invalid JSON' });\n return;\n }\n const payload = parsed as Record<string, unknown>;\n const decisionId = payload['decisionId'] as string;\n const reason = (payload['reason'] as string) || undefined;\n if (!decisionId) {\n respond(400, { error: 'missing_decision_id' });\n return;\n }\n\n const agentE = server.getAgentE();\n if (agentE.getMode() !== 'advisor') {\n respond(400, { error: 'not_in_advisor_mode' });\n return;\n }\n\n const entry = agentE.log.getById(decisionId);\n if (!entry) {\n respond(404, { error: 'decision_not_found' });\n return;\n }\n if (entry.result !== 'skipped_override') {\n respond(409, { error: 'decision_not_pending', currentResult: entry.result });\n return;\n }\n\n agentE.log.updateResult(decisionId, 'rejected', reason);\n server.broadcast({ type: 'advisor_action', action: 'rejected', decisionId, reason });\n respond(200, { ok: true, decisionId });\n return;\n }\n\n // GET /pending — List pending advisor recommendations\n if (path === '/pending' && method === 'GET') {\n const agentE = server.getAgentE();\n const pending = agentE.log.query({ result: 'skipped_override' });\n respond(200, {\n mode: agentE.getMode(),\n pending,\n count: pending.length,\n });\n return;\n }\n\n // 404\n respond(404, { error: 'Not found' });\n } catch (err) {\n console.error('[AgentE Server] Unhandled route error:', err);\n respond(500, { error: 'Internal server error' });\n }\n };\n}\n","// Dashboard HTML — self-contained single-page dashboard served at GET /\n// Inline CSS + Chart.js CDN + WebSocket real-time updates\n\nexport function getDashboardHtml(): string {\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"UTF-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n<title>AgentE Dashboard</title>\n<link rel=\"preconnect\" href=\"https://fonts.googleapis.com\">\n<link rel=\"preconnect\" href=\"https://fonts.gstatic.com\" crossorigin>\n<link href=\"https://fonts.googleapis.com/css2?family=Inter:opsz,wght@14..32,400;14..32,500;14..32,600;14..32,700&family=JetBrains+Mono:wght@400;500&display=swap\" rel=\"stylesheet\">\n<script src=\"https://cdn.jsdelivr.net/npm/chart.js@4.5.1/dist/chart.umd.min.js\"></script>\n<style>\n *, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }\n\n :root {\n --bg-root: #09090b;\n --bg-panel: #18181b;\n --bg-terminal: #09090b;\n --border: #27272a;\n --text-primary: #ffffff;\n --text-secondary: #52525b;\n --text-tertiary: #a1a1aa;\n --text-value: #d4d4d8;\n --accent: #22c55e;\n --warning: #eab308;\n --danger: #ef4444;\n --info: #71717a;\n }\n\n html { scroll-behavior: smooth; }\n\n body {\n background: var(--bg-root);\n color: var(--text-primary);\n font-family: 'Inter', system-ui, sans-serif;\n -webkit-font-smoothing: antialiased;\n line-height: 1.5;\n min-height: 100dvh;\n overflow-y: auto;\n }\n\n /* -- Header -- */\n .header {\n position: sticky;\n top: 0;\n z-index: 50;\n background: var(--bg-panel);\n border-bottom: 1px solid var(--border);\n padding: 12px 24px;\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 16px;\n }\n\n .header-left {\n display: flex;\n align-items: center;\n gap: 10px;\n }\n\n .header-logo {\n font-family: 'JetBrains Mono', monospace;\n font-size: 15px;\n font-weight: 500;\n color: var(--text-primary);\n }\n\n .header-version {\n font-family: 'JetBrains Mono', monospace;\n font-size: 11px;\n color: var(--text-secondary);\n background: var(--bg-root);\n padding: 2px 8px;\n border-radius: 4px;\n }\n\n .header-right {\n display: flex;\n align-items: center;\n gap: 20px;\n }\n\n .kpi-pill {\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 12px;\n font-family: 'JetBrains Mono', monospace;\n }\n\n .kpi-pill .label {\n color: var(--text-secondary);\n text-transform: uppercase;\n letter-spacing: 0.05em;\n }\n\n .kpi-pill .value {\n color: var(--text-value);\n font-variant-numeric: tabular-nums;\n }\n\n .kpi-pill .value.health-good { color: var(--accent); }\n .kpi-pill .value.health-warn { color: var(--warning); }\n .kpi-pill .value.health-bad { color: var(--danger); }\n\n .live-dot {\n width: 6px;\n height: 6px;\n border-radius: 50%;\n background: var(--accent);\n flex-shrink: 0;\n animation: pulse 2s ease-in-out infinite;\n }\n\n .live-dot.disconnected {\n background: var(--danger);\n animation: none;\n }\n\n @keyframes pulse {\n 0%, 100% { opacity: 1; }\n 50% { opacity: 0.4; }\n }\n\n /* -- Advisor Banner -- */\n .advisor-banner {\n display: none;\n background: rgba(234,179,8,0.08);\n border-bottom: 1px solid rgba(234,179,8,0.2);\n padding: 8px 24px;\n font-family: 'JetBrains Mono', monospace;\n font-size: 11px;\n color: var(--warning);\n text-align: center;\n letter-spacing: 0.03em;\n }\n\n .advisor-mode .advisor-banner { display: block; }\n\n /* -- Advisor-specific: pending pill -- */\n .pending-pill {\n display: none;\n background: rgba(234,179,8,0.15);\n border-radius: 4px;\n padding: 2px 8px;\n cursor: pointer;\n }\n\n .pending-pill:hover { background: rgba(234,179,8,0.25); }\n\n .advisor-mode .pending-pill { display: flex; }\n\n /* -- Mode value color switching -- */\n .mode-value-auto { color: var(--accent); }\n .mode-value-advisor { color: var(--warning); }\n\n /* -- Layout -- */\n .dashboard {\n max-width: 1440px;\n margin: 0 auto;\n padding: 16px;\n display: flex;\n flex-direction: column;\n gap: 12px;\n }\n\n /* -- Panels -- */\n .panel {\n background: var(--bg-panel);\n border: 1px solid var(--border);\n border-radius: 8px;\n padding: 16px 20px;\n min-width: 0;\n display: flex;\n flex-direction: column;\n }\n\n .panel-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-bottom: 12px;\n flex-shrink: 0;\n }\n\n .panel-title {\n font-size: 11px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.08em;\n color: var(--info);\n }\n\n .panel-meta {\n font-size: 11px;\n font-family: 'JetBrains Mono', monospace;\n color: var(--text-secondary);\n display: flex;\n align-items: center;\n gap: 8px;\n }\n\n .panel-meta .live-label {\n color: var(--accent);\n display: flex;\n align-items: center;\n gap: 4px;\n }\n\n .panel-body {\n flex: 1;\n min-height: 0;\n overflow: hidden;\n }\n\n /* -- Health Charts -- */\n .charts-panel .chart-row {\n display: grid;\n grid-template-columns: repeat(4, 1fr);\n gap: 16px;\n }\n\n .mini-chart { position: relative; }\n .mini-chart-label {\n font-size: 10px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.06em;\n color: var(--text-secondary);\n margin-bottom: 4px;\n }\n .mini-chart canvas { width: 100% !important; height: 64px !important; }\n\n @media (max-width: 900px) {\n .charts-panel .chart-row { grid-template-columns: 1fr 1fr; }\n }\n\n @media (max-width: 500px) {\n .charts-panel .chart-row { grid-template-columns: 1fr; }\n }\n\n /* -- Terminal Feed -- */\n .terminal-panel {\n border-left: 2px solid var(--accent);\n height: 380px;\n overflow: hidden;\n }\n\n .advisor-mode .terminal-panel {\n border-left-color: var(--warning);\n }\n\n .terminal {\n background: var(--bg-terminal);\n border-radius: 6px;\n padding: 12px 16px;\n height: 100%;\n overflow-y: auto;\n font-family: 'JetBrains Mono', monospace;\n font-size: 12.5px;\n line-height: 1.7;\n display: flex;\n flex-direction: column;\n }\n\n .terminal-inner {\n margin-top: auto;\n }\n\n .terminal::-webkit-scrollbar { width: 4px; }\n .terminal::-webkit-scrollbar-track { background: transparent; }\n .terminal::-webkit-scrollbar-thumb { background: var(--border); border-radius: 2px; }\n\n .term-line {\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n opacity: 0;\n transform: translateY(4px);\n animation: termIn 0.3s ease-out forwards;\n }\n\n @keyframes termIn {\n to { opacity: 1; transform: translateY(0); }\n }\n\n .t-tick { color: var(--text-secondary); }\n .t-ok { color: var(--accent); }\n .t-check { color: var(--accent); }\n .t-skip { color: var(--warning); }\n .t-fail { color: var(--danger); }\n .t-principle { color: var(--text-primary); font-weight: 500; }\n .t-param { color: var(--text-tertiary); }\n .t-old { color: var(--text-value); font-variant-numeric: tabular-nums; }\n .t-arrow { color: var(--info); }\n .t-new { color: var(--accent); font-variant-numeric: tabular-nums; }\n .t-meta { color: var(--text-secondary); }\n .t-violation-id { color: var(--warning); }\n .t-violation-desc { color: var(--text-tertiary); }\n .t-status-label { color: var(--text-tertiary); }\n .t-status-value { color: var(--accent); font-variant-numeric: tabular-nums; }\n .t-dim { color: var(--text-secondary); }\n .t-white { color: var(--text-primary); }\n .t-separator { color: var(--info); }\n .t-pending-icon { color: var(--warning); }\n .t-pending-val { color: var(--warning); font-variant-numeric: tabular-nums; }\n\n /* -- Advisor Inline Buttons -- */\n .advisor-btn {\n display: none;\n font-family: 'JetBrains Mono', monospace;\n font-size: 10px;\n border-radius: 3px;\n padding: 2px 8px;\n cursor: pointer;\n border: 1px solid;\n margin-left: 6px;\n vertical-align: middle;\n line-height: 1.4;\n transition: background 0.15s;\n }\n\n .advisor-mode .advisor-btn { display: inline-flex; align-items: center; }\n\n .advisor-btn.approve-btn {\n background: rgba(34,197,94,0.15);\n color: var(--accent);\n border-color: rgba(34,197,94,0.3);\n }\n .advisor-btn.approve-btn:hover { background: rgba(34,197,94,0.25); }\n\n .advisor-btn.reject-btn {\n background: rgba(239,68,68,0.1);\n color: var(--danger);\n border-color: rgba(239,68,68,0.2);\n }\n .advisor-btn.reject-btn:hover { background: rgba(239,68,68,0.2); }\n\n /* Approved/Rejected flash labels */\n .action-flash {\n font-family: 'JetBrains Mono', monospace;\n font-size: 10px;\n margin-left: 8px;\n animation: flashIn 0.3s ease-out;\n }\n\n .action-flash.approved { color: var(--accent); }\n .action-flash.rejected { color: var(--info); }\n\n @keyframes flashIn {\n from { opacity: 0; transform: translateX(-4px); }\n to { opacity: 1; transform: translateX(0); }\n }\n\n /* Dimmed line after rejection */\n .term-line.rejected-line { opacity: 0.5; }\n\n /* -- Alerts -- */\n .alert-card {\n background: var(--bg-root);\n border-radius: 6px;\n padding: 10px 14px;\n margin-bottom: 8px;\n border-left: 3px solid transparent;\n overflow: hidden;\n }\n\n .alert-card.sev-high { border-left-color: var(--danger); }\n .alert-card.sev-med { border-left-color: var(--warning); }\n .alert-card.sev-low { border-left-color: var(--accent); }\n\n .alert-top {\n display: flex;\n align-items: center;\n gap: 8px;\n margin-bottom: 4px;\n }\n\n .sev-badge {\n width: 20px;\n height: 20px;\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 10px;\n font-weight: 700;\n font-family: 'JetBrains Mono', monospace;\n color: var(--bg-root);\n flex-shrink: 0;\n }\n\n .sev-badge.high { background: var(--danger); }\n .sev-badge.med { background: var(--warning); }\n .sev-badge.low { background: var(--accent); }\n\n .alert-principle-id { color: var(--warning); font-family: 'JetBrains Mono', monospace; font-size: 11px; }\n .alert-principle-name { color: var(--text-primary); font-size: 12px; font-weight: 500; }\n .alert-evidence { color: var(--text-tertiary); font-size: 10px; margin-top: 2px; }\n .alert-suggestion { color: var(--accent); font-size: 10px; margin-top: 2px; }\n\n .alert-approve-btn {\n display: none;\n font-family: 'JetBrains Mono', monospace;\n font-size: 10px;\n background: rgba(34,197,94,0.15);\n color: var(--accent);\n border: 1px solid rgba(34,197,94,0.3);\n border-radius: 3px;\n padding: 3px 10px;\n cursor: pointer;\n margin-top: 8px;\n transition: background 0.15s;\n }\n\n .alert-approve-btn:hover { background: rgba(34,197,94,0.25); }\n\n .alert-reject-btn {\n display: none;\n font-family: 'JetBrains Mono', monospace;\n font-size: 10px;\n background: rgba(239,68,68,0.1);\n color: var(--danger);\n border: 1px solid rgba(239,68,68,0.2);\n border-radius: 3px;\n padding: 3px 10px;\n cursor: pointer;\n margin-top: 8px;\n margin-left: 6px;\n transition: background 0.15s;\n }\n\n .alert-reject-btn:hover { background: rgba(239,68,68,0.2); }\n\n .advisor-mode .alert-approve-btn { display: inline-block; }\n .advisor-mode .alert-reject-btn { display: inline-block; }\n\n /* Alert resolved state */\n .alert-card.resolved {\n opacity: 0;\n max-height: 0;\n padding: 0 14px;\n margin-bottom: 0;\n overflow: hidden;\n transition: opacity 0.4s ease-out, max-height 0.5s ease-out 0.1s, padding 0.5s ease-out 0.1s, margin 0.5s ease-out 0.1s;\n }\n\n .alerts-scroll {\n overflow-y: auto;\n max-height: 300px;\n }\n\n .alerts-scroll::-webkit-scrollbar { width: 4px; }\n .alerts-scroll::-webkit-scrollbar-track { background: transparent; }\n .alerts-scroll::-webkit-scrollbar-thumb { background: var(--border); border-radius: 2px; }\n\n /* -- Persona Bars -- */\n .persona-row {\n display: flex;\n align-items: center;\n gap: 8px;\n margin-bottom: 5px;\n font-size: 11px;\n font-family: 'JetBrains Mono', monospace;\n }\n\n .persona-label {\n width: 100px;\n text-align: right;\n color: var(--text-tertiary);\n flex-shrink: 0;\n font-variant-numeric: tabular-nums;\n }\n\n .persona-bar-track {\n flex: 1;\n height: 12px;\n background: var(--bg-root);\n border-radius: 3px;\n overflow: hidden;\n }\n\n .persona-bar-fill {\n height: 100%;\n border-radius: 3px;\n transition: width 0.6s ease-out;\n }\n\n .persona-pct {\n width: 32px;\n text-align: right;\n color: var(--text-secondary);\n font-variant-numeric: tabular-nums;\n }\n\n /* -- Parameter Registry -- */\n .param-row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 6px 0;\n border-bottom: 1px solid rgba(39,39,42,0.4);\n font-family: 'JetBrains Mono', monospace;\n font-size: 11px;\n }\n\n .param-row:last-child { border-bottom: none; }\n\n .param-name {\n color: var(--text-tertiary);\n }\n\n .param-val {\n color: var(--accent);\n font-variant-numeric: tabular-nums;\n }\n\n .param-changed {\n color: var(--text-secondary);\n font-size: 9px;\n margin-left: 6px;\n }\n\n /* Ghost preview for pending recommendations */\n .param-ghost {\n display: none;\n color: var(--warning);\n font-size: 9px;\n margin-left: 4px;\n font-variant-numeric: tabular-nums;\n }\n\n .advisor-mode .param-ghost { display: inline; }\n\n .param-pending-label {\n display: none;\n color: var(--warning);\n font-size: 9px;\n margin-left: 6px;\n }\n\n .advisor-mode .param-pending-label { display: inline; }\n\n .params-scroll {\n overflow-y: auto;\n max-height: 300px;\n }\n\n .params-scroll::-webkit-scrollbar { width: 4px; }\n .params-scroll::-webkit-scrollbar-track { background: transparent; }\n .params-scroll::-webkit-scrollbar-thumb { background: var(--border); border-radius: 2px; }\n\n /* -- Violations Table -- */\n .violations-table {\n width: 100%;\n border-collapse: collapse;\n font-size: 11px;\n font-family: 'JetBrains Mono', monospace;\n }\n\n .violations-table thead th {\n text-align: left;\n padding: 7px 8px;\n border-bottom: 1px solid var(--border);\n color: var(--text-secondary);\n font-weight: 600;\n font-size: 10px;\n text-transform: uppercase;\n letter-spacing: 0.06em;\n cursor: pointer;\n user-select: none;\n white-space: nowrap;\n position: sticky;\n top: 0;\n background: var(--bg-panel);\n }\n\n .violations-table thead th:hover { color: var(--text-tertiary); }\n\n .violations-table tbody td {\n padding: 6px 8px;\n border-bottom: 1px solid rgba(39,39,42,0.4);\n color: var(--text-value);\n font-variant-numeric: tabular-nums;\n }\n\n .violations-table tbody tr:hover td { background: rgba(39,39,42,0.3); }\n\n .table-scroll {\n overflow-y: auto;\n max-height: 240px;\n }\n\n .table-scroll::-webkit-scrollbar { width: 4px; }\n .table-scroll::-webkit-scrollbar-track { background: transparent; }\n .table-scroll::-webkit-scrollbar-thumb { background: var(--border); border-radius: 2px; }\n\n .badge-applied { color: var(--accent); font-size: 10px; }\n .badge-skipped { color: var(--text-secondary); font-size: 10px; }\n .badge-rejected { color: var(--danger); font-size: 10px; opacity: 0.6; }\n\n /* Pending badge in violations table (advisor mode) */\n .badge-pending {\n display: none;\n color: var(--warning);\n font-size: 10px;\n cursor: pointer;\n position: relative;\n }\n\n .advisor-mode .badge-pending { display: inline-flex; align-items: center; gap: 4px; }\n\n .badge-pending:hover { text-decoration: underline; }\n\n /* Pending dropdown in violations table */\n .pending-dropdown {\n display: none;\n position: absolute;\n bottom: 100%;\n left: 0;\n background: var(--bg-panel);\n border: 1px solid var(--border);\n border-radius: 4px;\n padding: 4px 0;\n z-index: 20;\n min-width: 120px;\n box-shadow: 0 -4px 12px rgba(0,0,0,0.4);\n }\n\n .pending-dropdown.open { display: block; }\n\n .pending-dropdown-item {\n padding: 5px 12px;\n font-family: 'JetBrains Mono', monospace;\n font-size: 10px;\n cursor: pointer;\n display: flex;\n align-items: center;\n gap: 6px;\n white-space: nowrap;\n }\n\n .pending-dropdown-item:hover { background: rgba(39,39,42,0.5); }\n .pending-dropdown-item.approve-item { color: var(--accent); }\n .pending-dropdown-item.reject-item { color: var(--danger); }\n\n /* -- Bottom split row -- */\n .split-row {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 12px;\n }\n\n /* -- Empty state -- */\n .empty-state {\n color: var(--text-secondary);\n font-family: 'JetBrains Mono', monospace;\n font-size: 11px;\n text-align: center;\n padding: 20px;\n }\n\n /* -- Responsive -- */\n @media (max-width: 768px) {\n .split-row { grid-template-columns: 1fr; }\n .header { flex-direction: column; align-items: flex-start; gap: 8px; }\n .header-right { flex-wrap: wrap; gap: 12px; }\n .dashboard { padding: 8px; gap: 8px; }\n }\n\n /* -- Reduced motion -- */\n @media (prefers-reduced-motion: reduce) {\n .term-line { animation: none; opacity: 1; transform: none; }\n .live-dot { animation: none; }\n .persona-bar-fill { transition: none; }\n .alert-card.resolved { transition: none; }\n }\n</style>\n</head>\n<body>\n\n<!-- Header -->\n<header class=\"header\" id=\"header\">\n <div class=\"header-left\">\n <span class=\"header-logo\">AgentE</span>\n <span class=\"header-version\">v1.8.0</span>\n </div>\n <div class=\"header-right\">\n <div class=\"kpi-pill\">\n <span class=\"label\">Health</span>\n <span class=\"value health-good\" id=\"h-health\">--</span>\n </div>\n <div class=\"kpi-pill\">\n <span class=\"label\">Mode</span>\n <span class=\"value mode-value-auto\" id=\"h-mode\">--</span>\n </div>\n <div class=\"kpi-pill\">\n <span class=\"label\">Tick</span>\n <span class=\"value\" id=\"h-tick\">0</span>\n </div>\n <div class=\"kpi-pill pending-pill\" id=\"pending-pill\">\n <span class=\"label\" style=\"color: var(--warning);\">Pending</span>\n <span class=\"value\" style=\"color: var(--warning);\" id=\"h-pending\">0</span>\n </div>\n <div class=\"kpi-pill\">\n <span class=\"label\">Uptime</span>\n <span class=\"value\" id=\"h-uptime\">0s</span>\n </div>\n <div class=\"kpi-pill\">\n <div class=\"live-dot\" id=\"live-dot\" title=\"WebSocket connected\"></div>\n <span style=\"color: var(--accent); font-size: 11px;\">LIVE</span>\n </div>\n </div>\n</header>\n\n<!-- Advisor Banner -->\n<div class=\"advisor-banner\" id=\"advisor-banner\">\n ADVISOR MODE \\\\u2014 AgentE is waiting for your approval before applying changes\n</div>\n\n<!-- Dashboard -->\n<main class=\"dashboard\" id=\"dashboard-root\">\n\n <!-- Health & Metrics -->\n <div class=\"panel charts-panel\">\n <div class=\"panel-header\">\n <span class=\"panel-title\">Health & Metrics</span>\n </div>\n <div class=\"chart-row\">\n <div class=\"mini-chart\">\n <div class=\"mini-chart-label\">Health Score</div>\n <canvas id=\"chart-health\"></canvas>\n </div>\n <div class=\"mini-chart\">\n <div class=\"mini-chart-label\">Gini Coefficient</div>\n <canvas id=\"chart-gini\"></canvas>\n </div>\n <div class=\"mini-chart\">\n <div class=\"mini-chart-label\">Net Flow</div>\n <canvas id=\"chart-flow\"></canvas>\n </div>\n <div class=\"mini-chart\">\n <div class=\"mini-chart-label\">Avg Satisfaction</div>\n <canvas id=\"chart-satisfaction\"></canvas>\n </div>\n </div>\n </div>\n\n <!-- Decision Feed -->\n <div class=\"panel terminal-panel\">\n <div class=\"panel-header\">\n <span class=\"panel-title\">Decision Feed</span>\n <div class=\"panel-meta\">\n <span id=\"decision-count\">0 decisions</span>\n <span class=\"live-label\"><div class=\"live-dot\" style=\"width:5px;height:5px;\"></div> LIVE</span>\n </div>\n </div>\n <div class=\"panel-body\">\n <div class=\"terminal\" id=\"terminal\">\n <div id=\"terminal-inner\"></div>\n </div>\n </div>\n </div>\n\n <!-- Active Alerts -->\n <div class=\"panel alerts-panel\">\n <div class=\"panel-header\">\n <span class=\"panel-title\">Active Alerts</span>\n <span class=\"panel-meta\" id=\"alerts-count\">All clear</span>\n </div>\n <div class=\"alerts-scroll\" id=\"alerts\"></div>\n </div>\n\n <!-- Violation History -->\n <div class=\"panel\">\n <div class=\"panel-header\">\n <span class=\"panel-title\">Violation History</span>\n <span class=\"panel-meta\">Last 100 decisions</span>\n </div>\n <div class=\"table-scroll\">\n <table class=\"violations-table\" id=\"violations-table\">\n <thead>\n <tr>\n <th data-sort=\"tick\">Tick</th>\n <th data-sort=\"principle\">Principle</th>\n <th data-sort=\"severity\">Sev</th>\n <th data-sort=\"parameter\">Parameter</th>\n <th data-sort=\"result\">Action</th>\n <th>Change</th>\n </tr>\n </thead>\n <tbody id=\"violations-body\"></tbody>\n </table>\n </div>\n </div>\n\n <!-- Persona Distribution + Parameters -->\n <div class=\"split-row\">\n <div class=\"panel persona-panel\">\n <div class=\"panel-header\">\n <span class=\"panel-title\">Persona Distribution</span>\n <span class=\"panel-meta\" id=\"persona-count\"></span>\n </div>\n <div id=\"persona-bars\">\n <div class=\"empty-state\">No persona data yet</div>\n </div>\n </div>\n\n <div class=\"panel params-panel\">\n <div class=\"panel-header\">\n <span class=\"panel-title\">Parameters</span>\n <span class=\"panel-meta\" id=\"params-count\"></span>\n </div>\n <div class=\"params-scroll\" id=\"params-list\">\n <div class=\"empty-state\">No parameters registered</div>\n </div>\n </div>\n </div>\n\n</main>\n\n<script>\n(function() {\n 'use strict';\n\n // -- State --\n var ws = null;\n var reconnectDelay = 1000;\n var MAX_RECONNECT = 30000;\n var isAdvisor = false;\n var pendingDecisions = [];\n var MAX_TERMINAL_LINES = 80;\n var MAX_VIOLATIONS = 100;\n var violationSortKey = 'tick';\n var violationSortAsc = false;\n var violations = [];\n var decisionCount = 0;\n\n // Chart instances\n var chartHealth, chartGini, chartFlow, chartSatisfaction;\n\n // Param state for registry display\n var paramRegistry = [];\n var paramValues = {};\n var paramLastTick = {};\n var paramPending = {};\n var currentTick = 0;\n\n // -- DOM refs --\n var $hHealth = document.getElementById('h-health');\n var $hMode = document.getElementById('h-mode');\n var $hTick = document.getElementById('h-tick');\n var $hUptime = document.getElementById('h-uptime');\n var $hPending = document.getElementById('h-pending');\n var $liveDot = document.getElementById('live-dot');\n var $terminal = document.getElementById('terminal');\n var $terminalInner = document.getElementById('terminal-inner');\n var $alerts = document.getElementById('alerts');\n var $alertsCount = document.getElementById('alerts-count');\n var $violationsBody = document.getElementById('violations-body');\n var $personaBars = document.getElementById('persona-bars');\n var $paramsList = document.getElementById('params-list');\n var $paramsCount = document.getElementById('params-count');\n var $personaCount = document.getElementById('persona-count');\n var $decisionCount = document.getElementById('decision-count');\n var $dashboardRoot = document.getElementById('dashboard-root');\n\n // -- Helpers --\n function esc(s) { return String(s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/\"/g,'&quot;').replace(/'/g,'&#39;').replace(/\\\\\\\\/g,'&#92;'); }\n function pad(n, w) { return String(n).padStart(w || 4, ' '); }\n function fmt(n) { return typeof n === 'number' ? n.toFixed(3) : '\\\\u2014'; }\n function pct(n) { return typeof n === 'number' ? (n * 100).toFixed(0) + '%' : '\\\\u2014'; }\n\n function formatUptime(ms) {\n var s = Math.floor(ms / 1000);\n if (s < 60) return s + 's';\n if (s < 3600) return Math.floor(s / 60) + 'm ' + (s % 60) + 's';\n var h = Math.floor(s / 3600);\n return h + 'h ' + Math.floor((s % 3600) / 60) + 'm';\n }\n\n function healthClass(h) {\n if (h >= 70) return 'health-good';\n if (h >= 40) return 'health-warn';\n return 'health-bad';\n }\n\n function sevClass(s) {\n if (s >= 7) return 'high';\n if (s >= 4) return 'med';\n return 'low';\n }\n\n function sevCardClass(s) {\n if (s >= 7) return 'sev-high';\n if (s >= 4) return 'sev-med';\n return 'sev-low';\n }\n\n function personaColor(name) {\n var n = (name || '').toLowerCase();\n if (n === 'atrisk' || n === 'at_risk' || n === 'dormant') return 'var(--danger)';\n if (n === 'spender' || n === 'newentrant' || n === 'new_entrant' || n === 'passive') return 'var(--warning)';\n return 'var(--accent)';\n }\n\n // -- Chart setup --\n var chartOpts = {\n responsive: true,\n maintainAspectRatio: false,\n animation: false,\n plugins: {\n legend: { display: false },\n tooltip: {\n backgroundColor: '#18181b',\n titleColor: '#a1a1aa',\n bodyColor: '#d4d4d8',\n titleFont: { family: 'JetBrains Mono', size: 10 },\n bodyFont: { family: 'JetBrains Mono', size: 11 },\n borderColor: '#27272a',\n borderWidth: 1,\n padding: 8,\n }\n },\n scales: {\n x: { display: false },\n y: {\n grid: { color: 'rgba(39,39,42,0.5)', drawBorder: false },\n ticks: {\n color: '#52525b',\n font: { family: 'JetBrains Mono', size: 9 },\n maxTicksLimit: 3,\n },\n border: { display: false },\n }\n },\n elements: {\n point: { radius: 0, hoverRadius: 3, backgroundColor: '#22c55e' },\n line: { borderWidth: 1.5, tension: 0.3 },\n }\n };\n\n function makeChart(id, color, minY, maxY) {\n var ctx = document.getElementById(id).getContext('2d');\n var opts = JSON.parse(JSON.stringify(chartOpts));\n if (minY !== undefined) opts.scales.y.min = minY;\n if (maxY !== undefined) opts.scales.y.max = maxY;\n return new Chart(ctx, {\n type: 'line',\n data: {\n labels: [],\n datasets: [{\n data: [],\n borderColor: color,\n backgroundColor: color + '18',\n fill: true,\n }]\n },\n options: opts,\n });\n }\n\n function initCharts() {\n chartHealth = makeChart('chart-health', '#22c55e', 0, 100);\n chartGini = makeChart('chart-gini', '#eab308', 0, 1);\n chartFlow = makeChart('chart-flow', '#3b82f6');\n chartSatisfaction = makeChart('chart-satisfaction', '#22c55e', 0, 100);\n }\n\n function updateChart(chart, labels, data) {\n chart.data.labels = labels;\n chart.data.datasets[0].data = data;\n chart.update('none');\n }\n\n // -- Terminal --\n function addTerminalLine(html) {\n var el = document.createElement('div');\n el.className = 'term-line';\n el.innerHTML = html;\n $terminalInner.appendChild(el);\n while ($terminalInner.children.length > MAX_TERMINAL_LINES) {\n $terminalInner.removeChild($terminalInner.firstChild);\n }\n $terminal.scrollTop = $terminal.scrollHeight;\n }\n\n function decisionToTerminal(d) {\n var resultIcon = d.result === 'applied'\n ? '<span class=\"t-check\">\\\\u2705 </span>'\n : d.result === 'rejected'\n ? '<span class=\"t-fail\">\\\\u274c </span>'\n : d.result === 'skipped_override'\n ? '<span class=\"t-pending-icon\">\\\\u23f3 </span>'\n : '<span class=\"t-skip\">\\\\u23f8 </span>';\n\n var principle = d.diagnosis?.principle || {};\n var plan = d.plan || {};\n var severity = d.diagnosis?.violation?.severity ?? '?';\n var confidence = d.diagnosis?.violation?.confidence;\n var confStr = confidence != null ? (confidence * 100).toFixed(0) + '%' : '?';\n\n var advisorBtns = '';\n if (isAdvisor && d.result === 'skipped_override') {\n advisorBtns = '<span class=\"advisor-btn-group\" data-id=\"' + esc(d.id) + '\">'\n + '<button class=\"advisor-btn approve-btn\" data-action=\"approve\" data-id=\"' + esc(d.id) + '\">&#10003; Approve</button>'\n + '<button class=\"advisor-btn reject-btn\" data-action=\"reject\" data-id=\"' + esc(d.id) + '\">&#10005; Reject</button>'\n + '</span>';\n }\n\n return '<span class=\"t-tick\">[Tick ' + pad(d.tick) + ']</span> '\n + resultIcon\n + '<span class=\"t-principle\">' + esc(principle.name || '') + ':</span> '\n + '<span class=\"t-param\">' + esc(plan.parameter || '\\\\u2014') + ' </span>'\n + '<span class=\"t-old\">' + fmt(plan.currentValue) + '</span>'\n + '<span class=\"t-arrow\"> \\\\u2192 </span>'\n + (d.result === 'skipped_override'\n ? '<span class=\"t-pending-val\">' + fmt(plan.targetValue) + '</span>'\n : '<span class=\"t-new\">' + fmt(plan.targetValue) + '</span>')\n + '<span class=\"t-meta\"> sev ' + severity + ', conf ' + confStr + '</span>'\n + advisorBtns;\n }\n\n // -- Alerts --\n function renderAlerts(alerts) {\n if (!alerts || alerts.length === 0) {\n $alerts.innerHTML = '<div class=\"empty-state\">No active violations. Economy is healthy.</div>';\n $alertsCount.textContent = 'All clear';\n return;\n }\n var sorted = alerts.slice().sort(function(a, b) { return (b.severity || 0) - (a.severity || 0); });\n $alertsCount.textContent = sorted.length + ' violation' + (sorted.length !== 1 ? 's' : '');\n\n $alerts.innerHTML = sorted.map(function(a) {\n var sev = a.severity || a.violation?.severity || 0;\n var sc = sevClass(sev);\n var cardCls = sevCardClass(sev);\n var name = a.principleName || a.principle?.name || '?';\n var pid = a.principleId || a.principle?.id || '?';\n var reason = a.reasoning || a.violation?.suggestedAction?.reasoning || '';\n var suggestion = a.suggestion || '';\n\n var hasPending = isAdvisor && pendingDecisions.some(function(pd) {\n return pd.principleId === pid || (pd.diagnosis?.principle?.id === pid);\n });\n\n var btns = '';\n if (hasPending) {\n btns = '<button class=\"alert-approve-btn\" data-action=\"approve-alert\" data-principle=\"' + esc(pid) + '\">&#10003; Approve Fix</button>'\n + '<button class=\"alert-reject-btn\" data-action=\"reject-alert\" data-principle=\"' + esc(pid) + '\">&#10007; Reject</button>';\n }\n\n return '<div class=\"alert-card ' + cardCls + '\" data-principle-id=\"' + esc(pid) + '\">'\n + '<div class=\"alert-top\">'\n + '<span class=\"sev-badge ' + sc + '\">' + sev + '</span>'\n + '<span class=\"alert-principle-name\">[' + esc(pid) + '] ' + esc(name) + '</span>'\n + '</div>'\n + (reason ? '<div class=\"alert-evidence\">' + esc(reason) + '</div>' : '')\n + (suggestion ? '<div class=\"alert-suggestion\">Suggested: ' + esc(suggestion) + '</div>' : '')\n + btns\n + '</div>';\n }).join('');\n }\n\n // -- Violations table --\n function addViolation(d) {\n var plan = d.plan || {};\n violations.push({\n tick: d.tick,\n principle: (d.diagnosis?.principle?.id || '?') + ' ' + (d.diagnosis?.principle?.name || ''),\n severity: d.diagnosis?.violation?.severity || 0,\n parameter: plan.parameter || '\\\\u2014',\n result: d.result,\n currentValue: plan.currentValue,\n targetValue: plan.targetValue,\n decisionId: d.id,\n });\n if (violations.length > MAX_VIOLATIONS) violations.shift();\n decisionCount = violations.length;\n $decisionCount.textContent = decisionCount + ' decisions';\n renderViolations();\n }\n\n function renderViolations() {\n var sorted = violations.slice().sort(function(a, b) {\n var va = a[violationSortKey], vb = b[violationSortKey];\n if (va < vb) return violationSortAsc ? -1 : 1;\n if (va > vb) return violationSortAsc ? 1 : -1;\n return 0;\n });\n $violationsBody.innerHTML = sorted.map(function(v) {\n var isPending = v.result === 'skipped_override';\n\n var actionHtml;\n if (isPending && isAdvisor) {\n actionHtml = '<span class=\"badge-pending\" data-action=\"toggle-pending\" data-id=\"' + esc(v.decisionId || '') + '\">'\n + 'Pending \\\\u25BE'\n + '<div class=\"pending-dropdown\">'\n + '<div class=\"pending-dropdown-item approve-item\" data-action=\"approve\" data-id=\"' + esc(v.decisionId || '') + '\">&#10003; Approve</div>'\n + '<div class=\"pending-dropdown-item reject-item\" data-action=\"reject\" data-id=\"' + esc(v.decisionId || '') + '\">&#10007; Reject</div>'\n + '</div>'\n + '</span>';\n } else if (v.result === 'applied') {\n actionHtml = '<span class=\"badge-applied\">Applied</span>';\n } else if (v.result === 'rejected') {\n actionHtml = '<span class=\"badge-rejected\">Rejected</span>';\n } else {\n actionHtml = '<span class=\"badge-skipped\">' + esc(v.result || 'Skipped') + '</span>';\n }\n\n var changeHtml = '';\n if (v.currentValue != null && v.targetValue != null) {\n var valColor = isPending && isAdvisor ? 'var(--warning)' : 'var(--accent)';\n changeHtml = '<span style=\"color:var(--text-value)\">' + fmt(v.currentValue) + '</span>'\n + '<span style=\"color:var(--info)\"> \\\\u2192 </span>'\n + '<span style=\"color:' + valColor + '\">' + fmt(v.targetValue) + '</span>';\n }\n\n var sevColor = v.severity >= 7 ? 'var(--danger)' : v.severity >= 4 ? 'var(--warning)' : 'var(--accent)';\n\n return '<tr>'\n + '<td style=\"color:var(--text-secondary)\">' + v.tick + '</td>'\n + '<td style=\"color:var(--text-value)\">' + esc(v.principle) + '</td>'\n + '<td style=\"color:' + sevColor + '\">' + v.severity + '</td>'\n + '<td style=\"color:var(--text-tertiary)\">' + esc(v.parameter) + '</td>'\n + '<td class=\"action-cell\">' + actionHtml + '</td>'\n + '<td>' + changeHtml + '</td>'\n + '</tr>';\n }).join('');\n }\n\n // Table sorting\n document.querySelectorAll('.violations-table th[data-sort]').forEach(function(th) {\n th.addEventListener('click', function() {\n var key = th.dataset.sort;\n if (violationSortKey === key) violationSortAsc = !violationSortAsc;\n else { violationSortKey = key; violationSortAsc = true; }\n renderViolations();\n });\n });\n\n // -- Personas --\n function renderPersonas(dist) {\n if (!dist || Object.keys(dist).length === 0) {\n $personaBars.innerHTML = '<div class=\"empty-state\">No persona data yet</div>';\n $personaCount.textContent = '';\n return;\n }\n var total = Object.values(dist).reduce(function(s, v) { return s + v; }, 0);\n $personaCount.textContent = total + ' agents';\n var entries = Object.entries(dist).sort(function(a, b) { return b[1] - a[1]; });\n $personaBars.innerHTML = entries.map(function(e) {\n var pctVal = total > 0 ? (e[1] / total * 100) : 0;\n var color = personaColor(e[0]);\n return '<div class=\"persona-row\">'\n + '<span class=\"persona-label\">' + esc(e[0]) + '</span>'\n + '<div class=\"persona-bar-track\"><div class=\"persona-bar-fill\" style=\"width:' + pctVal.toFixed(0) + '%;background:' + color + ';\"></div></div>'\n + '<span class=\"persona-pct\">' + pctVal.toFixed(0) + '%</span>'\n + '</div>';\n }).join('');\n }\n\n // -- Parameters --\n function renderParams(principles, registryValues) {\n if ((!principles || principles.length === 0) && Object.keys(paramValues).length === 0) {\n $paramsList.innerHTML = '<div class=\"empty-state\">No parameters registered</div>';\n $paramsCount.textContent = '';\n return;\n }\n\n // If we have actual parameter values from API, show those\n if (registryValues && Object.keys(registryValues).length > 0) {\n var entries = Object.entries(registryValues);\n $paramsCount.textContent = entries.length + ' tracked';\n $paramsList.innerHTML = entries.map(function(e) {\n var key = e[0];\n var val = e[1];\n var ticksAgo = currentTick - (paramLastTick[key] || 0);\n var agoText = ticksAgo <= 0 ? '' : ticksAgo <= 5 ? 'just now' : ticksAgo + ' ticks ago';\n var pending = paramPending[key];\n\n if (pending && isAdvisor) {\n return '<div class=\"param-row\">'\n + '<span class=\"param-name\">' + esc(key) + '</span>'\n + '<span>'\n + '<span class=\"param-val\">' + fmt(val) + '</span>'\n + '<span class=\"param-ghost\" style=\"display:inline;\"> \\\\u2192 ' + fmt(pending.proposedVal) + '?</span>'\n + '<span class=\"param-pending-label\" style=\"display:inline;\">pending</span>'\n + '</span>'\n + '</div>';\n }\n return '<div class=\"param-row\">'\n + '<span class=\"param-name\">' + esc(key) + '</span>'\n + '<span><span class=\"param-val\">' + fmt(val) + '</span>'\n + (agoText ? '<span class=\"param-changed\">' + agoText + '</span>' : '')\n + '</span>'\n + '</div>';\n }).join('');\n return;\n }\n\n // Fallback: show principle names (legacy behavior)\n if (principles && principles.length > 0) {\n $paramsCount.textContent = principles.length + ' registered';\n $paramsList.innerHTML = principles.slice(0, 30).map(function(p) {\n return '<div class=\"param-row\">'\n + '<span class=\"param-name\">[' + esc(p.id) + ']</span>'\n + '<span class=\"param-val\">' + esc(p.name) + '</span>'\n + '</div>';\n }).join('');\n }\n }\n\n // -- KPI update --\n function updateKPIs(data) {\n if (data.health != null) {\n $hHealth.textContent = data.health + '/100';\n $hHealth.className = 'value ' + healthClass(data.health);\n }\n if (data.mode != null) {\n $hMode.textContent = data.mode;\n isAdvisor = data.mode === 'advisor';\n $hMode.className = 'value ' + (isAdvisor ? 'mode-value-advisor' : 'mode-value-auto');\n document.body.classList.toggle('advisor-mode', isAdvisor);\n }\n if (data.tick != null) {\n $hTick.textContent = data.tick;\n currentTick = data.tick;\n }\n if (data.uptime != null) $hUptime.textContent = formatUptime(data.uptime);\n if (data.pendingCount != null || data.activePlans != null) {\n var count = data.pendingCount || data.activePlans || 0;\n $hPending.textContent = count;\n }\n }\n\n // -- Metrics history --\n function updateChartsFromHistory(history) {\n if (!history || history.length === 0) return;\n var ticks = history.map(function(h) { return h.tick; });\n updateChart(chartHealth, ticks, history.map(function(h) { return h.health; }));\n updateChart(chartGini, ticks, history.map(function(h) { return h.giniCoefficient; }));\n updateChart(chartFlow, ticks, history.map(function(h) { return h.netFlow; }));\n updateChart(chartSatisfaction, ticks, history.map(function(h) { return h.avgSatisfaction; }));\n }\n\n // -- API calls --\n function fetchJSON(path) {\n return fetch(path).then(function(r) { return r.json(); });\n }\n\n function postJSON(path, body) {\n return fetch(path, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(body),\n }).then(function(r) { return r.json(); });\n }\n\n function loadInitialData() {\n fetchJSON('/health').then(function(data) {\n updateKPIs(data);\n }).catch(function() {});\n\n fetchJSON('/decisions?limit=50').then(function(data) {\n if (data.decisions) {\n data.decisions.reverse().forEach(function(d) {\n addTerminalLine(decisionToTerminal(d));\n addViolation(d);\n });\n }\n }).catch(function() {});\n\n fetchJSON('/metrics').then(function(data) {\n if (data.history) updateChartsFromHistory(data.history);\n if (data.latest) {\n renderPersonas(data.latest.personaDistribution);\n }\n }).catch(function() {});\n\n fetchJSON('/principles').then(function(data) {\n if (data.principles) {\n paramRegistry = data.principles;\n renderParams(data.principles, paramValues);\n }\n }).catch(function() {});\n\n fetchJSON('/pending').then(function(data) {\n if (data.pending) {\n pendingDecisions = data.pending;\n $hPending.textContent = data.count || 0;\n }\n }).catch(function() {});\n }\n\n // -- Polling fallback --\n var pollInterval = null;\n\n function startPolling() {\n if (pollInterval) return;\n pollInterval = setInterval(function() {\n fetchJSON('/health').then(updateKPIs).catch(function() {});\n fetchJSON('/metrics').then(function(data) {\n if (data.history) updateChartsFromHistory(data.history);\n if (data.latest) renderPersonas(data.latest.personaDistribution);\n }).catch(function() {});\n }, 5000);\n }\n\n function stopPolling() {\n if (pollInterval) { clearInterval(pollInterval); pollInterval = null; }\n }\n\n // -- WebSocket --\n function connectWS() {\n var proto = location.protocol === 'https:' ? 'wss:' : 'ws:';\n ws = new WebSocket(proto + '//' + location.host);\n\n ws.onopen = function() {\n reconnectDelay = 1000;\n $liveDot.classList.remove('disconnected');\n $liveDot.title = 'WebSocket connected';\n stopPolling();\n ws.send(JSON.stringify({ type: 'health' }));\n };\n\n ws.onclose = function() {\n $liveDot.classList.add('disconnected');\n $liveDot.title = 'WebSocket disconnected \\\\u2014 reconnecting...';\n startPolling();\n setTimeout(connectWS, reconnectDelay);\n reconnectDelay = Math.min(reconnectDelay * 1.5, MAX_RECONNECT);\n };\n\n ws.onerror = function() { ws.close(); };\n\n ws.onmessage = function(ev) {\n var msg;\n try { msg = JSON.parse(ev.data); } catch(e) { return; }\n\n switch (msg.type) {\n case 'tick_result':\n updateKPIs({ health: msg.health, tick: msg.tick });\n if (msg.alerts) renderAlerts(msg.alerts);\n fetchJSON('/metrics').then(function(data) {\n if (data.history) updateChartsFromHistory(data.history);\n if (data.latest) renderPersonas(data.latest.personaDistribution);\n }).catch(function() {});\n break;\n\n case 'health_result':\n updateKPIs(msg);\n break;\n\n case 'advisor_action':\n if (msg.action === 'approved' || msg.action === 'rejected') {\n pendingDecisions = pendingDecisions.filter(function(d) {\n return d.id !== msg.decisionId;\n });\n $hPending.textContent = pendingDecisions.length;\n }\n break;\n }\n };\n }\n\n // -- Advisor actions (event delegation) --\n document.addEventListener('click', function(e) {\n var btn = e.target.closest('[data-action]');\n if (!btn) return;\n var action = btn.getAttribute('data-action');\n var id = btn.getAttribute('data-id');\n var principleId = btn.getAttribute('data-principle');\n\n // Toggle pending dropdown\n if (action === 'toggle-pending') {\n var dropdown = btn.querySelector('.pending-dropdown');\n if (!dropdown) return;\n document.querySelectorAll('.pending-dropdown.open').forEach(function(d) {\n if (d !== dropdown) d.classList.remove('open');\n });\n dropdown.classList.toggle('open');\n e.stopPropagation();\n return;\n }\n\n // Approve from alert card\n if (action === 'approve-alert' && principleId) {\n var pd = pendingDecisions.find(function(d) {\n return d.principleId === principleId || (d.diagnosis?.principle?.id === principleId);\n });\n if (pd) {\n postJSON('/approve', { decisionId: pd.id }).then(function(data) {\n if (data.ok) {\n addTerminalLine('<span class=\"t-tick\">[Advisor]</span> <span class=\"t-check\">\\\\u2705 Approved ' + esc(pd.id) + '</span>');\n }\n }).catch(function() {});\n }\n return;\n }\n\n // Reject from alert card\n if (action === 'reject-alert' && principleId) {\n var pd2 = pendingDecisions.find(function(d) {\n return d.principleId === principleId || (d.diagnosis?.principle?.id === principleId);\n });\n if (pd2) {\n var reason = prompt('Rejection reason (optional):');\n postJSON('/reject', { decisionId: pd2.id, reason: reason || undefined }).then(function(data) {\n if (data.ok) {\n addTerminalLine('<span class=\"t-tick\">[Advisor]</span> <span class=\"t-fail\">\\\\u274c Rejected ' + esc(pd2.id) + '</span>');\n }\n }).catch(function() {});\n }\n return;\n }\n\n if (!id) return;\n\n if (action === 'approve') {\n postJSON('/approve', { decisionId: id }).then(function(data) {\n if (data.ok) {\n addTerminalLine('<span class=\"t-tick\">[Advisor]</span> <span class=\"t-check\">\\\\u2705 Approved ' + esc(id) + '</span>');\n }\n }).catch(function() {});\n } else if (action === 'reject') {\n var reason2 = prompt('Rejection reason (optional):');\n postJSON('/reject', { decisionId: id, reason: reason2 || undefined }).then(function(data) {\n if (data.ok) {\n addTerminalLine('<span class=\"t-tick\">[Advisor]</span> <span class=\"t-fail\">\\\\u274c Rejected ' + esc(id) + '</span>');\n }\n }).catch(function() {});\n }\n });\n\n // Close dropdowns on outside click\n document.addEventListener('click', function(e) {\n if (!e.target.closest('.badge-pending')) {\n document.querySelectorAll('.pending-dropdown.open').forEach(function(d) { d.classList.remove('open'); });\n }\n });\n\n // -- Init --\n initCharts();\n loadInitialData();\n connectWS();\n\n})();\n</script>\n</body>\n</html>`;\n}\n","// WebSocket handler for AgentE Server\n// Same port via HTTP upgrade. JSON messages with `type` field.\n\nimport type * as http from 'node:http';\nimport { timingSafeEqual } from 'node:crypto';\nimport { WebSocketServer, WebSocket } from 'ws';\nimport { validateEconomyState, type EconomyState, type EconomicEvent } from '@agent-e/core';\nimport type { AgentEServer } from './AgentEServer.js';\n\ninterface IncomingMessage {\n type: string;\n [key: string]: unknown;\n}\n\nfunction send(ws: WebSocket, data: Record<string, unknown>): void {\n if (ws.readyState === WebSocket.OPEN) {\n ws.send(JSON.stringify(data));\n }\n}\n\nexport interface WebSocketHandle {\n cleanup: () => void;\n broadcast: (data: Record<string, unknown>) => void;\n}\n\nconst MAX_WS_PAYLOAD = 1_048_576; // 1 MB\nconst MAX_WS_CONNECTIONS = 100;\nconst MIN_TICK_INTERVAL_MS = 100; // rate limit: max 10 ticks/sec per connection\n\n/** Strips prototype-polluting keys from parsed JSON objects (recursive). */\nfunction sanitizeJson(obj: unknown): unknown {\n if (obj === null || typeof obj !== 'object') return obj;\n if (Array.isArray(obj)) return obj.map(sanitizeJson);\n const clean: Record<string, unknown> = {};\n for (const [key, val] of Object.entries(obj as Record<string, unknown>)) {\n if (key === '__proto__' || key === 'constructor' || key === 'prototype') continue;\n clean[key] = sanitizeJson(val);\n }\n return clean;\n}\n\nexport function createWebSocketHandler(\n httpServer: http.Server,\n server: AgentEServer,\n): WebSocketHandle {\n const wss = new WebSocketServer({ server: httpServer, maxPayload: MAX_WS_PAYLOAD });\n\n // Heartbeat: ping every 30s, disconnect if no pong within 10s\n const aliveMap = new WeakMap<WebSocket, boolean>();\n\n const heartbeatInterval = setInterval(() => {\n for (const ws of wss.clients) {\n if (ws.readyState === WebSocket.OPEN) {\n if (aliveMap.get(ws) === false) {\n // No pong received since last ping — terminate\n ws.terminate();\n continue;\n }\n aliveMap.set(ws, false);\n ws.ping();\n }\n }\n }, 30_000);\n\n wss.on('connection', (ws, req) => {\n if (wss.clients.size > MAX_WS_CONNECTIONS) {\n ws.close(1013, 'Server at capacity');\n return;\n }\n\n // Origin check: validate against CORS policy (skip for non-browser / missing origin)\n const wsOrigin = req.headers['origin'];\n if (wsOrigin && server.corsOrigin !== '*') {\n if (wsOrigin.toLowerCase() !== server.corsOrigin.toLowerCase()) {\n ws.close(1008, 'Origin not allowed');\n return;\n }\n }\n\n // Auth check: if apiKey is configured, require it via query param or protocol header\n if (server.apiKey) {\n const url = new URL(req.url ?? '/', `http://${req.headers.host ?? 'localhost'}`);\n const token = url.searchParams.get('token') ?? req.headers['authorization']?.replace('Bearer ', '');\n if (!token || token.length !== server.apiKey.length || !timingSafeEqual(Buffer.from(token), Buffer.from(server.apiKey))) {\n ws.close(1008, 'Unauthorized');\n return;\n }\n }\n\n console.log('[AgentE Server] Client connected');\n aliveMap.set(ws, true);\n\n let lastTickTime = 0;\n\n ws.on('pong', () => {\n aliveMap.set(ws, true);\n });\n\n ws.on('close', () => {\n console.log('[AgentE Server] Client disconnected');\n });\n\n ws.on('message', async (raw) => {\n let msg: IncomingMessage;\n try {\n msg = sanitizeJson(JSON.parse(raw.toString())) as IncomingMessage;\n } catch {\n send(ws, { type: 'error', message: 'Malformed JSON' });\n return;\n }\n\n if (!msg.type || typeof msg.type !== 'string') {\n send(ws, { type: 'error', message: 'Missing \"type\" field' });\n return;\n }\n\n switch (msg.type) {\n case 'tick': {\n const now = Date.now();\n if (now - lastTickTime < MIN_TICK_INTERVAL_MS) {\n send(ws, { type: 'error', message: 'Rate limited — min 100ms between ticks' });\n break;\n }\n lastTickTime = now;\n\n const state = msg['state'];\n const events = msg['events'];\n\n if (server.validateState) {\n const validation = validateEconomyState(state);\n if (!validation.valid) {\n send(ws, { type: 'validation_error', validationErrors: validation.errors });\n return;\n }\n\n // Forward warnings even if valid\n if (validation.warnings.length > 0) {\n send(ws, { type: 'validation_warning', validationWarnings: validation.warnings });\n }\n }\n\n try {\n const result = await server.processTick(\n state as EconomyState,\n Array.isArray(events) ? events as EconomicEvent[] : undefined,\n );\n\n send(ws, {\n type: 'tick_result',\n adjustments: result.adjustments,\n alerts: result.alerts.map(a => ({\n principleId: a.principle.id,\n principleName: a.principle.name,\n severity: a.violation.severity,\n reasoning: a.violation.suggestedAction.reasoning,\n })),\n health: result.health,\n tick: result.tick,\n });\n } catch (_err) {\n send(ws, { type: 'error', message: 'Tick processing failed' });\n }\n break;\n }\n\n case 'event': {\n const event = msg['event'] as EconomicEvent | undefined;\n if (event) {\n server.getAgentE().ingest(event);\n send(ws, { type: 'event_ack' });\n } else {\n send(ws, { type: 'error', message: 'Missing \"event\" field' });\n }\n break;\n }\n\n case 'health': {\n const agentE = server.getAgentE();\n send(ws, {\n type: 'health_result',\n health: agentE.getHealth(),\n tick: agentE.metrics.latest()?.tick ?? 0,\n mode: agentE.getMode(),\n activePlans: agentE.getActivePlans().length,\n uptime: server.getUptime(),\n });\n break;\n }\n\n case 'diagnose': {\n const state = msg['state'];\n\n if (server.validateState) {\n const validation = validateEconomyState(state);\n if (!validation.valid) {\n send(ws, { type: 'validation_error', validationErrors: validation.errors });\n return;\n }\n }\n\n const result = server.diagnoseOnly(state as EconomyState);\n send(ws, {\n type: 'diagnose_result',\n health: result.health,\n diagnoses: result.diagnoses.map(d => ({\n principleId: d.principle.id,\n principleName: d.principle.name,\n severity: d.violation.severity,\n suggestedAction: d.violation.suggestedAction,\n })),\n });\n break;\n }\n\n default:\n send(ws, { type: 'error', message: `Unknown message type: \"${String(msg.type).slice(0, 100)}\"` });\n }\n });\n });\n\n function broadcast(data: Record<string, unknown>): void {\n const payload = JSON.stringify(data);\n for (const ws of wss.clients) {\n if (ws.readyState === WebSocket.OPEN) {\n ws.send(payload);\n }\n }\n }\n\n return {\n cleanup: () => {\n clearInterval(heartbeatInterval);\n wss.close();\n },\n broadcast,\n };\n}\n"],"mappings":";AAEA,YAAY,UAAU;AACtB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAQK;;;ACZP,SAAS,uBAAuB;AAChC,SAAS,4BAA4B;;;ACF9B,SAAS,mBAA2B;AACzC,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;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","names":["timingSafeEqual","validateEconomyState","sanitizeJson"]}
package/dist/cli.js CHANGED
@@ -718,7 +718,7 @@ function getDashboardHtml() {
718
718
  <header class="header" id="header">
719
719
  <div class="header-left">
720
720
  <span class="header-logo">AgentE</span>
721
- <span class="header-version">v1.7.2</span>
721
+ <span class="header-version">v1.8.0</span>
722
722
  </div>
723
723
  <div class="header-right">
724
724
  <div class="kpi-pill">