@agent-e/server 1.6.8 → 1.6.10

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-MH5XTBNY.mjs";
4
+ export {
5
+ AgentEServer
6
+ };
7
+ //# sourceMappingURL=AgentEServer-WOYIMZWG.mjs.map
@@ -550,7 +550,7 @@ function getDashboardHtml() {
550
550
  const $app = document.getElementById('app');
551
551
 
552
552
  // \u2500\u2500 Helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
553
- function esc(s) { return String(s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;').replace(/'/g,'&#39;'); }
553
+ function esc(s) { return String(s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;').replace(/'/g,'&#39;').replace(/\\\\/g,'&#92;'); }
554
554
  function pad(n, w) { return String(n).padStart(w || 4, ' '); }
555
555
  function fmt(n) { return typeof n === 'number' ? n.toFixed(3) : '\u2014'; }
556
556
  function pct(n) { return typeof n === 'number' ? (n * 100).toFixed(0) + '%' : '\u2014'; }
@@ -922,7 +922,7 @@ function getDashboardHtml() {
922
922
  window._approve = function(id) {
923
923
  postJSON('/approve', { decisionId: id }).then(function(data) {
924
924
  if (data.ok) {
925
- addTerminalLine('<span class="t-tick">[Advisor]</span> <span class="t-ok">\\u2705 Approved ' + id + '</span>');
925
+ addTerminalLine('<span class="t-tick">[Advisor]</span> <span class="t-ok">\\u2705 Approved ' + esc(id) + '</span>');
926
926
  }
927
927
  }).catch(function() {});
928
928
  };
@@ -931,7 +931,7 @@ function getDashboardHtml() {
931
931
  var reason = prompt('Rejection reason (optional):');
932
932
  postJSON('/reject', { decisionId: id, reason: reason || undefined }).then(function(data) {
933
933
  if (data.ok) {
934
- addTerminalLine('<span class="t-tick">[Advisor]</span> <span class="t-fail">\\u274c Rejected ' + id + '</span>');
934
+ addTerminalLine('<span class="t-tick">[Advisor]</span> <span class="t-fail">\\u274c Rejected ' + esc(id) + '</span>');
935
935
  }
936
936
  }).catch(function() {});
937
937
  };
@@ -953,14 +953,30 @@ function setSecurityHeaders(res) {
953
953
  res.setHeader("X-Frame-Options", "DENY");
954
954
  res.setHeader("Referrer-Policy", "strict-origin-when-cross-origin");
955
955
  }
956
- function setCorsHeaders(res, origin) {
956
+ function setCorsHeaders(res, allowedOrigin, requestOrigin) {
957
957
  setSecurityHeaders(res);
958
+ const origin = allowedOrigin === "*" ? "*" : requestOrigin === allowedOrigin ? allowedOrigin : allowedOrigin;
958
959
  res.setHeader("Access-Control-Allow-Origin", origin);
959
960
  res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
960
- res.setHeader("Access-Control-Allow-Headers", "Content-Type");
961
+ res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
961
962
  }
962
- function json(res, status, data, origin) {
963
- setCorsHeaders(res, origin);
963
+ function sanitizeJson(obj) {
964
+ if (obj === null || typeof obj !== "object") return obj;
965
+ if (Array.isArray(obj)) return obj.map(sanitizeJson);
966
+ const clean = {};
967
+ for (const [key, val] of Object.entries(obj)) {
968
+ if (key === "__proto__" || key === "constructor" || key === "prototype") continue;
969
+ clean[key] = sanitizeJson(val);
970
+ }
971
+ return clean;
972
+ }
973
+ function checkAuth(req, apiKey) {
974
+ if (!apiKey) return true;
975
+ const header = req.headers["authorization"];
976
+ return header === `Bearer ${apiKey}`;
977
+ }
978
+ function json(res, status, data, origin, reqOrigin) {
979
+ setCorsHeaders(res, origin, reqOrigin);
964
980
  res.writeHead(status, { "Content-Type": "application/json" });
965
981
  res.end(JSON.stringify(data));
966
982
  }
@@ -984,6 +1000,7 @@ function readBody(req) {
984
1000
  }
985
1001
  function createRouteHandler(server) {
986
1002
  const cors = server.corsOrigin;
1003
+ const apiKey = server.apiKey;
987
1004
  return async (req, res) => {
988
1005
  const url = new URL(req.url ?? "/", `http://${req.headers.host ?? "localhost"}`);
989
1006
  const path = url.pathname;
@@ -996,10 +1013,14 @@ function createRouteHandler(server) {
996
1013
  }
997
1014
  try {
998
1015
  if (path === "/tick" && method === "POST") {
1016
+ if (!checkAuth(req, apiKey)) {
1017
+ json(res, 401, { error: "Unauthorized" }, cors);
1018
+ return;
1019
+ }
999
1020
  const body = await readBody(req);
1000
1021
  let parsed;
1001
1022
  try {
1002
- parsed = JSON.parse(body);
1023
+ parsed = sanitizeJson(JSON.parse(body));
1003
1024
  } catch {
1004
1025
  json(res, 400, { error: "Invalid JSON" }, cors);
1005
1026
  return;
@@ -1070,10 +1091,14 @@ function createRouteHandler(server) {
1070
1091
  return;
1071
1092
  }
1072
1093
  if (path === "/config" && method === "POST") {
1094
+ if (!checkAuth(req, apiKey)) {
1095
+ json(res, 401, { error: "Unauthorized" }, cors);
1096
+ return;
1097
+ }
1073
1098
  const body = await readBody(req);
1074
1099
  let parsed;
1075
1100
  try {
1076
- parsed = JSON.parse(body);
1101
+ parsed = sanitizeJson(JSON.parse(body));
1077
1102
  } catch {
1078
1103
  json(res, 400, { error: "Invalid JSON" }, cors);
1079
1104
  return;
@@ -1090,6 +1115,7 @@ function createRouteHandler(server) {
1090
1115
  }
1091
1116
  }
1092
1117
  if (Array.isArray(config["constrain"])) {
1118
+ const validated = [];
1093
1119
  for (const c of config["constrain"]) {
1094
1120
  if (c && typeof c === "object" && typeof c["param"] === "string" && typeof c["min"] === "number" && typeof c["max"] === "number") {
1095
1121
  const constraint = c;
@@ -1101,9 +1127,12 @@ function createRouteHandler(server) {
1101
1127
  json(res, 400, { error: "Constraint min cannot exceed max" }, cors);
1102
1128
  return;
1103
1129
  }
1104
- server.constrain(constraint.param, { min: constraint.min, max: constraint.max });
1130
+ validated.push(constraint);
1105
1131
  }
1106
1132
  }
1133
+ for (const constraint of validated) {
1134
+ server.constrain(constraint.param, { min: constraint.min, max: constraint.max });
1135
+ }
1107
1136
  }
1108
1137
  if (config["mode"] === "autonomous" || config["mode"] === "advisor") {
1109
1138
  server.setMode(config["mode"]);
@@ -1125,10 +1154,14 @@ function createRouteHandler(server) {
1125
1154
  return;
1126
1155
  }
1127
1156
  if (path === "/diagnose" && method === "POST") {
1157
+ if (!checkAuth(req, apiKey)) {
1158
+ json(res, 401, { error: "Unauthorized" }, cors);
1159
+ return;
1160
+ }
1128
1161
  const body = await readBody(req);
1129
1162
  let parsed;
1130
1163
  try {
1131
- parsed = JSON.parse(body);
1164
+ parsed = sanitizeJson(JSON.parse(body));
1132
1165
  } catch {
1133
1166
  json(res, 400, { error: "Invalid JSON" }, cors);
1134
1167
  return;
@@ -1179,10 +1212,14 @@ function createRouteHandler(server) {
1179
1212
  return;
1180
1213
  }
1181
1214
  if (path === "/approve" && method === "POST") {
1215
+ if (!checkAuth(req, apiKey)) {
1216
+ json(res, 401, { error: "Unauthorized" }, cors);
1217
+ return;
1218
+ }
1182
1219
  const body = await readBody(req);
1183
1220
  let parsed;
1184
1221
  try {
1185
- parsed = JSON.parse(body);
1222
+ parsed = sanitizeJson(JSON.parse(body));
1186
1223
  } catch {
1187
1224
  json(res, 400, { error: "Invalid JSON" }, cors);
1188
1225
  return;
@@ -1218,10 +1255,14 @@ function createRouteHandler(server) {
1218
1255
  return;
1219
1256
  }
1220
1257
  if (path === "/reject" && method === "POST") {
1258
+ if (!checkAuth(req, apiKey)) {
1259
+ json(res, 401, { error: "Unauthorized" }, cors);
1260
+ return;
1261
+ }
1221
1262
  const body = await readBody(req);
1222
1263
  let parsed;
1223
1264
  try {
1224
- parsed = JSON.parse(body);
1265
+ parsed = sanitizeJson(JSON.parse(body));
1225
1266
  } catch {
1226
1267
  json(res, 400, { error: "Invalid JSON" }, cors);
1227
1268
  return;
@@ -1280,6 +1321,17 @@ function send(ws, data) {
1280
1321
  }
1281
1322
  var MAX_WS_PAYLOAD = 1048576;
1282
1323
  var MAX_WS_CONNECTIONS = 100;
1324
+ var MIN_TICK_INTERVAL_MS = 100;
1325
+ function sanitizeJson2(obj) {
1326
+ if (obj === null || typeof obj !== "object") return obj;
1327
+ if (Array.isArray(obj)) return obj.map(sanitizeJson2);
1328
+ const clean = {};
1329
+ for (const [key, val] of Object.entries(obj)) {
1330
+ if (key === "__proto__" || key === "constructor" || key === "prototype") continue;
1331
+ clean[key] = sanitizeJson2(val);
1332
+ }
1333
+ return clean;
1334
+ }
1283
1335
  function createWebSocketHandler(httpServer, server) {
1284
1336
  const wss = new WebSocketServer({ server: httpServer, maxPayload: MAX_WS_PAYLOAD });
1285
1337
  const aliveMap = /* @__PURE__ */ new WeakMap();
@@ -1295,13 +1347,22 @@ function createWebSocketHandler(httpServer, server) {
1295
1347
  }
1296
1348
  }
1297
1349
  }, 3e4);
1298
- wss.on("connection", (ws) => {
1350
+ wss.on("connection", (ws, req) => {
1299
1351
  if (wss.clients.size > MAX_WS_CONNECTIONS) {
1300
1352
  ws.close(1013, "Server at capacity");
1301
1353
  return;
1302
1354
  }
1355
+ if (server.apiKey) {
1356
+ const url = new URL(req.url ?? "/", `http://${req.headers.host ?? "localhost"}`);
1357
+ const token = url.searchParams.get("token") ?? req.headers["authorization"]?.replace("Bearer ", "");
1358
+ if (token !== server.apiKey) {
1359
+ ws.close(1008, "Unauthorized");
1360
+ return;
1361
+ }
1362
+ }
1303
1363
  console.log("[AgentE Server] Client connected");
1304
1364
  aliveMap.set(ws, true);
1365
+ let lastTickTime = 0;
1305
1366
  ws.on("pong", () => {
1306
1367
  aliveMap.set(ws, true);
1307
1368
  });
@@ -1311,7 +1372,7 @@ function createWebSocketHandler(httpServer, server) {
1311
1372
  ws.on("message", async (raw) => {
1312
1373
  let msg;
1313
1374
  try {
1314
- msg = JSON.parse(raw.toString());
1375
+ msg = sanitizeJson2(JSON.parse(raw.toString()));
1315
1376
  } catch {
1316
1377
  send(ws, { type: "error", message: "Malformed JSON" });
1317
1378
  return;
@@ -1322,6 +1383,12 @@ function createWebSocketHandler(httpServer, server) {
1322
1383
  }
1323
1384
  switch (msg.type) {
1324
1385
  case "tick": {
1386
+ const now = Date.now();
1387
+ if (now - lastTickTime < MIN_TICK_INTERVAL_MS) {
1388
+ send(ws, { type: "error", message: "Rate limited \u2014 min 100ms between ticks" });
1389
+ break;
1390
+ }
1391
+ lastTickTime = now;
1325
1392
  const state = msg["state"];
1326
1393
  const events = msg["events"];
1327
1394
  if (server.validateState) {
@@ -1401,7 +1468,7 @@ function createWebSocketHandler(httpServer, server) {
1401
1468
  break;
1402
1469
  }
1403
1470
  default:
1404
- send(ws, { type: "error", message: `Unknown message type: "${msg.type}"` });
1471
+ send(ws, { type: "error", message: `Unknown message type: "${String(msg.type).slice(0, 100)}"` });
1405
1472
  }
1406
1473
  });
1407
1474
  });
@@ -1431,7 +1498,8 @@ var AgentEServer = class {
1431
1498
  this.startedAt = Date.now();
1432
1499
  this.wsHandle = null;
1433
1500
  this.port = config.port ?? 3100;
1434
- this.host = config.host ?? "0.0.0.0";
1501
+ this.host = config.host ?? "127.0.0.1";
1502
+ this.apiKey = config.apiKey;
1435
1503
  this.validateState = config.validateState ?? true;
1436
1504
  this.corsOrigin = config.corsOrigin ?? "http://localhost:3100";
1437
1505
  this.serveDashboard = config.serveDashboard ?? true;
@@ -1596,4 +1664,4 @@ var AgentEServer = class {
1596
1664
  export {
1597
1665
  AgentEServer
1598
1666
  };
1599
- //# sourceMappingURL=chunk-T66KRKT4.mjs.map
1667
+ //# sourceMappingURL=chunk-MH5XTBNY.mjs.map
@@ -0,0 +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 { 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; otherwise validate request origin\n const origin = allowedOrigin === '*' ? '*'\n : (requestOrigin === allowedOrigin ? allowedOrigin : allowedOrigin);\n res.setHeader('Access-Control-Allow-Origin', origin);\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 return header === `Bearer ${apiKey}`;\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\n // CORS preflight\n if (method === 'OPTIONS') {\n setCorsHeaders(res, cors);\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 json(res, 401, { error: 'Unauthorized' }, cors);\n return;\n }\n const body = await readBody(req);\n let parsed: unknown;\n try {\n parsed = sanitizeJson(JSON.parse(body));\n } catch {\n json(res, 400, { error: 'Invalid JSON' }, cors);\n return;\n }\n\n if (!parsed || typeof parsed !== 'object') {\n json(res, 400, { error: 'Body must be a JSON object' }, cors);\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 json(res, 400, {\n error: 'invalid_state',\n validationErrors: validation.errors,\n }, cors);\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 json(res, 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 }, cors);\n return;\n }\n\n // GET /health — health, tick, mode, activePlans, uptime\n if (path === '/health' && method === 'GET') {\n const agentE = server.getAgentE();\n json(res, 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 }, cors);\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 json(res, 400, { error: 'Invalid \"since\" parameter — must be a number' }, cors);\n return;\n }\n decisions = agentE.getDecisions({ since });\n } else {\n decisions = agentE.log.latest(limit);\n }\n\n json(res, 200, { decisions }, cors);\n return;\n }\n\n // POST /config — batch lock/unlock/constrain/mode\n if (path === '/config' && method === 'POST') {\n if (!checkAuth(req, apiKey)) {\n json(res, 401, { error: 'Unauthorized' }, cors);\n return;\n }\n const body = await readBody(req);\n let parsed: unknown;\n try {\n parsed = sanitizeJson(JSON.parse(body));\n } catch {\n json(res, 400, { error: 'Invalid JSON' }, cors);\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 json(res, 400, { error: 'Constraint bounds must be finite numbers' }, cors);\n return;\n }\n if (constraint.min > constraint.max) {\n json(res, 400, { error: 'Constraint min cannot exceed max' }, cors);\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 json(res, 200, { ok: true }, cors);\n return;\n }\n\n // GET /principles — list all principles\n if (path === '/principles' && method === 'GET') {\n const principles = server.getAgentE().getPrinciples();\n json(res, 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 }, cors);\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 json(res, 401, { error: 'Unauthorized' }, cors);\n return;\n }\n const body = await readBody(req);\n let parsed: unknown;\n try {\n parsed = sanitizeJson(JSON.parse(body));\n } catch {\n json(res, 400, { error: 'Invalid JSON' }, cors);\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 json(res, 400, { error: 'invalid_state', validationErrors: validation.errors }, cors);\n return;\n }\n }\n\n const result = server.diagnoseOnly(state as import('@agent-e/core').EconomyState);\n\n json(res, 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 }, cors);\n return;\n }\n\n // GET / — Dashboard HTML\n if (path === '/' && method === 'GET' && server.serveDashboard) {\n setCorsHeaders(res, cors);\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 json(res, 200, { latest, history }, cors);\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 json(res, 200, { distribution: dist, total }, cors);\n return;\n }\n\n // POST /approve — Approve advisor recommendation\n if (path === '/approve' && method === 'POST') {\n if (!checkAuth(req, apiKey)) {\n json(res, 401, { error: 'Unauthorized' }, cors);\n return;\n }\n const body = await readBody(req);\n let parsed: unknown;\n try { parsed = sanitizeJson(JSON.parse(body)); } catch {\n json(res, 400, { error: 'Invalid JSON' }, cors);\n return;\n }\n const payload = parsed as Record<string, unknown>;\n const decisionId = payload['decisionId'] as string;\n if (!decisionId) {\n json(res, 400, { error: 'missing_decision_id' }, cors);\n return;\n }\n\n const agentE = server.getAgentE();\n if (agentE.getMode() !== 'advisor') {\n json(res, 400, { error: 'not_in_advisor_mode' }, cors);\n return;\n }\n\n const entry = agentE.log.getById(decisionId);\n if (!entry) {\n json(res, 404, { error: 'decision_not_found' }, cors);\n return;\n }\n if (entry.result !== 'skipped_override') {\n json(res, 409, { error: 'decision_not_pending', currentResult: entry.result }, cors);\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 json(res, 200, {\n ok: true,\n parameter: entry.plan.parameter,\n value: entry.plan.targetValue,\n }, cors);\n return;\n }\n\n // POST /reject — Reject advisor recommendation\n if (path === '/reject' && method === 'POST') {\n if (!checkAuth(req, apiKey)) {\n json(res, 401, { error: 'Unauthorized' }, cors);\n return;\n }\n const body = await readBody(req);\n let parsed: unknown;\n try { parsed = sanitizeJson(JSON.parse(body)); } catch {\n json(res, 400, { error: 'Invalid JSON' }, cors);\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 json(res, 400, { error: 'missing_decision_id' }, cors);\n return;\n }\n\n const agentE = server.getAgentE();\n if (agentE.getMode() !== 'advisor') {\n json(res, 400, { error: 'not_in_advisor_mode' }, cors);\n return;\n }\n\n const entry = agentE.log.getById(decisionId);\n if (!entry) {\n json(res, 404, { error: 'decision_not_found' }, cors);\n return;\n }\n if (entry.result !== 'skipped_override') {\n json(res, 409, { error: 'decision_not_pending', currentResult: entry.result }, cors);\n return;\n }\n\n agentE.log.updateResult(decisionId, 'rejected', reason);\n server.broadcast({ type: 'advisor_action', action: 'rejected', decisionId, reason });\n json(res, 200, { ok: true, decisionId }, cors);\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 json(res, 200, {\n mode: agentE.getMode(),\n pending,\n count: pending.length,\n }, cors);\n return;\n }\n\n // 404\n json(res, 404, { error: 'Not found' }, cors);\n } catch (err) {\n console.error('[AgentE Server] Unhandled route error:', err);\n json(res, 500, { error: 'Internal server error' }, cors);\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=IBM+Plex+Sans:wght@400;500;600&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 :root {\n --bg-root: #09090b;\n --bg-panel: #18181b;\n --bg-panel-hover: #1f1f23;\n --border: #27272a;\n --border-light: #3f3f46;\n --text-primary: #f4f4f5;\n --text-secondary: #a1a1aa;\n --text-muted: #71717a;\n --text-dim: #52525b;\n --accent: #22c55e;\n --accent-dim: #166534;\n --warning: #eab308;\n --warning-dim: #854d0e;\n --danger: #ef4444;\n --danger-dim: #991b1b;\n --blue: #3b82f6;\n --font-sans: 'IBM Plex Sans', system-ui, sans-serif;\n --font-mono: 'JetBrains Mono', monospace;\n }\n\n * { margin: 0; padding: 0; box-sizing: border-box; }\n\n body {\n background: var(--bg-root);\n color: var(--text-primary);\n font-family: var(--font-sans);\n font-size: 14px;\n line-height: 1.5;\n overflow-x: hidden;\n }\n\n /* ── Header ─────────────────────────────────────── */\n .header {\n position: sticky;\n top: 0;\n z-index: 100;\n background: var(--bg-root);\n border-bottom: 1px solid var(--border);\n padding: 12px 24px;\n display: flex;\n align-items: center;\n gap: 24px;\n backdrop-filter: blur(8px);\n }\n\n .header-brand {\n font-weight: 600;\n font-size: 16px;\n color: var(--text-primary);\n white-space: nowrap;\n }\n\n .header-brand span { color: var(--accent); }\n\n .kpi-row {\n display: flex;\n gap: 20px;\n flex-wrap: wrap;\n align-items: center;\n margin-left: auto;\n }\n\n .kpi {\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 13px;\n color: var(--text-secondary);\n }\n\n .kpi-value {\n font-family: var(--font-mono);\n font-weight: 500;\n color: var(--text-primary);\n font-size: 13px;\n }\n\n .kpi-value.health-good { color: var(--accent); }\n .kpi-value.health-warn { color: var(--warning); }\n .kpi-value.health-bad { color: var(--danger); }\n\n .live-dot {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n background: var(--accent);\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 /* ── Layout ─────────────────────────────────────── */\n .container {\n max-width: 1440px;\n margin: 0 auto;\n padding: 20px 24px;\n display: flex;\n flex-direction: column;\n gap: 16px;\n }\n\n .panel {\n background: var(--bg-panel);\n border: 1px solid var(--border);\n border-radius: 8px;\n padding: 16px;\n }\n\n .panel-title {\n font-size: 13px;\n font-weight: 600;\n color: var(--text-secondary);\n text-transform: uppercase;\n letter-spacing: 0.05em;\n margin-bottom: 12px;\n }\n\n /* ── Charts grid ────────────────────────────────── */\n .charts-grid {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));\n gap: 16px;\n }\n\n .chart-box {\n background: var(--bg-panel);\n border: 1px solid var(--border);\n border-radius: 8px;\n padding: 16px;\n }\n\n .chart-box canvas { width: 100% !important; height: 160px !important; }\n\n .chart-label {\n font-size: 12px;\n color: var(--text-muted);\n font-weight: 500;\n margin-bottom: 8px;\n }\n\n .chart-value {\n font-family: var(--font-mono);\n font-size: 22px;\n font-weight: 500;\n color: var(--text-primary);\n margin-bottom: 8px;\n }\n\n /* ── Terminal (Decision Feed) ───────────────────── */\n .terminal {\n background: var(--bg-root);\n border: 1px solid var(--border);\n border-radius: 8px;\n height: 380px;\n overflow-y: auto;\n font-family: var(--font-mono);\n font-size: 12px;\n line-height: 1.7;\n padding: 12px 16px;\n }\n\n .terminal::-webkit-scrollbar { width: 6px; }\n .terminal::-webkit-scrollbar-track { background: transparent; }\n .terminal::-webkit-scrollbar-thumb { background: var(--border-light); border-radius: 3px; }\n\n .term-line {\n white-space: nowrap;\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-dim); }\n .t-ok { 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-secondary); }\n .t-old { color: #d4d4d8; font-variant-numeric: tabular-nums; }\n .t-arrow { color: var(--text-dim); }\n .t-new { color: var(--accent); font-variant-numeric: tabular-nums; }\n .t-meta { color: var(--text-dim); }\n\n /* ── Alerts ─────────────────────────────────────── */\n .alerts-container {\n display: flex;\n flex-direction: column;\n gap: 8px;\n max-height: 320px;\n overflow-y: auto;\n }\n\n .alert-card {\n display: flex;\n align-items: flex-start;\n gap: 12px;\n padding: 12px;\n border-radius: 6px;\n border: 1px solid var(--border);\n background: var(--bg-panel);\n transition: opacity 0.3s, transform 0.3s;\n }\n\n .alert-card.fade-out {\n opacity: 0;\n transform: translateX(20px);\n }\n\n .alert-severity {\n font-family: var(--font-mono);\n font-weight: 600;\n font-size: 13px;\n padding: 2px 8px;\n border-radius: 4px;\n white-space: nowrap;\n }\n\n .sev-high { background: var(--danger-dim); color: var(--danger); }\n .sev-med { background: var(--warning-dim); color: var(--warning); }\n .sev-low { background: var(--accent-dim); color: var(--accent); }\n\n .alert-body { flex: 1; }\n .alert-principle { font-weight: 500; font-size: 13px; }\n .alert-reason { color: var(--text-secondary); font-size: 12px; margin-top: 2px; }\n\n /* ── Violations table ──────────────────────────── */\n .violations-table {\n width: 100%;\n border-collapse: collapse;\n font-size: 12px;\n }\n\n .violations-table th {\n text-align: left;\n color: var(--text-muted);\n font-weight: 500;\n padding: 6px 10px;\n border-bottom: 1px solid var(--border);\n cursor: pointer;\n user-select: none;\n }\n\n .violations-table th:hover { color: var(--text-secondary); }\n\n .violations-table td {\n padding: 6px 10px;\n border-bottom: 1px solid var(--border);\n color: var(--text-secondary);\n font-family: var(--font-mono);\n font-size: 11px;\n }\n\n .violations-table tr:hover td { background: var(--bg-panel-hover); }\n\n /* ── Split row ─────────────────────────────────── */\n .split-row {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 16px;\n }\n\n @media (max-width: 800px) {\n .split-row { grid-template-columns: 1fr; }\n }\n\n /* ── Persona bar chart ─────────────────────────── */\n .persona-bars { display: flex; flex-direction: column; gap: 6px; }\n\n .persona-row {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 12px;\n }\n\n .persona-label {\n width: 100px;\n text-align: right;\n color: var(--text-secondary);\n font-size: 11px;\n flex-shrink: 0;\n }\n\n .persona-bar-track {\n flex: 1;\n height: 16px;\n background: var(--bg-root);\n border-radius: 3px;\n overflow: hidden;\n }\n\n .persona-bar-fill {\n height: 100%;\n background: var(--accent);\n border-radius: 3px;\n transition: width 0.5s ease;\n }\n\n .persona-pct {\n width: 40px;\n font-family: var(--font-mono);\n font-size: 11px;\n color: var(--text-muted);\n }\n\n /* ── Registry list ─────────────────────────────── */\n .registry-list { display: flex; flex-direction: column; gap: 4px; }\n\n .registry-item {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 6px 10px;\n border-radius: 4px;\n font-size: 12px;\n }\n\n .registry-item:nth-child(odd) { background: rgba(255,255,255,0.02); }\n .registry-key { color: var(--text-secondary); font-family: var(--font-mono); }\n .registry-val { color: var(--accent); font-family: var(--font-mono); font-weight: 500; }\n\n /* ── Advisor mode ──────────────────────────────── */\n .advisor-banner {\n display: none;\n background: var(--warning-dim);\n border: 1px solid var(--warning);\n color: var(--warning);\n padding: 8px 16px;\n border-radius: 6px;\n font-size: 13px;\n font-weight: 500;\n align-items: center;\n gap: 8px;\n }\n\n .advisor-mode .advisor-banner { display: flex; }\n\n .pending-pill {\n background: var(--warning);\n color: var(--bg-root);\n font-size: 11px;\n font-weight: 600;\n padding: 1px 8px;\n border-radius: 10px;\n font-family: var(--font-mono);\n }\n\n .advisor-btn {\n font-family: var(--font-mono);\n font-size: 11px;\n padding: 2px 10px;\n border-radius: 4px;\n border: none;\n cursor: pointer;\n font-weight: 500;\n transition: opacity 0.15s;\n }\n\n .advisor-btn:hover { opacity: 0.85; }\n .advisor-btn.approve { background: var(--accent); color: var(--bg-root); }\n .advisor-btn.reject { background: var(--danger); color: #fff; }\n\n .advisor-actions { display: none; gap: 6px; margin-left: 8px; }\n .advisor-mode .advisor-actions { display: inline-flex; }\n\n /* ── Empty state ───────────────────────────────── */\n .empty-state {\n color: var(--text-dim);\n font-size: 13px;\n text-align: center;\n padding: 40px 20px;\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 }\n</style>\n</head>\n<body>\n\n<!-- Header -->\n<div class=\"header\" id=\"header\">\n <div class=\"header-brand\">Agent<span>E</span> v1.6</div>\n <div class=\"kpi-row\">\n <div class=\"kpi\">Health <span class=\"kpi-value health-good\" id=\"kpi-health\">--</span></div>\n <div class=\"kpi\">Mode <span class=\"kpi-value\" id=\"kpi-mode\">--</span></div>\n <div class=\"kpi\">Tick <span class=\"kpi-value\" id=\"kpi-tick\">0</span></div>\n <div class=\"kpi\">Uptime <span class=\"kpi-value\" id=\"kpi-uptime\">0s</span></div>\n <div class=\"kpi\">Plans <span class=\"kpi-value\" id=\"kpi-plans\">0</span></div>\n <div class=\"live-dot\" id=\"live-dot\" title=\"WebSocket connected\"></div>\n </div>\n</div>\n\n<div class=\"container\" id=\"app\">\n <!-- Advisor banner -->\n <div class=\"advisor-banner\" id=\"advisor-banner\">\n ADVISOR MODE — Recommendations require manual approval\n <span class=\"pending-pill\" id=\"pending-count\">0</span> pending\n </div>\n\n <!-- Charts -->\n <div class=\"charts-grid\">\n <div class=\"chart-box\">\n <div class=\"chart-label\">Economy Health</div>\n <div class=\"chart-value\" id=\"cv-health\">--</div>\n <canvas id=\"chart-health\"></canvas>\n </div>\n <div class=\"chart-box\">\n <div class=\"chart-label\">Gini Coefficient</div>\n <div class=\"chart-value\" id=\"cv-gini\">--</div>\n <canvas id=\"chart-gini\"></canvas>\n </div>\n <div class=\"chart-box\">\n <div class=\"chart-label\">Net Flow</div>\n <div class=\"chart-value\" id=\"cv-netflow\">--</div>\n <canvas id=\"chart-netflow\"></canvas>\n </div>\n <div class=\"chart-box\">\n <div class=\"chart-label\">Avg Satisfaction</div>\n <div class=\"chart-value\" id=\"cv-satisfaction\">--</div>\n <canvas id=\"chart-satisfaction\"></canvas>\n </div>\n </div>\n\n <!-- Decision Feed -->\n <div class=\"panel\">\n <div class=\"panel-title\">Decision Feed</div>\n <div class=\"terminal\" id=\"terminal\"></div>\n </div>\n\n <!-- Active Alerts -->\n <div class=\"panel\">\n <div class=\"panel-title\">Active Alerts</div>\n <div class=\"alerts-container\" id=\"alerts-container\">\n <div class=\"empty-state\" id=\"alerts-empty\">No active violations</div>\n </div>\n </div>\n\n <!-- Violation History -->\n <div class=\"panel\">\n <div class=\"panel-title\">Violation History</div>\n <div style=\"max-height:320px;overflow-y:auto\">\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\">Severity</th>\n <th data-sort=\"parameter\">Parameter</th>\n <th data-sort=\"result\">Result</th>\n </tr>\n </thead>\n <tbody id=\"violations-body\"></tbody>\n </table>\n </div>\n </div>\n\n <!-- Split: Personas + Registry -->\n <div class=\"split-row\">\n <div class=\"panel\">\n <div class=\"panel-title\">Persona Distribution</div>\n <div class=\"persona-bars\" id=\"persona-bars\">\n <div class=\"empty-state\">No persona data yet</div>\n </div>\n </div>\n <div class=\"panel\">\n <div class=\"panel-title\">Parameter Registry</div>\n <div class=\"registry-list\" id=\"registry-list\">\n <div class=\"empty-state\">No parameters registered</div>\n </div>\n </div>\n </div>\n</div>\n\n<script>\n(function() {\n 'use strict';\n\n // ── State ────────────────────────────────────────\n let ws = null;\n let reconnectDelay = 1000;\n const MAX_RECONNECT = 30000;\n let isAdvisor = false;\n let pendingDecisions = [];\n const MAX_TERMINAL_LINES = 80;\n const MAX_VIOLATIONS = 100;\n let violationSortKey = 'tick';\n let violationSortAsc = false;\n let violations = [];\n\n // Chart instances\n let chartHealth, chartGini, chartNetflow, chartSatisfaction;\n\n // ── DOM refs ─────────────────────────────────────\n const $kpiHealth = document.getElementById('kpi-health');\n const $kpiMode = document.getElementById('kpi-mode');\n const $kpiTick = document.getElementById('kpi-tick');\n const $kpiUptime = document.getElementById('kpi-uptime');\n const $kpiPlans = document.getElementById('kpi-plans');\n const $liveDot = document.getElementById('live-dot');\n const $terminal = document.getElementById('terminal');\n const $alertsContainer = document.getElementById('alerts-container');\n const $alertsEmpty = document.getElementById('alerts-empty');\n const $violationsBody = document.getElementById('violations-body');\n const $personaBars = document.getElementById('persona-bars');\n const $registryList = document.getElementById('registry-list');\n const $pendingCount = document.getElementById('pending-count');\n const $app = document.getElementById('app');\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) : '—'; }\n function pct(n) { return typeof n === 'number' ? (n * 100).toFixed(0) + '%' : '—'; }\n\n function formatUptime(ms) {\n const 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 const 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 'sev-high';\n if (s >= 4) return 'sev-med';\n return 'sev-low';\n }\n\n // ── Chart setup ──────────────────────────────────\n const chartOpts = {\n responsive: true,\n maintainAspectRatio: false,\n animation: { duration: 300 },\n plugins: { legend: { display: false } },\n scales: {\n x: { display: false },\n y: {\n ticks: { color: '#71717a', font: { family: \"'JetBrains Mono'\", size: 10 } },\n grid: { color: 'rgba(63,63,70,0.3)' },\n border: { display: false },\n }\n },\n elements: {\n point: { radius: 0 },\n line: { borderWidth: 1.5, tension: 0.3 },\n }\n };\n\n function makeChart(id, color, minY, maxY) {\n const ctx = document.getElementById(id).getContext('2d');\n const 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 chartNetflow = makeChart('chart-netflow', '#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 const el = document.createElement('div');\n el.className = 'term-line';\n el.innerHTML = html;\n $terminal.appendChild(el);\n while ($terminal.children.length > MAX_TERMINAL_LINES) {\n $terminal.removeChild($terminal.firstChild);\n }\n $terminal.scrollTop = $terminal.scrollHeight;\n }\n\n function decisionToTerminal(d) {\n const resultIcon = d.result === 'applied'\n ? '<span class=\"t-ok\">\\\\u2705 </span>'\n : d.result === 'rejected'\n ? '<span class=\"t-fail\">\\\\u274c </span>'\n : '<span class=\"t-skip\">\\\\u23f8 </span>';\n\n const principle = d.diagnosis?.principle || {};\n const plan = d.plan || {};\n const severity = d.diagnosis?.violation?.severity ?? '?';\n const confidence = d.diagnosis?.violation?.confidence;\n const confStr = confidence != null ? (confidence * 100).toFixed(0) + '%' : '?';\n\n let advisorBtns = '';\n if (isAdvisor && d.result === 'skipped_override') {\n advisorBtns = '<span class=\"advisor-actions\">'\n + '<button class=\"advisor-btn approve\" onclick=\"window._approve(\\\\'' + esc(d.id) + '\\\\')\">[Approve]</button>'\n + '<button class=\"advisor-btn reject\" onclick=\"window._reject(\\\\'' + esc(d.id) + '\\\\')\">[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.id || '?') + '] ' + esc(principle.name || '') + ':</span> '\n + '<span class=\"t-param\">' + esc(plan.parameter || '—') + ' </span>'\n + '<span class=\"t-old\">' + fmt(plan.currentValue) + '</span>'\n + '<span class=\"t-arrow\"> \\\\u2192 </span>'\n + '<span class=\"t-new\">' + fmt(plan.targetValue) + '</span>'\n + '<span class=\"t-meta\"> severity ' + severity + '/10, confidence ' + confStr + '</span>'\n + advisorBtns;\n }\n\n // ── Alerts ───────────────────────────────────────\n function renderAlerts(alerts) {\n if (!alerts || alerts.length === 0) {\n $alertsContainer.innerHTML = '<div class=\"empty-state\">No active violations</div>';\n return;\n }\n const sorted = [...alerts].sort((a, b) => (b.severity || 0) - (a.severity || 0));\n $alertsContainer.innerHTML = sorted.map(function(a) {\n const sev = a.severity || a.violation?.severity || 0;\n const sc = sevClass(sev);\n const name = a.principleName || a.principle?.name || '?';\n const pid = a.principleId || a.principle?.id || '?';\n const reason = a.reasoning || a.violation?.suggestedAction?.reasoning || '';\n return '<div class=\"alert-card\">'\n + '<span class=\"alert-severity ' + sc + '\">' + sev + '/10</span>'\n + '<div class=\"alert-body\">'\n + '<div class=\"alert-principle\">[' + esc(pid) + '] ' + esc(name) + '</div>'\n + '<div class=\"alert-reason\">' + esc(reason) + '</div>'\n + '</div></div>';\n }).join('');\n }\n\n // ── Violations table ─────────────────────────────\n function addViolation(d) {\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: d.plan?.parameter || '—',\n result: d.result,\n });\n if (violations.length > MAX_VIOLATIONS) violations.shift();\n renderViolations();\n }\n\n function renderViolations() {\n const sorted = [...violations].sort(function(a, b) {\n const 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 return '<tr>'\n + '<td>' + v.tick + '</td>'\n + '<td style=\"color:var(--text-primary);font-family:var(--font-sans)\">' + esc(v.principle) + '</td>'\n + '<td><span class=\"alert-severity ' + sevClass(v.severity) + '\">' + v.severity + '</span></td>'\n + '<td>' + esc(v.parameter) + '</td>'\n + '<td>' + esc(v.result) + '</td>'\n + '</tr>';\n }).join('');\n }\n\n // Table sorting\n document.querySelectorAll('.violations-table th').forEach(function(th) {\n th.addEventListener('click', function() {\n const 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 return;\n }\n const total = Object.values(dist).reduce(function(s, v) { return s + v; }, 0);\n const entries = Object.entries(dist).sort(function(a, b) { return b[1] - a[1]; });\n $personaBars.innerHTML = entries.map(function(e) {\n const pctVal = total > 0 ? (e[1] / total * 100) : 0;\n return '<div class=\"persona-row\">'\n + '<div class=\"persona-label\">' + esc(e[0]) + '</div>'\n + '<div class=\"persona-bar-track\"><div class=\"persona-bar-fill\" style=\"width:' + pctVal + '%\"></div></div>'\n + '<div class=\"persona-pct\">' + pctVal.toFixed(0) + '%</div>'\n + '</div>';\n }).join('');\n }\n\n // ── Registry ─────────────────────────────────────\n function renderRegistry(principles) {\n if (!principles || principles.length === 0) {\n $registryList.innerHTML = '<div class=\"empty-state\">No parameters registered</div>';\n return;\n }\n $registryList.innerHTML = principles.slice(0, 30).map(function(p) {\n return '<div class=\"registry-item\">'\n + '<span class=\"registry-key\">[' + esc(p.id) + ']</span>'\n + '<span class=\"registry-val\">' + esc(p.name) + '</span>'\n + '</div>';\n }).join('');\n }\n\n // ── KPI update ───────────────────────────────────\n function updateKPIs(data) {\n if (data.health != null) {\n $kpiHealth.textContent = data.health + '/100';\n $kpiHealth.className = 'kpi-value ' + healthClass(data.health);\n document.getElementById('cv-health').textContent = data.health + '/100';\n }\n if (data.mode != null) {\n $kpiMode.textContent = data.mode;\n isAdvisor = data.mode === 'advisor';\n $app.classList.toggle('advisor-mode', isAdvisor);\n }\n if (data.tick != null) $kpiTick.textContent = data.tick;\n if (data.uptime != null) $kpiUptime.textContent = formatUptime(data.uptime);\n if (data.activePlans != null) $kpiPlans.textContent = data.activePlans;\n }\n\n // ── Metrics history ──────────────────────────────\n function updateChartsFromHistory(history) {\n if (!history || history.length === 0) return;\n const 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(chartNetflow, ticks, history.map(function(h) { return h.netFlow; }));\n updateChart(chartSatisfaction, ticks, history.map(function(h) { return h.avgSatisfaction; }));\n\n const last = history[history.length - 1];\n document.getElementById('cv-gini').textContent = last.giniCoefficient.toFixed(3);\n document.getElementById('cv-netflow').textContent = last.netFlow.toFixed(1);\n document.getElementById('cv-satisfaction').textContent = last.avgSatisfaction.toFixed(0) + '/100';\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) renderRegistry(data.principles);\n }).catch(function() {});\n\n fetchJSON('/pending').then(function(data) {\n if (data.pending) {\n pendingDecisions = data.pending;\n $pendingCount.textContent = data.count || 0;\n }\n }).catch(function() {});\n }\n\n // ── Polling fallback ─────────────────────────────\n let 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 const 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 // Request fresh health\n ws.send(JSON.stringify({ type: 'health' }));\n };\n\n ws.onclose = function() {\n $liveDot.classList.add('disconnected');\n $liveDot.title = 'WebSocket disconnected — 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 // Refresh charts\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 $pendingCount.textContent = pendingDecisions.length;\n }\n break;\n }\n };\n }\n\n // ── Advisor actions ──────────────────────────────\n window._approve = function(id) {\n postJSON('/approve', { decisionId: id }).then(function(data) {\n if (data.ok) {\n addTerminalLine('<span class=\"t-tick\">[Advisor]</span> <span class=\"t-ok\">\\\\u2705 Approved ' + esc(id) + '</span>');\n }\n }).catch(function() {});\n };\n\n window._reject = function(id) {\n var reason = prompt('Rejection reason (optional):');\n postJSON('/reject', { decisionId: 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(id) + '</span>');\n }\n }).catch(function() {});\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 { 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 // 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 !== 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,4BAA4B;;;ACD9B,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;AAo6BT;;;ADh6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","names":["validateEconomyState","sanitizeJson"]}