@agent-e/server 1.7.1 → 1.7.3
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.
- package/dist/AgentEServer-JRUR4XQJ.mjs +7 -0
- package/dist/{chunk-O2UFQVI2.mjs → chunk-AWYCK646.mjs} +930 -410
- package/dist/chunk-AWYCK646.mjs.map +1 -0
- package/dist/cli.js +929 -409
- package/dist/cli.js.map +1 -1
- package/dist/cli.mjs +1 -1
- package/dist/index.js +930 -410
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2 -2
- package/package.json +3 -3
- package/dist/AgentEServer-PXMTFBHR.mjs +0 -7
- package/dist/chunk-O2UFQVI2.mjs.map +0 -1
- /package/dist/{AgentEServer-PXMTFBHR.mjs.map → AgentEServer-JRUR4XQJ.mjs.map} +0 -0
|
@@ -22,96 +22,109 @@ function getDashboardHtml() {
|
|
|
22
22
|
<title>AgentE Dashboard</title>
|
|
23
23
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
24
24
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
25
|
-
<link href="https://fonts.googleapis.com/css2?family=
|
|
25
|
+
<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">
|
|
26
26
|
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.5.1/dist/chart.umd.min.js"></script>
|
|
27
27
|
<style>
|
|
28
|
+
*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
|
|
29
|
+
|
|
28
30
|
:root {
|
|
29
31
|
--bg-root: #09090b;
|
|
30
32
|
--bg-panel: #18181b;
|
|
31
|
-
--bg-
|
|
33
|
+
--bg-terminal: #09090b;
|
|
32
34
|
--border: #27272a;
|
|
33
|
-
--
|
|
34
|
-
--text-
|
|
35
|
-
--text-
|
|
36
|
-
--text-
|
|
37
|
-
--text-dim: #52525b;
|
|
35
|
+
--text-primary: #ffffff;
|
|
36
|
+
--text-secondary: #52525b;
|
|
37
|
+
--text-tertiary: #a1a1aa;
|
|
38
|
+
--text-value: #d4d4d8;
|
|
38
39
|
--accent: #22c55e;
|
|
39
|
-
--accent-dim: #166534;
|
|
40
40
|
--warning: #eab308;
|
|
41
|
-
--warning-dim: #854d0e;
|
|
42
41
|
--danger: #ef4444;
|
|
43
|
-
--
|
|
44
|
-
--blue: #3b82f6;
|
|
45
|
-
--font-sans: 'IBM Plex Sans', system-ui, sans-serif;
|
|
46
|
-
--font-mono: 'JetBrains Mono', monospace;
|
|
42
|
+
--info: #71717a;
|
|
47
43
|
}
|
|
48
44
|
|
|
49
|
-
|
|
45
|
+
html { scroll-behavior: smooth; }
|
|
50
46
|
|
|
51
47
|
body {
|
|
52
48
|
background: var(--bg-root);
|
|
53
49
|
color: var(--text-primary);
|
|
54
|
-
font-family:
|
|
55
|
-
font-
|
|
50
|
+
font-family: 'Inter', system-ui, sans-serif;
|
|
51
|
+
-webkit-font-smoothing: antialiased;
|
|
56
52
|
line-height: 1.5;
|
|
57
|
-
|
|
53
|
+
min-height: 100dvh;
|
|
54
|
+
overflow-y: auto;
|
|
58
55
|
}
|
|
59
56
|
|
|
60
|
-
/*
|
|
57
|
+
/* -- Header -- */
|
|
61
58
|
.header {
|
|
62
59
|
position: sticky;
|
|
63
60
|
top: 0;
|
|
64
|
-
z-index:
|
|
65
|
-
background: var(--bg-
|
|
61
|
+
z-index: 50;
|
|
62
|
+
background: var(--bg-panel);
|
|
66
63
|
border-bottom: 1px solid var(--border);
|
|
67
64
|
padding: 12px 24px;
|
|
68
65
|
display: flex;
|
|
69
66
|
align-items: center;
|
|
70
|
-
|
|
71
|
-
|
|
67
|
+
justify-content: space-between;
|
|
68
|
+
gap: 16px;
|
|
72
69
|
}
|
|
73
70
|
|
|
74
|
-
.header-
|
|
75
|
-
|
|
76
|
-
|
|
71
|
+
.header-left {
|
|
72
|
+
display: flex;
|
|
73
|
+
align-items: center;
|
|
74
|
+
gap: 10px;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.header-logo {
|
|
78
|
+
font-family: 'JetBrains Mono', monospace;
|
|
79
|
+
font-size: 15px;
|
|
80
|
+
font-weight: 500;
|
|
77
81
|
color: var(--text-primary);
|
|
78
|
-
white-space: nowrap;
|
|
79
82
|
}
|
|
80
83
|
|
|
81
|
-
.header-
|
|
84
|
+
.header-version {
|
|
85
|
+
font-family: 'JetBrains Mono', monospace;
|
|
86
|
+
font-size: 11px;
|
|
87
|
+
color: var(--text-secondary);
|
|
88
|
+
background: var(--bg-root);
|
|
89
|
+
padding: 2px 8px;
|
|
90
|
+
border-radius: 4px;
|
|
91
|
+
}
|
|
82
92
|
|
|
83
|
-
.
|
|
93
|
+
.header-right {
|
|
84
94
|
display: flex;
|
|
85
|
-
gap: 20px;
|
|
86
|
-
flex-wrap: wrap;
|
|
87
95
|
align-items: center;
|
|
88
|
-
|
|
96
|
+
gap: 20px;
|
|
89
97
|
}
|
|
90
98
|
|
|
91
|
-
.kpi {
|
|
99
|
+
.kpi-pill {
|
|
92
100
|
display: flex;
|
|
93
101
|
align-items: center;
|
|
94
102
|
gap: 6px;
|
|
95
|
-
font-size:
|
|
103
|
+
font-size: 12px;
|
|
104
|
+
font-family: 'JetBrains Mono', monospace;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
.kpi-pill .label {
|
|
96
108
|
color: var(--text-secondary);
|
|
109
|
+
text-transform: uppercase;
|
|
110
|
+
letter-spacing: 0.05em;
|
|
97
111
|
}
|
|
98
112
|
|
|
99
|
-
.kpi-value {
|
|
100
|
-
|
|
101
|
-
font-
|
|
102
|
-
color: var(--text-primary);
|
|
103
|
-
font-size: 13px;
|
|
113
|
+
.kpi-pill .value {
|
|
114
|
+
color: var(--text-value);
|
|
115
|
+
font-variant-numeric: tabular-nums;
|
|
104
116
|
}
|
|
105
117
|
|
|
106
|
-
.kpi-value.health-good { color: var(--accent); }
|
|
107
|
-
.kpi-value.health-warn { color: var(--warning); }
|
|
108
|
-
.kpi-value.health-bad { color: var(--danger); }
|
|
118
|
+
.kpi-pill .value.health-good { color: var(--accent); }
|
|
119
|
+
.kpi-pill .value.health-warn { color: var(--warning); }
|
|
120
|
+
.kpi-pill .value.health-bad { color: var(--danger); }
|
|
109
121
|
|
|
110
122
|
.live-dot {
|
|
111
|
-
width:
|
|
112
|
-
height:
|
|
123
|
+
width: 6px;
|
|
124
|
+
height: 6px;
|
|
113
125
|
border-radius: 50%;
|
|
114
126
|
background: var(--accent);
|
|
127
|
+
flex-shrink: 0;
|
|
115
128
|
animation: pulse 2s ease-in-out infinite;
|
|
116
129
|
}
|
|
117
130
|
|
|
@@ -125,82 +138,159 @@ function getDashboardHtml() {
|
|
|
125
138
|
50% { opacity: 0.4; }
|
|
126
139
|
}
|
|
127
140
|
|
|
128
|
-
/*
|
|
129
|
-
.
|
|
141
|
+
/* -- Advisor Banner -- */
|
|
142
|
+
.advisor-banner {
|
|
143
|
+
display: none;
|
|
144
|
+
background: rgba(234,179,8,0.08);
|
|
145
|
+
border-bottom: 1px solid rgba(234,179,8,0.2);
|
|
146
|
+
padding: 8px 24px;
|
|
147
|
+
font-family: 'JetBrains Mono', monospace;
|
|
148
|
+
font-size: 11px;
|
|
149
|
+
color: var(--warning);
|
|
150
|
+
text-align: center;
|
|
151
|
+
letter-spacing: 0.03em;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
.advisor-mode .advisor-banner { display: block; }
|
|
155
|
+
|
|
156
|
+
/* -- Advisor-specific: pending pill -- */
|
|
157
|
+
.pending-pill {
|
|
158
|
+
display: none;
|
|
159
|
+
background: rgba(234,179,8,0.15);
|
|
160
|
+
border-radius: 4px;
|
|
161
|
+
padding: 2px 8px;
|
|
162
|
+
cursor: pointer;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
.pending-pill:hover { background: rgba(234,179,8,0.25); }
|
|
166
|
+
|
|
167
|
+
.advisor-mode .pending-pill { display: flex; }
|
|
168
|
+
|
|
169
|
+
/* -- Mode value color switching -- */
|
|
170
|
+
.mode-value-auto { color: var(--accent); }
|
|
171
|
+
.mode-value-advisor { color: var(--warning); }
|
|
172
|
+
|
|
173
|
+
/* -- Layout -- */
|
|
174
|
+
.dashboard {
|
|
130
175
|
max-width: 1440px;
|
|
131
176
|
margin: 0 auto;
|
|
132
|
-
padding:
|
|
177
|
+
padding: 16px;
|
|
133
178
|
display: flex;
|
|
134
179
|
flex-direction: column;
|
|
135
|
-
gap:
|
|
180
|
+
gap: 12px;
|
|
136
181
|
}
|
|
137
182
|
|
|
183
|
+
/* -- Panels -- */
|
|
138
184
|
.panel {
|
|
139
185
|
background: var(--bg-panel);
|
|
140
186
|
border: 1px solid var(--border);
|
|
141
187
|
border-radius: 8px;
|
|
142
|
-
padding: 16px;
|
|
188
|
+
padding: 16px 20px;
|
|
189
|
+
min-width: 0;
|
|
190
|
+
display: flex;
|
|
191
|
+
flex-direction: column;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
.panel-header {
|
|
195
|
+
display: flex;
|
|
196
|
+
align-items: center;
|
|
197
|
+
justify-content: space-between;
|
|
198
|
+
margin-bottom: 12px;
|
|
199
|
+
flex-shrink: 0;
|
|
143
200
|
}
|
|
144
201
|
|
|
145
202
|
.panel-title {
|
|
146
|
-
font-size:
|
|
203
|
+
font-size: 11px;
|
|
147
204
|
font-weight: 600;
|
|
148
|
-
color: var(--text-secondary);
|
|
149
205
|
text-transform: uppercase;
|
|
150
|
-
letter-spacing: 0.
|
|
151
|
-
|
|
206
|
+
letter-spacing: 0.08em;
|
|
207
|
+
color: var(--info);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
.panel-meta {
|
|
211
|
+
font-size: 11px;
|
|
212
|
+
font-family: 'JetBrains Mono', monospace;
|
|
213
|
+
color: var(--text-secondary);
|
|
214
|
+
display: flex;
|
|
215
|
+
align-items: center;
|
|
216
|
+
gap: 8px;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
.panel-meta .live-label {
|
|
220
|
+
color: var(--accent);
|
|
221
|
+
display: flex;
|
|
222
|
+
align-items: center;
|
|
223
|
+
gap: 4px;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
.panel-body {
|
|
227
|
+
flex: 1;
|
|
228
|
+
min-height: 0;
|
|
229
|
+
overflow: hidden;
|
|
152
230
|
}
|
|
153
231
|
|
|
154
|
-
/*
|
|
155
|
-
.charts-
|
|
232
|
+
/* -- Health Charts -- */
|
|
233
|
+
.charts-panel .chart-row {
|
|
156
234
|
display: grid;
|
|
157
|
-
grid-template-columns: repeat(
|
|
235
|
+
grid-template-columns: repeat(4, 1fr);
|
|
158
236
|
gap: 16px;
|
|
159
237
|
}
|
|
160
238
|
|
|
161
|
-
.chart
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
239
|
+
.mini-chart { position: relative; }
|
|
240
|
+
.mini-chart-label {
|
|
241
|
+
font-size: 10px;
|
|
242
|
+
font-weight: 600;
|
|
243
|
+
text-transform: uppercase;
|
|
244
|
+
letter-spacing: 0.06em;
|
|
245
|
+
color: var(--text-secondary);
|
|
246
|
+
margin-bottom: 4px;
|
|
166
247
|
}
|
|
248
|
+
.mini-chart canvas { width: 100% !important; height: 64px !important; }
|
|
167
249
|
|
|
168
|
-
|
|
250
|
+
@media (max-width: 900px) {
|
|
251
|
+
.charts-panel .chart-row { grid-template-columns: 1fr 1fr; }
|
|
252
|
+
}
|
|
169
253
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
color: var(--text-muted);
|
|
173
|
-
font-weight: 500;
|
|
174
|
-
margin-bottom: 8px;
|
|
254
|
+
@media (max-width: 500px) {
|
|
255
|
+
.charts-panel .chart-row { grid-template-columns: 1fr; }
|
|
175
256
|
}
|
|
176
257
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
258
|
+
/* -- Terminal Feed -- */
|
|
259
|
+
.terminal-panel {
|
|
260
|
+
border-left: 2px solid var(--accent);
|
|
261
|
+
height: 380px;
|
|
262
|
+
overflow: hidden;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
.advisor-mode .terminal-panel {
|
|
266
|
+
border-left-color: var(--warning);
|
|
183
267
|
}
|
|
184
268
|
|
|
185
|
-
/* \u2500\u2500 Terminal (Decision Feed) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
|
|
186
269
|
.terminal {
|
|
187
|
-
background: var(--bg-
|
|
188
|
-
border:
|
|
189
|
-
|
|
190
|
-
height:
|
|
270
|
+
background: var(--bg-terminal);
|
|
271
|
+
border-radius: 6px;
|
|
272
|
+
padding: 12px 16px;
|
|
273
|
+
height: 100%;
|
|
191
274
|
overflow-y: auto;
|
|
192
|
-
font-family:
|
|
193
|
-
font-size:
|
|
275
|
+
font-family: 'JetBrains Mono', monospace;
|
|
276
|
+
font-size: 12.5px;
|
|
194
277
|
line-height: 1.7;
|
|
195
|
-
|
|
278
|
+
display: flex;
|
|
279
|
+
flex-direction: column;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
.terminal-inner {
|
|
283
|
+
margin-top: auto;
|
|
196
284
|
}
|
|
197
285
|
|
|
198
|
-
.terminal::-webkit-scrollbar { width:
|
|
286
|
+
.terminal::-webkit-scrollbar { width: 4px; }
|
|
199
287
|
.terminal::-webkit-scrollbar-track { background: transparent; }
|
|
200
|
-
.terminal::-webkit-scrollbar-thumb { background: var(--border
|
|
288
|
+
.terminal::-webkit-scrollbar-thumb { background: var(--border); border-radius: 2px; }
|
|
201
289
|
|
|
202
290
|
.term-line {
|
|
203
291
|
white-space: nowrap;
|
|
292
|
+
overflow: hidden;
|
|
293
|
+
text-overflow: ellipsis;
|
|
204
294
|
opacity: 0;
|
|
205
295
|
transform: translateY(4px);
|
|
206
296
|
animation: termIn 0.3s ease-out forwards;
|
|
@@ -210,120 +300,197 @@ function getDashboardHtml() {
|
|
|
210
300
|
to { opacity: 1; transform: translateY(0); }
|
|
211
301
|
}
|
|
212
302
|
|
|
213
|
-
.t-tick { color: var(--text-
|
|
303
|
+
.t-tick { color: var(--text-secondary); }
|
|
214
304
|
.t-ok { color: var(--accent); }
|
|
305
|
+
.t-check { color: var(--accent); }
|
|
215
306
|
.t-skip { color: var(--warning); }
|
|
216
307
|
.t-fail { color: var(--danger); }
|
|
217
308
|
.t-principle { color: var(--text-primary); font-weight: 500; }
|
|
218
|
-
.t-param { color: var(--text-
|
|
219
|
-
.t-old { color:
|
|
220
|
-
.t-arrow { color: var(--
|
|
309
|
+
.t-param { color: var(--text-tertiary); }
|
|
310
|
+
.t-old { color: var(--text-value); font-variant-numeric: tabular-nums; }
|
|
311
|
+
.t-arrow { color: var(--info); }
|
|
221
312
|
.t-new { color: var(--accent); font-variant-numeric: tabular-nums; }
|
|
222
|
-
.t-meta { color: var(--text-
|
|
313
|
+
.t-meta { color: var(--text-secondary); }
|
|
314
|
+
.t-violation-id { color: var(--warning); }
|
|
315
|
+
.t-violation-desc { color: var(--text-tertiary); }
|
|
316
|
+
.t-status-label { color: var(--text-tertiary); }
|
|
317
|
+
.t-status-value { color: var(--accent); font-variant-numeric: tabular-nums; }
|
|
318
|
+
.t-dim { color: var(--text-secondary); }
|
|
319
|
+
.t-white { color: var(--text-primary); }
|
|
320
|
+
.t-separator { color: var(--info); }
|
|
321
|
+
.t-pending-icon { color: var(--warning); }
|
|
322
|
+
.t-pending-val { color: var(--warning); font-variant-numeric: tabular-nums; }
|
|
323
|
+
|
|
324
|
+
/* -- Advisor Inline Buttons -- */
|
|
325
|
+
.advisor-btn {
|
|
326
|
+
display: none;
|
|
327
|
+
font-family: 'JetBrains Mono', monospace;
|
|
328
|
+
font-size: 10px;
|
|
329
|
+
border-radius: 3px;
|
|
330
|
+
padding: 2px 8px;
|
|
331
|
+
cursor: pointer;
|
|
332
|
+
border: 1px solid;
|
|
333
|
+
margin-left: 6px;
|
|
334
|
+
vertical-align: middle;
|
|
335
|
+
line-height: 1.4;
|
|
336
|
+
transition: background 0.15s;
|
|
337
|
+
}
|
|
223
338
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
overflow-y: auto;
|
|
339
|
+
.advisor-mode .advisor-btn { display: inline-flex; align-items: center; }
|
|
340
|
+
|
|
341
|
+
.advisor-btn.approve-btn {
|
|
342
|
+
background: rgba(34,197,94,0.15);
|
|
343
|
+
color: var(--accent);
|
|
344
|
+
border-color: rgba(34,197,94,0.3);
|
|
231
345
|
}
|
|
346
|
+
.advisor-btn.approve-btn:hover { background: rgba(34,197,94,0.25); }
|
|
232
347
|
|
|
233
|
-
.
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
padding: 12px;
|
|
238
|
-
border-radius: 6px;
|
|
239
|
-
border: 1px solid var(--border);
|
|
240
|
-
background: var(--bg-panel);
|
|
241
|
-
transition: opacity 0.3s, transform 0.3s;
|
|
348
|
+
.advisor-btn.reject-btn {
|
|
349
|
+
background: rgba(239,68,68,0.1);
|
|
350
|
+
color: var(--danger);
|
|
351
|
+
border-color: rgba(239,68,68,0.2);
|
|
242
352
|
}
|
|
353
|
+
.advisor-btn.reject-btn:hover { background: rgba(239,68,68,0.2); }
|
|
243
354
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
355
|
+
/* Approved/Rejected flash labels */
|
|
356
|
+
.action-flash {
|
|
357
|
+
font-family: 'JetBrains Mono', monospace;
|
|
358
|
+
font-size: 10px;
|
|
359
|
+
margin-left: 8px;
|
|
360
|
+
animation: flashIn 0.3s ease-out;
|
|
247
361
|
}
|
|
248
362
|
|
|
249
|
-
.
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
white-space: nowrap;
|
|
363
|
+
.action-flash.approved { color: var(--accent); }
|
|
364
|
+
.action-flash.rejected { color: var(--info); }
|
|
365
|
+
|
|
366
|
+
@keyframes flashIn {
|
|
367
|
+
from { opacity: 0; transform: translateX(-4px); }
|
|
368
|
+
to { opacity: 1; transform: translateX(0); }
|
|
256
369
|
}
|
|
257
370
|
|
|
258
|
-
|
|
259
|
-
.
|
|
260
|
-
.sev-low { background: var(--accent-dim); color: var(--accent); }
|
|
371
|
+
/* Dimmed line after rejection */
|
|
372
|
+
.term-line.rejected-line { opacity: 0.5; }
|
|
261
373
|
|
|
262
|
-
|
|
263
|
-
.alert-
|
|
264
|
-
|
|
374
|
+
/* -- Alerts -- */
|
|
375
|
+
.alert-card {
|
|
376
|
+
background: var(--bg-root);
|
|
377
|
+
border-radius: 6px;
|
|
378
|
+
padding: 10px 14px;
|
|
379
|
+
margin-bottom: 8px;
|
|
380
|
+
border-left: 3px solid transparent;
|
|
381
|
+
overflow: hidden;
|
|
382
|
+
}
|
|
265
383
|
|
|
266
|
-
|
|
267
|
-
.
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
384
|
+
.alert-card.sev-high { border-left-color: var(--danger); }
|
|
385
|
+
.alert-card.sev-med { border-left-color: var(--warning); }
|
|
386
|
+
.alert-card.sev-low { border-left-color: var(--accent); }
|
|
387
|
+
|
|
388
|
+
.alert-top {
|
|
389
|
+
display: flex;
|
|
390
|
+
align-items: center;
|
|
391
|
+
gap: 8px;
|
|
392
|
+
margin-bottom: 4px;
|
|
271
393
|
}
|
|
272
394
|
|
|
273
|
-
.
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
395
|
+
.sev-badge {
|
|
396
|
+
width: 20px;
|
|
397
|
+
height: 20px;
|
|
398
|
+
border-radius: 50%;
|
|
399
|
+
display: flex;
|
|
400
|
+
align-items: center;
|
|
401
|
+
justify-content: center;
|
|
402
|
+
font-size: 10px;
|
|
403
|
+
font-weight: 700;
|
|
404
|
+
font-family: 'JetBrains Mono', monospace;
|
|
405
|
+
color: var(--bg-root);
|
|
406
|
+
flex-shrink: 0;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
.sev-badge.high { background: var(--danger); }
|
|
410
|
+
.sev-badge.med { background: var(--warning); }
|
|
411
|
+
.sev-badge.low { background: var(--accent); }
|
|
412
|
+
|
|
413
|
+
.alert-principle-id { color: var(--warning); font-family: 'JetBrains Mono', monospace; font-size: 11px; }
|
|
414
|
+
.alert-principle-name { color: var(--text-primary); font-size: 12px; font-weight: 500; }
|
|
415
|
+
.alert-evidence { color: var(--text-tertiary); font-size: 10px; margin-top: 2px; }
|
|
416
|
+
.alert-suggestion { color: var(--accent); font-size: 10px; margin-top: 2px; }
|
|
417
|
+
|
|
418
|
+
.alert-approve-btn {
|
|
419
|
+
display: none;
|
|
420
|
+
font-family: 'JetBrains Mono', monospace;
|
|
421
|
+
font-size: 10px;
|
|
422
|
+
background: rgba(34,197,94,0.15);
|
|
423
|
+
color: var(--accent);
|
|
424
|
+
border: 1px solid rgba(34,197,94,0.3);
|
|
425
|
+
border-radius: 3px;
|
|
426
|
+
padding: 3px 10px;
|
|
279
427
|
cursor: pointer;
|
|
280
|
-
|
|
428
|
+
margin-top: 8px;
|
|
429
|
+
transition: background 0.15s;
|
|
281
430
|
}
|
|
282
431
|
|
|
283
|
-
.
|
|
432
|
+
.alert-approve-btn:hover { background: rgba(34,197,94,0.25); }
|
|
284
433
|
|
|
285
|
-
.
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
434
|
+
.alert-reject-btn {
|
|
435
|
+
display: none;
|
|
436
|
+
font-family: 'JetBrains Mono', monospace;
|
|
437
|
+
font-size: 10px;
|
|
438
|
+
background: rgba(239,68,68,0.1);
|
|
439
|
+
color: var(--danger);
|
|
440
|
+
border: 1px solid rgba(239,68,68,0.2);
|
|
441
|
+
border-radius: 3px;
|
|
442
|
+
padding: 3px 10px;
|
|
443
|
+
cursor: pointer;
|
|
444
|
+
margin-top: 8px;
|
|
445
|
+
margin-left: 6px;
|
|
446
|
+
transition: background 0.15s;
|
|
291
447
|
}
|
|
292
448
|
|
|
293
|
-
.
|
|
449
|
+
.alert-reject-btn:hover { background: rgba(239,68,68,0.2); }
|
|
294
450
|
|
|
295
|
-
|
|
296
|
-
.
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
451
|
+
.advisor-mode .alert-approve-btn { display: inline-block; }
|
|
452
|
+
.advisor-mode .alert-reject-btn { display: inline-block; }
|
|
453
|
+
|
|
454
|
+
/* Alert resolved state */
|
|
455
|
+
.alert-card.resolved {
|
|
456
|
+
opacity: 0;
|
|
457
|
+
max-height: 0;
|
|
458
|
+
padding: 0 14px;
|
|
459
|
+
margin-bottom: 0;
|
|
460
|
+
overflow: hidden;
|
|
461
|
+
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;
|
|
300
462
|
}
|
|
301
463
|
|
|
302
|
-
|
|
303
|
-
|
|
464
|
+
.alerts-scroll {
|
|
465
|
+
overflow-y: auto;
|
|
466
|
+
max-height: 300px;
|
|
304
467
|
}
|
|
305
468
|
|
|
306
|
-
|
|
307
|
-
.
|
|
469
|
+
.alerts-scroll::-webkit-scrollbar { width: 4px; }
|
|
470
|
+
.alerts-scroll::-webkit-scrollbar-track { background: transparent; }
|
|
471
|
+
.alerts-scroll::-webkit-scrollbar-thumb { background: var(--border); border-radius: 2px; }
|
|
308
472
|
|
|
473
|
+
/* -- Persona Bars -- */
|
|
309
474
|
.persona-row {
|
|
310
475
|
display: flex;
|
|
311
476
|
align-items: center;
|
|
312
477
|
gap: 8px;
|
|
313
|
-
|
|
478
|
+
margin-bottom: 5px;
|
|
479
|
+
font-size: 11px;
|
|
480
|
+
font-family: 'JetBrains Mono', monospace;
|
|
314
481
|
}
|
|
315
482
|
|
|
316
483
|
.persona-label {
|
|
317
484
|
width: 100px;
|
|
318
485
|
text-align: right;
|
|
319
|
-
color: var(--text-
|
|
320
|
-
font-size: 11px;
|
|
486
|
+
color: var(--text-tertiary);
|
|
321
487
|
flex-shrink: 0;
|
|
488
|
+
font-variant-numeric: tabular-nums;
|
|
322
489
|
}
|
|
323
490
|
|
|
324
491
|
.persona-bar-track {
|
|
325
492
|
flex: 1;
|
|
326
|
-
height:
|
|
493
|
+
height: 12px;
|
|
327
494
|
background: var(--bg-root);
|
|
328
495
|
border-radius: 3px;
|
|
329
496
|
overflow: hidden;
|
|
@@ -331,166 +498,311 @@ function getDashboardHtml() {
|
|
|
331
498
|
|
|
332
499
|
.persona-bar-fill {
|
|
333
500
|
height: 100%;
|
|
334
|
-
background: var(--accent);
|
|
335
501
|
border-radius: 3px;
|
|
336
|
-
transition: width 0.
|
|
502
|
+
transition: width 0.6s ease-out;
|
|
337
503
|
}
|
|
338
504
|
|
|
339
505
|
.persona-pct {
|
|
340
|
-
width:
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
506
|
+
width: 32px;
|
|
507
|
+
text-align: right;
|
|
508
|
+
color: var(--text-secondary);
|
|
509
|
+
font-variant-numeric: tabular-nums;
|
|
344
510
|
}
|
|
345
511
|
|
|
346
|
-
/*
|
|
347
|
-
.
|
|
348
|
-
|
|
349
|
-
.registry-item {
|
|
512
|
+
/* -- Parameter Registry -- */
|
|
513
|
+
.param-row {
|
|
350
514
|
display: flex;
|
|
351
|
-
justify-content: space-between;
|
|
352
515
|
align-items: center;
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
516
|
+
justify-content: space-between;
|
|
517
|
+
padding: 6px 0;
|
|
518
|
+
border-bottom: 1px solid rgba(39,39,42,0.4);
|
|
519
|
+
font-family: 'JetBrains Mono', monospace;
|
|
520
|
+
font-size: 11px;
|
|
356
521
|
}
|
|
357
522
|
|
|
358
|
-
.
|
|
359
|
-
.registry-key { color: var(--text-secondary); font-family: var(--font-mono); }
|
|
360
|
-
.registry-val { color: var(--accent); font-family: var(--font-mono); font-weight: 500; }
|
|
523
|
+
.param-row:last-child { border-bottom: none; }
|
|
361
524
|
|
|
362
|
-
|
|
363
|
-
|
|
525
|
+
.param-name {
|
|
526
|
+
color: var(--text-tertiary);
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
.param-val {
|
|
530
|
+
color: var(--accent);
|
|
531
|
+
font-variant-numeric: tabular-nums;
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
.param-changed {
|
|
535
|
+
color: var(--text-secondary);
|
|
536
|
+
font-size: 9px;
|
|
537
|
+
margin-left: 6px;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
/* Ghost preview for pending recommendations */
|
|
541
|
+
.param-ghost {
|
|
364
542
|
display: none;
|
|
365
|
-
background: var(--warning-dim);
|
|
366
|
-
border: 1px solid var(--warning);
|
|
367
543
|
color: var(--warning);
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
font-
|
|
371
|
-
font-weight: 500;
|
|
372
|
-
align-items: center;
|
|
373
|
-
gap: 8px;
|
|
544
|
+
font-size: 9px;
|
|
545
|
+
margin-left: 4px;
|
|
546
|
+
font-variant-numeric: tabular-nums;
|
|
374
547
|
}
|
|
375
548
|
|
|
376
|
-
.advisor-mode .
|
|
549
|
+
.advisor-mode .param-ghost { display: inline; }
|
|
377
550
|
|
|
378
|
-
.pending-
|
|
379
|
-
|
|
380
|
-
color: var(--
|
|
551
|
+
.param-pending-label {
|
|
552
|
+
display: none;
|
|
553
|
+
color: var(--warning);
|
|
554
|
+
font-size: 9px;
|
|
555
|
+
margin-left: 6px;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
.advisor-mode .param-pending-label { display: inline; }
|
|
559
|
+
|
|
560
|
+
.params-scroll {
|
|
561
|
+
overflow-y: auto;
|
|
562
|
+
max-height: 300px;
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
.params-scroll::-webkit-scrollbar { width: 4px; }
|
|
566
|
+
.params-scroll::-webkit-scrollbar-track { background: transparent; }
|
|
567
|
+
.params-scroll::-webkit-scrollbar-thumb { background: var(--border); border-radius: 2px; }
|
|
568
|
+
|
|
569
|
+
/* -- Violations Table -- */
|
|
570
|
+
.violations-table {
|
|
571
|
+
width: 100%;
|
|
572
|
+
border-collapse: collapse;
|
|
381
573
|
font-size: 11px;
|
|
574
|
+
font-family: 'JetBrains Mono', monospace;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
.violations-table thead th {
|
|
578
|
+
text-align: left;
|
|
579
|
+
padding: 7px 8px;
|
|
580
|
+
border-bottom: 1px solid var(--border);
|
|
581
|
+
color: var(--text-secondary);
|
|
382
582
|
font-weight: 600;
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
583
|
+
font-size: 10px;
|
|
584
|
+
text-transform: uppercase;
|
|
585
|
+
letter-spacing: 0.06em;
|
|
586
|
+
cursor: pointer;
|
|
587
|
+
user-select: none;
|
|
588
|
+
white-space: nowrap;
|
|
589
|
+
position: sticky;
|
|
590
|
+
top: 0;
|
|
591
|
+
background: var(--bg-panel);
|
|
386
592
|
}
|
|
387
593
|
|
|
388
|
-
.
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
padding:
|
|
594
|
+
.violations-table thead th:hover { color: var(--text-tertiary); }
|
|
595
|
+
|
|
596
|
+
.violations-table tbody td {
|
|
597
|
+
padding: 6px 8px;
|
|
598
|
+
border-bottom: 1px solid rgba(39,39,42,0.4);
|
|
599
|
+
color: var(--text-value);
|
|
600
|
+
font-variant-numeric: tabular-nums;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
.violations-table tbody tr:hover td { background: rgba(39,39,42,0.3); }
|
|
604
|
+
|
|
605
|
+
.table-scroll {
|
|
606
|
+
overflow-y: auto;
|
|
607
|
+
max-height: 240px;
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
.table-scroll::-webkit-scrollbar { width: 4px; }
|
|
611
|
+
.table-scroll::-webkit-scrollbar-track { background: transparent; }
|
|
612
|
+
.table-scroll::-webkit-scrollbar-thumb { background: var(--border); border-radius: 2px; }
|
|
613
|
+
|
|
614
|
+
.badge-applied { color: var(--accent); font-size: 10px; }
|
|
615
|
+
.badge-skipped { color: var(--text-secondary); font-size: 10px; }
|
|
616
|
+
.badge-rejected { color: var(--danger); font-size: 10px; opacity: 0.6; }
|
|
617
|
+
|
|
618
|
+
/* Pending badge in violations table (advisor mode) */
|
|
619
|
+
.badge-pending {
|
|
620
|
+
display: none;
|
|
621
|
+
color: var(--warning);
|
|
622
|
+
font-size: 10px;
|
|
623
|
+
cursor: pointer;
|
|
624
|
+
position: relative;
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
.advisor-mode .badge-pending { display: inline-flex; align-items: center; gap: 4px; }
|
|
628
|
+
|
|
629
|
+
.badge-pending:hover { text-decoration: underline; }
|
|
630
|
+
|
|
631
|
+
/* Pending dropdown in violations table */
|
|
632
|
+
.pending-dropdown {
|
|
633
|
+
display: none;
|
|
634
|
+
position: absolute;
|
|
635
|
+
bottom: 100%;
|
|
636
|
+
left: 0;
|
|
637
|
+
background: var(--bg-panel);
|
|
638
|
+
border: 1px solid var(--border);
|
|
392
639
|
border-radius: 4px;
|
|
393
|
-
|
|
640
|
+
padding: 4px 0;
|
|
641
|
+
z-index: 20;
|
|
642
|
+
min-width: 120px;
|
|
643
|
+
box-shadow: 0 -4px 12px rgba(0,0,0,0.4);
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
.pending-dropdown.open { display: block; }
|
|
647
|
+
|
|
648
|
+
.pending-dropdown-item {
|
|
649
|
+
padding: 5px 12px;
|
|
650
|
+
font-family: 'JetBrains Mono', monospace;
|
|
651
|
+
font-size: 10px;
|
|
394
652
|
cursor: pointer;
|
|
395
|
-
|
|
396
|
-
|
|
653
|
+
display: flex;
|
|
654
|
+
align-items: center;
|
|
655
|
+
gap: 6px;
|
|
656
|
+
white-space: nowrap;
|
|
397
657
|
}
|
|
398
658
|
|
|
399
|
-
.
|
|
400
|
-
.
|
|
401
|
-
.
|
|
659
|
+
.pending-dropdown-item:hover { background: rgba(39,39,42,0.5); }
|
|
660
|
+
.pending-dropdown-item.approve-item { color: var(--accent); }
|
|
661
|
+
.pending-dropdown-item.reject-item { color: var(--danger); }
|
|
402
662
|
|
|
403
|
-
|
|
404
|
-
.
|
|
663
|
+
/* -- Bottom split row -- */
|
|
664
|
+
.split-row {
|
|
665
|
+
display: grid;
|
|
666
|
+
grid-template-columns: 1fr 1fr;
|
|
667
|
+
gap: 12px;
|
|
668
|
+
}
|
|
405
669
|
|
|
406
|
-
/*
|
|
670
|
+
/* -- Empty state -- */
|
|
407
671
|
.empty-state {
|
|
408
|
-
color: var(--text-
|
|
409
|
-
font-
|
|
672
|
+
color: var(--text-secondary);
|
|
673
|
+
font-family: 'JetBrains Mono', monospace;
|
|
674
|
+
font-size: 11px;
|
|
410
675
|
text-align: center;
|
|
411
|
-
padding:
|
|
676
|
+
padding: 20px;
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
/* -- Responsive -- */
|
|
680
|
+
@media (max-width: 768px) {
|
|
681
|
+
.split-row { grid-template-columns: 1fr; }
|
|
682
|
+
.header { flex-direction: column; align-items: flex-start; gap: 8px; }
|
|
683
|
+
.header-right { flex-wrap: wrap; gap: 12px; }
|
|
684
|
+
.dashboard { padding: 8px; gap: 8px; }
|
|
412
685
|
}
|
|
413
686
|
|
|
414
|
-
/*
|
|
687
|
+
/* -- Reduced motion -- */
|
|
415
688
|
@media (prefers-reduced-motion: reduce) {
|
|
416
689
|
.term-line { animation: none; opacity: 1; transform: none; }
|
|
417
690
|
.live-dot { animation: none; }
|
|
418
691
|
.persona-bar-fill { transition: none; }
|
|
692
|
+
.alert-card.resolved { transition: none; }
|
|
419
693
|
}
|
|
420
694
|
</style>
|
|
421
695
|
</head>
|
|
422
696
|
<body>
|
|
423
697
|
|
|
424
698
|
<!-- Header -->
|
|
425
|
-
<
|
|
426
|
-
<div class="header-
|
|
427
|
-
|
|
428
|
-
<
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
<div class="kpi
|
|
432
|
-
|
|
433
|
-
|
|
699
|
+
<header class="header" id="header">
|
|
700
|
+
<div class="header-left">
|
|
701
|
+
<span class="header-logo">AgentE</span>
|
|
702
|
+
<span class="header-version">v1.7.2</span>
|
|
703
|
+
</div>
|
|
704
|
+
<div class="header-right">
|
|
705
|
+
<div class="kpi-pill">
|
|
706
|
+
<span class="label">Health</span>
|
|
707
|
+
<span class="value health-good" id="h-health">--</span>
|
|
708
|
+
</div>
|
|
709
|
+
<div class="kpi-pill">
|
|
710
|
+
<span class="label">Mode</span>
|
|
711
|
+
<span class="value mode-value-auto" id="h-mode">--</span>
|
|
712
|
+
</div>
|
|
713
|
+
<div class="kpi-pill">
|
|
714
|
+
<span class="label">Tick</span>
|
|
715
|
+
<span class="value" id="h-tick">0</span>
|
|
716
|
+
</div>
|
|
717
|
+
<div class="kpi-pill pending-pill" id="pending-pill">
|
|
718
|
+
<span class="label" style="color: var(--warning);">Pending</span>
|
|
719
|
+
<span class="value" style="color: var(--warning);" id="h-pending">0</span>
|
|
720
|
+
</div>
|
|
721
|
+
<div class="kpi-pill">
|
|
722
|
+
<span class="label">Uptime</span>
|
|
723
|
+
<span class="value" id="h-uptime">0s</span>
|
|
724
|
+
</div>
|
|
725
|
+
<div class="kpi-pill">
|
|
726
|
+
<div class="live-dot" id="live-dot" title="WebSocket connected"></div>
|
|
727
|
+
<span style="color: var(--accent); font-size: 11px;">LIVE</span>
|
|
728
|
+
</div>
|
|
434
729
|
</div>
|
|
730
|
+
</header>
|
|
731
|
+
|
|
732
|
+
<!-- Advisor Banner -->
|
|
733
|
+
<div class="advisor-banner" id="advisor-banner">
|
|
734
|
+
ADVISOR MODE \\u2014 AgentE is waiting for your approval before applying changes
|
|
435
735
|
</div>
|
|
436
736
|
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
<div class="advisor-banner" id="advisor-banner">
|
|
440
|
-
ADVISOR MODE \u2014 Recommendations require manual approval
|
|
441
|
-
<span class="pending-pill" id="pending-count">0</span> pending
|
|
442
|
-
</div>
|
|
737
|
+
<!-- Dashboard -->
|
|
738
|
+
<main class="dashboard" id="dashboard-root">
|
|
443
739
|
|
|
444
|
-
<!--
|
|
445
|
-
<div class="charts-
|
|
446
|
-
<div class="
|
|
447
|
-
<
|
|
448
|
-
<div class="chart-value" id="cv-health">--</div>
|
|
449
|
-
<canvas id="chart-health"></canvas>
|
|
740
|
+
<!-- Health & Metrics -->
|
|
741
|
+
<div class="panel charts-panel">
|
|
742
|
+
<div class="panel-header">
|
|
743
|
+
<span class="panel-title">Health & Metrics</span>
|
|
450
744
|
</div>
|
|
451
|
-
<div class="chart-
|
|
452
|
-
<div class="chart
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
<
|
|
745
|
+
<div class="chart-row">
|
|
746
|
+
<div class="mini-chart">
|
|
747
|
+
<div class="mini-chart-label">Health Score</div>
|
|
748
|
+
<canvas id="chart-health"></canvas>
|
|
749
|
+
</div>
|
|
750
|
+
<div class="mini-chart">
|
|
751
|
+
<div class="mini-chart-label">Gini Coefficient</div>
|
|
752
|
+
<canvas id="chart-gini"></canvas>
|
|
753
|
+
</div>
|
|
754
|
+
<div class="mini-chart">
|
|
755
|
+
<div class="mini-chart-label">Net Flow</div>
|
|
756
|
+
<canvas id="chart-flow"></canvas>
|
|
757
|
+
</div>
|
|
758
|
+
<div class="mini-chart">
|
|
759
|
+
<div class="mini-chart-label">Avg Satisfaction</div>
|
|
760
|
+
<canvas id="chart-satisfaction"></canvas>
|
|
761
|
+
</div>
|
|
465
762
|
</div>
|
|
466
763
|
</div>
|
|
467
764
|
|
|
468
765
|
<!-- Decision Feed -->
|
|
469
|
-
<div class="panel">
|
|
470
|
-
<div class="panel-
|
|
471
|
-
|
|
766
|
+
<div class="panel terminal-panel">
|
|
767
|
+
<div class="panel-header">
|
|
768
|
+
<span class="panel-title">Decision Feed</span>
|
|
769
|
+
<div class="panel-meta">
|
|
770
|
+
<span id="decision-count">0 decisions</span>
|
|
771
|
+
<span class="live-label"><div class="live-dot" style="width:5px;height:5px;"></div> LIVE</span>
|
|
772
|
+
</div>
|
|
773
|
+
</div>
|
|
774
|
+
<div class="panel-body">
|
|
775
|
+
<div class="terminal" id="terminal">
|
|
776
|
+
<div id="terminal-inner"></div>
|
|
777
|
+
</div>
|
|
778
|
+
</div>
|
|
472
779
|
</div>
|
|
473
780
|
|
|
474
781
|
<!-- Active Alerts -->
|
|
475
|
-
<div class="panel">
|
|
476
|
-
<div class="panel-
|
|
477
|
-
|
|
478
|
-
<
|
|
782
|
+
<div class="panel alerts-panel">
|
|
783
|
+
<div class="panel-header">
|
|
784
|
+
<span class="panel-title">Active Alerts</span>
|
|
785
|
+
<span class="panel-meta" id="alerts-count">All clear</span>
|
|
479
786
|
</div>
|
|
787
|
+
<div class="alerts-scroll" id="alerts"></div>
|
|
480
788
|
</div>
|
|
481
789
|
|
|
482
790
|
<!-- Violation History -->
|
|
483
791
|
<div class="panel">
|
|
484
|
-
<div class="panel-
|
|
485
|
-
|
|
792
|
+
<div class="panel-header">
|
|
793
|
+
<span class="panel-title">Violation History</span>
|
|
794
|
+
<span class="panel-meta">Last 100 decisions</span>
|
|
795
|
+
</div>
|
|
796
|
+
<div class="table-scroll">
|
|
486
797
|
<table class="violations-table" id="violations-table">
|
|
487
798
|
<thead>
|
|
488
799
|
<tr>
|
|
489
800
|
<th data-sort="tick">Tick</th>
|
|
490
801
|
<th data-sort="principle">Principle</th>
|
|
491
|
-
<th data-sort="severity">
|
|
802
|
+
<th data-sort="severity">Sev</th>
|
|
492
803
|
<th data-sort="parameter">Parameter</th>
|
|
493
|
-
<th data-sort="result">
|
|
804
|
+
<th data-sort="result">Action</th>
|
|
805
|
+
<th>Change</th>
|
|
494
806
|
</tr>
|
|
495
807
|
</thead>
|
|
496
808
|
<tbody id="violations-body"></tbody>
|
|
@@ -498,69 +810,88 @@ function getDashboardHtml() {
|
|
|
498
810
|
</div>
|
|
499
811
|
</div>
|
|
500
812
|
|
|
501
|
-
<!--
|
|
813
|
+
<!-- Persona Distribution + Parameters -->
|
|
502
814
|
<div class="split-row">
|
|
503
|
-
<div class="panel">
|
|
504
|
-
<div class="panel-
|
|
505
|
-
|
|
815
|
+
<div class="panel persona-panel">
|
|
816
|
+
<div class="panel-header">
|
|
817
|
+
<span class="panel-title">Persona Distribution</span>
|
|
818
|
+
<span class="panel-meta" id="persona-count"></span>
|
|
819
|
+
</div>
|
|
820
|
+
<div id="persona-bars">
|
|
506
821
|
<div class="empty-state">No persona data yet</div>
|
|
507
822
|
</div>
|
|
508
823
|
</div>
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
<div class="
|
|
824
|
+
|
|
825
|
+
<div class="panel params-panel">
|
|
826
|
+
<div class="panel-header">
|
|
827
|
+
<span class="panel-title">Parameters</span>
|
|
828
|
+
<span class="panel-meta" id="params-count"></span>
|
|
829
|
+
</div>
|
|
830
|
+
<div class="params-scroll" id="params-list">
|
|
512
831
|
<div class="empty-state">No parameters registered</div>
|
|
513
832
|
</div>
|
|
514
833
|
</div>
|
|
515
834
|
</div>
|
|
516
|
-
|
|
835
|
+
|
|
836
|
+
</main>
|
|
517
837
|
|
|
518
838
|
<script>
|
|
519
839
|
(function() {
|
|
520
840
|
'use strict';
|
|
521
841
|
|
|
522
|
-
//
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
842
|
+
// -- State --
|
|
843
|
+
var ws = null;
|
|
844
|
+
var reconnectDelay = 1000;
|
|
845
|
+
var MAX_RECONNECT = 30000;
|
|
846
|
+
var isAdvisor = false;
|
|
847
|
+
var pendingDecisions = [];
|
|
848
|
+
var MAX_TERMINAL_LINES = 80;
|
|
849
|
+
var MAX_VIOLATIONS = 100;
|
|
850
|
+
var violationSortKey = 'tick';
|
|
851
|
+
var violationSortAsc = false;
|
|
852
|
+
var violations = [];
|
|
853
|
+
var decisionCount = 0;
|
|
533
854
|
|
|
534
855
|
// Chart instances
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
//
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
856
|
+
var chartHealth, chartGini, chartFlow, chartSatisfaction;
|
|
857
|
+
|
|
858
|
+
// Param state for registry display
|
|
859
|
+
var paramRegistry = [];
|
|
860
|
+
var paramValues = {};
|
|
861
|
+
var paramLastTick = {};
|
|
862
|
+
var paramPending = {};
|
|
863
|
+
var currentTick = 0;
|
|
864
|
+
|
|
865
|
+
// -- DOM refs --
|
|
866
|
+
var $hHealth = document.getElementById('h-health');
|
|
867
|
+
var $hMode = document.getElementById('h-mode');
|
|
868
|
+
var $hTick = document.getElementById('h-tick');
|
|
869
|
+
var $hUptime = document.getElementById('h-uptime');
|
|
870
|
+
var $hPending = document.getElementById('h-pending');
|
|
871
|
+
var $liveDot = document.getElementById('live-dot');
|
|
872
|
+
var $terminal = document.getElementById('terminal');
|
|
873
|
+
var $terminalInner = document.getElementById('terminal-inner');
|
|
874
|
+
var $alerts = document.getElementById('alerts');
|
|
875
|
+
var $alertsCount = document.getElementById('alerts-count');
|
|
876
|
+
var $violationsBody = document.getElementById('violations-body');
|
|
877
|
+
var $personaBars = document.getElementById('persona-bars');
|
|
878
|
+
var $paramsList = document.getElementById('params-list');
|
|
879
|
+
var $paramsCount = document.getElementById('params-count');
|
|
880
|
+
var $personaCount = document.getElementById('persona-count');
|
|
881
|
+
var $decisionCount = document.getElementById('decision-count');
|
|
882
|
+
var $dashboardRoot = document.getElementById('dashboard-root');
|
|
883
|
+
|
|
884
|
+
// -- Helpers --
|
|
554
885
|
function esc(s) { return String(s).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"').replace(/'/g,''').replace(/\\\\/g,'\'); }
|
|
555
886
|
function pad(n, w) { return String(n).padStart(w || 4, ' '); }
|
|
556
|
-
function fmt(n) { return typeof n === 'number' ? n.toFixed(3) : '
|
|
557
|
-
function pct(n) { return typeof n === 'number' ? (n * 100).toFixed(0) + '%' : '
|
|
887
|
+
function fmt(n) { return typeof n === 'number' ? n.toFixed(3) : '\\u2014'; }
|
|
888
|
+
function pct(n) { return typeof n === 'number' ? (n * 100).toFixed(0) + '%' : '\\u2014'; }
|
|
558
889
|
|
|
559
890
|
function formatUptime(ms) {
|
|
560
|
-
|
|
891
|
+
var s = Math.floor(ms / 1000);
|
|
561
892
|
if (s < 60) return s + 's';
|
|
562
893
|
if (s < 3600) return Math.floor(s / 60) + 'm ' + (s % 60) + 's';
|
|
563
|
-
|
|
894
|
+
var h = Math.floor(s / 3600);
|
|
564
895
|
return h + 'h ' + Math.floor((s % 3600) / 60) + 'm';
|
|
565
896
|
}
|
|
566
897
|
|
|
@@ -571,34 +902,63 @@ function getDashboardHtml() {
|
|
|
571
902
|
}
|
|
572
903
|
|
|
573
904
|
function sevClass(s) {
|
|
905
|
+
if (s >= 7) return 'high';
|
|
906
|
+
if (s >= 4) return 'med';
|
|
907
|
+
return 'low';
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
function sevCardClass(s) {
|
|
574
911
|
if (s >= 7) return 'sev-high';
|
|
575
912
|
if (s >= 4) return 'sev-med';
|
|
576
913
|
return 'sev-low';
|
|
577
914
|
}
|
|
578
915
|
|
|
579
|
-
|
|
580
|
-
|
|
916
|
+
function personaColor(name) {
|
|
917
|
+
var n = (name || '').toLowerCase();
|
|
918
|
+
if (n === 'atrisk' || n === 'at_risk' || n === 'dormant') return 'var(--danger)';
|
|
919
|
+
if (n === 'spender' || n === 'newentrant' || n === 'new_entrant' || n === 'passive') return 'var(--warning)';
|
|
920
|
+
return 'var(--accent)';
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
// -- Chart setup --
|
|
924
|
+
var chartOpts = {
|
|
581
925
|
responsive: true,
|
|
582
926
|
maintainAspectRatio: false,
|
|
583
|
-
animation:
|
|
584
|
-
plugins: {
|
|
927
|
+
animation: false,
|
|
928
|
+
plugins: {
|
|
929
|
+
legend: { display: false },
|
|
930
|
+
tooltip: {
|
|
931
|
+
backgroundColor: '#18181b',
|
|
932
|
+
titleColor: '#a1a1aa',
|
|
933
|
+
bodyColor: '#d4d4d8',
|
|
934
|
+
titleFont: { family: 'JetBrains Mono', size: 10 },
|
|
935
|
+
bodyFont: { family: 'JetBrains Mono', size: 11 },
|
|
936
|
+
borderColor: '#27272a',
|
|
937
|
+
borderWidth: 1,
|
|
938
|
+
padding: 8,
|
|
939
|
+
}
|
|
940
|
+
},
|
|
585
941
|
scales: {
|
|
586
942
|
x: { display: false },
|
|
587
943
|
y: {
|
|
588
|
-
|
|
589
|
-
|
|
944
|
+
grid: { color: 'rgba(39,39,42,0.5)', drawBorder: false },
|
|
945
|
+
ticks: {
|
|
946
|
+
color: '#52525b',
|
|
947
|
+
font: { family: 'JetBrains Mono', size: 9 },
|
|
948
|
+
maxTicksLimit: 3,
|
|
949
|
+
},
|
|
590
950
|
border: { display: false },
|
|
591
951
|
}
|
|
592
952
|
},
|
|
593
953
|
elements: {
|
|
594
|
-
point: { radius: 0 },
|
|
954
|
+
point: { radius: 0, hoverRadius: 3, backgroundColor: '#22c55e' },
|
|
595
955
|
line: { borderWidth: 1.5, tension: 0.3 },
|
|
596
956
|
}
|
|
597
957
|
};
|
|
598
958
|
|
|
599
959
|
function makeChart(id, color, minY, maxY) {
|
|
600
|
-
|
|
601
|
-
|
|
960
|
+
var ctx = document.getElementById(id).getContext('2d');
|
|
961
|
+
var opts = JSON.parse(JSON.stringify(chartOpts));
|
|
602
962
|
if (minY !== undefined) opts.scales.y.min = minY;
|
|
603
963
|
if (maxY !== undefined) opts.scales.y.max = maxY;
|
|
604
964
|
return new Chart(ctx, {
|
|
@@ -619,7 +979,7 @@ function getDashboardHtml() {
|
|
|
619
979
|
function initCharts() {
|
|
620
980
|
chartHealth = makeChart('chart-health', '#22c55e', 0, 100);
|
|
621
981
|
chartGini = makeChart('chart-gini', '#eab308', 0, 1);
|
|
622
|
-
|
|
982
|
+
chartFlow = makeChart('chart-flow', '#3b82f6');
|
|
623
983
|
chartSatisfaction = makeChart('chart-satisfaction', '#22c55e', 0, 100);
|
|
624
984
|
}
|
|
625
985
|
|
|
@@ -629,178 +989,278 @@ function getDashboardHtml() {
|
|
|
629
989
|
chart.update('none');
|
|
630
990
|
}
|
|
631
991
|
|
|
632
|
-
//
|
|
992
|
+
// -- Terminal --
|
|
633
993
|
function addTerminalLine(html) {
|
|
634
|
-
|
|
994
|
+
var el = document.createElement('div');
|
|
635
995
|
el.className = 'term-line';
|
|
636
996
|
el.innerHTML = html;
|
|
637
|
-
$
|
|
638
|
-
while ($
|
|
639
|
-
$
|
|
997
|
+
$terminalInner.appendChild(el);
|
|
998
|
+
while ($terminalInner.children.length > MAX_TERMINAL_LINES) {
|
|
999
|
+
$terminalInner.removeChild($terminalInner.firstChild);
|
|
640
1000
|
}
|
|
641
1001
|
$terminal.scrollTop = $terminal.scrollHeight;
|
|
642
1002
|
}
|
|
643
1003
|
|
|
644
1004
|
function decisionToTerminal(d) {
|
|
645
|
-
|
|
646
|
-
? '<span class="t-
|
|
1005
|
+
var resultIcon = d.result === 'applied'
|
|
1006
|
+
? '<span class="t-check">\\u2705 </span>'
|
|
647
1007
|
: d.result === 'rejected'
|
|
648
1008
|
? '<span class="t-fail">\\u274c </span>'
|
|
649
|
-
:
|
|
1009
|
+
: d.result === 'skipped_override'
|
|
1010
|
+
? '<span class="t-pending-icon">\\u23f3 </span>'
|
|
1011
|
+
: '<span class="t-skip">\\u23f8 </span>';
|
|
650
1012
|
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
1013
|
+
var principle = d.diagnosis?.principle || {};
|
|
1014
|
+
var plan = d.plan || {};
|
|
1015
|
+
var severity = d.diagnosis?.violation?.severity ?? '?';
|
|
1016
|
+
var confidence = d.diagnosis?.violation?.confidence;
|
|
1017
|
+
var confStr = confidence != null ? (confidence * 100).toFixed(0) + '%' : '?';
|
|
656
1018
|
|
|
657
|
-
|
|
1019
|
+
var advisorBtns = '';
|
|
658
1020
|
if (isAdvisor && d.result === 'skipped_override') {
|
|
659
|
-
advisorBtns = '<span class="advisor-
|
|
660
|
-
+ '<button class="advisor-btn approve"
|
|
661
|
-
+ '<button class="advisor-btn reject"
|
|
1021
|
+
advisorBtns = '<span class="advisor-btn-group" data-id="' + esc(d.id) + '">'
|
|
1022
|
+
+ '<button class="advisor-btn approve-btn" data-action="approve" data-id="' + esc(d.id) + '">✓ Approve</button>'
|
|
1023
|
+
+ '<button class="advisor-btn reject-btn" data-action="reject" data-id="' + esc(d.id) + '">✕ Reject</button>'
|
|
662
1024
|
+ '</span>';
|
|
663
1025
|
}
|
|
664
1026
|
|
|
665
1027
|
return '<span class="t-tick">[Tick ' + pad(d.tick) + ']</span> '
|
|
666
1028
|
+ resultIcon
|
|
667
|
-
+ '<span class="t-principle">
|
|
668
|
-
+ '<span class="t-param">' + esc(plan.parameter || '
|
|
1029
|
+
+ '<span class="t-principle">' + esc(principle.name || '') + ':</span> '
|
|
1030
|
+
+ '<span class="t-param">' + esc(plan.parameter || '\\u2014') + ' </span>'
|
|
669
1031
|
+ '<span class="t-old">' + fmt(plan.currentValue) + '</span>'
|
|
670
1032
|
+ '<span class="t-arrow"> \\u2192 </span>'
|
|
671
|
-
+
|
|
672
|
-
|
|
1033
|
+
+ (d.result === 'skipped_override'
|
|
1034
|
+
? '<span class="t-pending-val">' + fmt(plan.targetValue) + '</span>'
|
|
1035
|
+
: '<span class="t-new">' + fmt(plan.targetValue) + '</span>')
|
|
1036
|
+
+ '<span class="t-meta"> sev ' + severity + ', conf ' + confStr + '</span>'
|
|
673
1037
|
+ advisorBtns;
|
|
674
1038
|
}
|
|
675
1039
|
|
|
676
|
-
//
|
|
1040
|
+
// -- Alerts --
|
|
677
1041
|
function renderAlerts(alerts) {
|
|
678
1042
|
if (!alerts || alerts.length === 0) {
|
|
679
|
-
$
|
|
1043
|
+
$alerts.innerHTML = '<div class="empty-state">No active violations. Economy is healthy.</div>';
|
|
1044
|
+
$alertsCount.textContent = 'All clear';
|
|
680
1045
|
return;
|
|
681
1046
|
}
|
|
682
|
-
|
|
683
|
-
$
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
1047
|
+
var sorted = alerts.slice().sort(function(a, b) { return (b.severity || 0) - (a.severity || 0); });
|
|
1048
|
+
$alertsCount.textContent = sorted.length + ' violation' + (sorted.length !== 1 ? 's' : '');
|
|
1049
|
+
|
|
1050
|
+
$alerts.innerHTML = sorted.map(function(a) {
|
|
1051
|
+
var sev = a.severity || a.violation?.severity || 0;
|
|
1052
|
+
var sc = sevClass(sev);
|
|
1053
|
+
var cardCls = sevCardClass(sev);
|
|
1054
|
+
var name = a.principleName || a.principle?.name || '?';
|
|
1055
|
+
var pid = a.principleId || a.principle?.id || '?';
|
|
1056
|
+
var reason = a.reasoning || a.violation?.suggestedAction?.reasoning || '';
|
|
1057
|
+
var suggestion = a.suggestion || '';
|
|
1058
|
+
|
|
1059
|
+
var hasPending = isAdvisor && pendingDecisions.some(function(pd) {
|
|
1060
|
+
return pd.principleId === pid || (pd.diagnosis?.principle?.id === pid);
|
|
1061
|
+
});
|
|
1062
|
+
|
|
1063
|
+
var btns = '';
|
|
1064
|
+
if (hasPending) {
|
|
1065
|
+
btns = '<button class="alert-approve-btn" data-action="approve-alert" data-principle="' + esc(pid) + '">✓ Approve Fix</button>'
|
|
1066
|
+
+ '<button class="alert-reject-btn" data-action="reject-alert" data-principle="' + esc(pid) + '">✗ Reject</button>';
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1069
|
+
return '<div class="alert-card ' + cardCls + '" data-principle-id="' + esc(pid) + '">'
|
|
1070
|
+
+ '<div class="alert-top">'
|
|
1071
|
+
+ '<span class="sev-badge ' + sc + '">' + sev + '</span>'
|
|
1072
|
+
+ '<span class="alert-principle-name">[' + esc(pid) + '] ' + esc(name) + '</span>'
|
|
1073
|
+
+ '</div>'
|
|
1074
|
+
+ (reason ? '<div class="alert-evidence">' + esc(reason) + '</div>' : '')
|
|
1075
|
+
+ (suggestion ? '<div class="alert-suggestion">Suggested: ' + esc(suggestion) + '</div>' : '')
|
|
1076
|
+
+ btns
|
|
1077
|
+
+ '</div>';
|
|
695
1078
|
}).join('');
|
|
696
1079
|
}
|
|
697
1080
|
|
|
698
|
-
//
|
|
1081
|
+
// -- Violations table --
|
|
699
1082
|
function addViolation(d) {
|
|
1083
|
+
var plan = d.plan || {};
|
|
700
1084
|
violations.push({
|
|
701
1085
|
tick: d.tick,
|
|
702
1086
|
principle: (d.diagnosis?.principle?.id || '?') + ' ' + (d.diagnosis?.principle?.name || ''),
|
|
703
1087
|
severity: d.diagnosis?.violation?.severity || 0,
|
|
704
|
-
parameter:
|
|
1088
|
+
parameter: plan.parameter || '\\u2014',
|
|
705
1089
|
result: d.result,
|
|
1090
|
+
currentValue: plan.currentValue,
|
|
1091
|
+
targetValue: plan.targetValue,
|
|
1092
|
+
decisionId: d.id,
|
|
706
1093
|
});
|
|
707
1094
|
if (violations.length > MAX_VIOLATIONS) violations.shift();
|
|
1095
|
+
decisionCount = violations.length;
|
|
1096
|
+
$decisionCount.textContent = decisionCount + ' decisions';
|
|
708
1097
|
renderViolations();
|
|
709
1098
|
}
|
|
710
1099
|
|
|
711
1100
|
function renderViolations() {
|
|
712
|
-
|
|
713
|
-
|
|
1101
|
+
var sorted = violations.slice().sort(function(a, b) {
|
|
1102
|
+
var va = a[violationSortKey], vb = b[violationSortKey];
|
|
714
1103
|
if (va < vb) return violationSortAsc ? -1 : 1;
|
|
715
1104
|
if (va > vb) return violationSortAsc ? 1 : -1;
|
|
716
1105
|
return 0;
|
|
717
1106
|
});
|
|
718
1107
|
$violationsBody.innerHTML = sorted.map(function(v) {
|
|
1108
|
+
var isPending = v.result === 'skipped_override';
|
|
1109
|
+
|
|
1110
|
+
var actionHtml;
|
|
1111
|
+
if (isPending && isAdvisor) {
|
|
1112
|
+
actionHtml = '<span class="badge-pending" data-action="toggle-pending" data-id="' + esc(v.decisionId || '') + '">'
|
|
1113
|
+
+ 'Pending \\u25BE'
|
|
1114
|
+
+ '<div class="pending-dropdown">'
|
|
1115
|
+
+ '<div class="pending-dropdown-item approve-item" data-action="approve" data-id="' + esc(v.decisionId || '') + '">✓ Approve</div>'
|
|
1116
|
+
+ '<div class="pending-dropdown-item reject-item" data-action="reject" data-id="' + esc(v.decisionId || '') + '">✗ Reject</div>'
|
|
1117
|
+
+ '</div>'
|
|
1118
|
+
+ '</span>';
|
|
1119
|
+
} else if (v.result === 'applied') {
|
|
1120
|
+
actionHtml = '<span class="badge-applied">Applied</span>';
|
|
1121
|
+
} else if (v.result === 'rejected') {
|
|
1122
|
+
actionHtml = '<span class="badge-rejected">Rejected</span>';
|
|
1123
|
+
} else {
|
|
1124
|
+
actionHtml = '<span class="badge-skipped">' + esc(v.result || 'Skipped') + '</span>';
|
|
1125
|
+
}
|
|
1126
|
+
|
|
1127
|
+
var changeHtml = '';
|
|
1128
|
+
if (v.currentValue != null && v.targetValue != null) {
|
|
1129
|
+
var valColor = isPending && isAdvisor ? 'var(--warning)' : 'var(--accent)';
|
|
1130
|
+
changeHtml = '<span style="color:var(--text-value)">' + fmt(v.currentValue) + '</span>'
|
|
1131
|
+
+ '<span style="color:var(--info)"> \\u2192 </span>'
|
|
1132
|
+
+ '<span style="color:' + valColor + '">' + fmt(v.targetValue) + '</span>';
|
|
1133
|
+
}
|
|
1134
|
+
|
|
1135
|
+
var sevColor = v.severity >= 7 ? 'var(--danger)' : v.severity >= 4 ? 'var(--warning)' : 'var(--accent)';
|
|
1136
|
+
|
|
719
1137
|
return '<tr>'
|
|
720
|
-
+ '<td>' + v.tick + '</td>'
|
|
721
|
-
+ '<td style="color:var(--text-
|
|
722
|
-
+ '<td
|
|
723
|
-
+ '<td>' + esc(v.parameter) + '</td>'
|
|
724
|
-
+ '<td>' +
|
|
1138
|
+
+ '<td style="color:var(--text-secondary)">' + v.tick + '</td>'
|
|
1139
|
+
+ '<td style="color:var(--text-value)">' + esc(v.principle) + '</td>'
|
|
1140
|
+
+ '<td style="color:' + sevColor + '">' + v.severity + '</td>'
|
|
1141
|
+
+ '<td style="color:var(--text-tertiary)">' + esc(v.parameter) + '</td>'
|
|
1142
|
+
+ '<td class="action-cell">' + actionHtml + '</td>'
|
|
1143
|
+
+ '<td>' + changeHtml + '</td>'
|
|
725
1144
|
+ '</tr>';
|
|
726
1145
|
}).join('');
|
|
727
1146
|
}
|
|
728
1147
|
|
|
729
1148
|
// Table sorting
|
|
730
|
-
document.querySelectorAll('.violations-table th').forEach(function(th) {
|
|
1149
|
+
document.querySelectorAll('.violations-table th[data-sort]').forEach(function(th) {
|
|
731
1150
|
th.addEventListener('click', function() {
|
|
732
|
-
|
|
1151
|
+
var key = th.dataset.sort;
|
|
733
1152
|
if (violationSortKey === key) violationSortAsc = !violationSortAsc;
|
|
734
1153
|
else { violationSortKey = key; violationSortAsc = true; }
|
|
735
1154
|
renderViolations();
|
|
736
1155
|
});
|
|
737
1156
|
});
|
|
738
1157
|
|
|
739
|
-
//
|
|
1158
|
+
// -- Personas --
|
|
740
1159
|
function renderPersonas(dist) {
|
|
741
1160
|
if (!dist || Object.keys(dist).length === 0) {
|
|
742
1161
|
$personaBars.innerHTML = '<div class="empty-state">No persona data yet</div>';
|
|
1162
|
+
$personaCount.textContent = '';
|
|
743
1163
|
return;
|
|
744
1164
|
}
|
|
745
|
-
|
|
746
|
-
|
|
1165
|
+
var total = Object.values(dist).reduce(function(s, v) { return s + v; }, 0);
|
|
1166
|
+
$personaCount.textContent = total + ' agents';
|
|
1167
|
+
var entries = Object.entries(dist).sort(function(a, b) { return b[1] - a[1]; });
|
|
747
1168
|
$personaBars.innerHTML = entries.map(function(e) {
|
|
748
|
-
|
|
1169
|
+
var pctVal = total > 0 ? (e[1] / total * 100) : 0;
|
|
1170
|
+
var color = personaColor(e[0]);
|
|
749
1171
|
return '<div class="persona-row">'
|
|
750
|
-
+ '<
|
|
751
|
-
+ '<div class="persona-bar-track"><div class="persona-bar-fill" style="width:' + pctVal + '
|
|
752
|
-
+ '<
|
|
1172
|
+
+ '<span class="persona-label">' + esc(e[0]) + '</span>'
|
|
1173
|
+
+ '<div class="persona-bar-track"><div class="persona-bar-fill" style="width:' + pctVal.toFixed(0) + '%;background:' + color + ';"></div></div>'
|
|
1174
|
+
+ '<span class="persona-pct">' + pctVal.toFixed(0) + '%</span>'
|
|
753
1175
|
+ '</div>';
|
|
754
1176
|
}).join('');
|
|
755
1177
|
}
|
|
756
1178
|
|
|
757
|
-
//
|
|
758
|
-
function
|
|
759
|
-
if (!principles || principles.length === 0) {
|
|
760
|
-
$
|
|
1179
|
+
// -- Parameters --
|
|
1180
|
+
function renderParams(principles, registryValues) {
|
|
1181
|
+
if ((!principles || principles.length === 0) && Object.keys(paramValues).length === 0) {
|
|
1182
|
+
$paramsList.innerHTML = '<div class="empty-state">No parameters registered</div>';
|
|
1183
|
+
$paramsCount.textContent = '';
|
|
761
1184
|
return;
|
|
762
1185
|
}
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
1186
|
+
|
|
1187
|
+
// If we have actual parameter values from API, show those
|
|
1188
|
+
if (registryValues && Object.keys(registryValues).length > 0) {
|
|
1189
|
+
var entries = Object.entries(registryValues);
|
|
1190
|
+
$paramsCount.textContent = entries.length + ' tracked';
|
|
1191
|
+
$paramsList.innerHTML = entries.map(function(e) {
|
|
1192
|
+
var key = e[0];
|
|
1193
|
+
var val = e[1];
|
|
1194
|
+
var ticksAgo = currentTick - (paramLastTick[key] || 0);
|
|
1195
|
+
var agoText = ticksAgo <= 0 ? '' : ticksAgo <= 5 ? 'just now' : ticksAgo + ' ticks ago';
|
|
1196
|
+
var pending = paramPending[key];
|
|
1197
|
+
|
|
1198
|
+
if (pending && isAdvisor) {
|
|
1199
|
+
return '<div class="param-row">'
|
|
1200
|
+
+ '<span class="param-name">' + esc(key) + '</span>'
|
|
1201
|
+
+ '<span>'
|
|
1202
|
+
+ '<span class="param-val">' + fmt(val) + '</span>'
|
|
1203
|
+
+ '<span class="param-ghost" style="display:inline;"> \\u2192 ' + fmt(pending.proposedVal) + '?</span>'
|
|
1204
|
+
+ '<span class="param-pending-label" style="display:inline;">pending</span>'
|
|
1205
|
+
+ '</span>'
|
|
1206
|
+
+ '</div>';
|
|
1207
|
+
}
|
|
1208
|
+
return '<div class="param-row">'
|
|
1209
|
+
+ '<span class="param-name">' + esc(key) + '</span>'
|
|
1210
|
+
+ '<span><span class="param-val">' + fmt(val) + '</span>'
|
|
1211
|
+
+ (agoText ? '<span class="param-changed">' + agoText + '</span>' : '')
|
|
1212
|
+
+ '</span>'
|
|
1213
|
+
+ '</div>';
|
|
1214
|
+
}).join('');
|
|
1215
|
+
return;
|
|
1216
|
+
}
|
|
1217
|
+
|
|
1218
|
+
// Fallback: show principle names (legacy behavior)
|
|
1219
|
+
if (principles && principles.length > 0) {
|
|
1220
|
+
$paramsCount.textContent = principles.length + ' registered';
|
|
1221
|
+
$paramsList.innerHTML = principles.slice(0, 30).map(function(p) {
|
|
1222
|
+
return '<div class="param-row">'
|
|
1223
|
+
+ '<span class="param-name">[' + esc(p.id) + ']</span>'
|
|
1224
|
+
+ '<span class="param-val">' + esc(p.name) + '</span>'
|
|
1225
|
+
+ '</div>';
|
|
1226
|
+
}).join('');
|
|
1227
|
+
}
|
|
769
1228
|
}
|
|
770
1229
|
|
|
771
|
-
//
|
|
1230
|
+
// -- KPI update --
|
|
772
1231
|
function updateKPIs(data) {
|
|
773
1232
|
if (data.health != null) {
|
|
774
|
-
$
|
|
775
|
-
$
|
|
776
|
-
document.getElementById('cv-health').textContent = data.health + '/100';
|
|
1233
|
+
$hHealth.textContent = data.health + '/100';
|
|
1234
|
+
$hHealth.className = 'value ' + healthClass(data.health);
|
|
777
1235
|
}
|
|
778
1236
|
if (data.mode != null) {
|
|
779
|
-
$
|
|
1237
|
+
$hMode.textContent = data.mode;
|
|
780
1238
|
isAdvisor = data.mode === 'advisor';
|
|
781
|
-
$
|
|
1239
|
+
$hMode.className = 'value ' + (isAdvisor ? 'mode-value-advisor' : 'mode-value-auto');
|
|
1240
|
+
document.body.classList.toggle('advisor-mode', isAdvisor);
|
|
1241
|
+
}
|
|
1242
|
+
if (data.tick != null) {
|
|
1243
|
+
$hTick.textContent = data.tick;
|
|
1244
|
+
currentTick = data.tick;
|
|
1245
|
+
}
|
|
1246
|
+
if (data.uptime != null) $hUptime.textContent = formatUptime(data.uptime);
|
|
1247
|
+
if (data.pendingCount != null || data.activePlans != null) {
|
|
1248
|
+
var count = data.pendingCount || data.activePlans || 0;
|
|
1249
|
+
$hPending.textContent = count;
|
|
782
1250
|
}
|
|
783
|
-
if (data.tick != null) $kpiTick.textContent = data.tick;
|
|
784
|
-
if (data.uptime != null) $kpiUptime.textContent = formatUptime(data.uptime);
|
|
785
|
-
if (data.activePlans != null) $kpiPlans.textContent = data.activePlans;
|
|
786
1251
|
}
|
|
787
1252
|
|
|
788
|
-
//
|
|
1253
|
+
// -- Metrics history --
|
|
789
1254
|
function updateChartsFromHistory(history) {
|
|
790
1255
|
if (!history || history.length === 0) return;
|
|
791
|
-
|
|
1256
|
+
var ticks = history.map(function(h) { return h.tick; });
|
|
792
1257
|
updateChart(chartHealth, ticks, history.map(function(h) { return h.health; }));
|
|
793
1258
|
updateChart(chartGini, ticks, history.map(function(h) { return h.giniCoefficient; }));
|
|
794
|
-
updateChart(
|
|
1259
|
+
updateChart(chartFlow, ticks, history.map(function(h) { return h.netFlow; }));
|
|
795
1260
|
updateChart(chartSatisfaction, ticks, history.map(function(h) { return h.avgSatisfaction; }));
|
|
796
|
-
|
|
797
|
-
const last = history[history.length - 1];
|
|
798
|
-
document.getElementById('cv-gini').textContent = last.giniCoefficient.toFixed(3);
|
|
799
|
-
document.getElementById('cv-netflow').textContent = last.netFlow.toFixed(1);
|
|
800
|
-
document.getElementById('cv-satisfaction').textContent = last.avgSatisfaction.toFixed(0) + '/100';
|
|
801
1261
|
}
|
|
802
1262
|
|
|
803
|
-
//
|
|
1263
|
+
// -- API calls --
|
|
804
1264
|
function fetchJSON(path) {
|
|
805
1265
|
return fetch(path).then(function(r) { return r.json(); });
|
|
806
1266
|
}
|
|
@@ -835,19 +1295,22 @@ function getDashboardHtml() {
|
|
|
835
1295
|
}).catch(function() {});
|
|
836
1296
|
|
|
837
1297
|
fetchJSON('/principles').then(function(data) {
|
|
838
|
-
if (data.principles)
|
|
1298
|
+
if (data.principles) {
|
|
1299
|
+
paramRegistry = data.principles;
|
|
1300
|
+
renderParams(data.principles, paramValues);
|
|
1301
|
+
}
|
|
839
1302
|
}).catch(function() {});
|
|
840
1303
|
|
|
841
1304
|
fetchJSON('/pending').then(function(data) {
|
|
842
1305
|
if (data.pending) {
|
|
843
1306
|
pendingDecisions = data.pending;
|
|
844
|
-
$
|
|
1307
|
+
$hPending.textContent = data.count || 0;
|
|
845
1308
|
}
|
|
846
1309
|
}).catch(function() {});
|
|
847
1310
|
}
|
|
848
1311
|
|
|
849
|
-
//
|
|
850
|
-
|
|
1312
|
+
// -- Polling fallback --
|
|
1313
|
+
var pollInterval = null;
|
|
851
1314
|
|
|
852
1315
|
function startPolling() {
|
|
853
1316
|
if (pollInterval) return;
|
|
@@ -864,9 +1327,9 @@ function getDashboardHtml() {
|
|
|
864
1327
|
if (pollInterval) { clearInterval(pollInterval); pollInterval = null; }
|
|
865
1328
|
}
|
|
866
1329
|
|
|
867
|
-
//
|
|
1330
|
+
// -- WebSocket --
|
|
868
1331
|
function connectWS() {
|
|
869
|
-
|
|
1332
|
+
var proto = location.protocol === 'https:' ? 'wss:' : 'ws:';
|
|
870
1333
|
ws = new WebSocket(proto + '//' + location.host);
|
|
871
1334
|
|
|
872
1335
|
ws.onopen = function() {
|
|
@@ -874,13 +1337,12 @@ function getDashboardHtml() {
|
|
|
874
1337
|
$liveDot.classList.remove('disconnected');
|
|
875
1338
|
$liveDot.title = 'WebSocket connected';
|
|
876
1339
|
stopPolling();
|
|
877
|
-
// Request fresh health
|
|
878
1340
|
ws.send(JSON.stringify({ type: 'health' }));
|
|
879
1341
|
};
|
|
880
1342
|
|
|
881
1343
|
ws.onclose = function() {
|
|
882
1344
|
$liveDot.classList.add('disconnected');
|
|
883
|
-
$liveDot.title = 'WebSocket disconnected
|
|
1345
|
+
$liveDot.title = 'WebSocket disconnected \\u2014 reconnecting...';
|
|
884
1346
|
startPolling();
|
|
885
1347
|
setTimeout(connectWS, reconnectDelay);
|
|
886
1348
|
reconnectDelay = Math.min(reconnectDelay * 1.5, MAX_RECONNECT);
|
|
@@ -896,7 +1358,6 @@ function getDashboardHtml() {
|
|
|
896
1358
|
case 'tick_result':
|
|
897
1359
|
updateKPIs({ health: msg.health, tick: msg.tick });
|
|
898
1360
|
if (msg.alerts) renderAlerts(msg.alerts);
|
|
899
|
-
// Refresh charts
|
|
900
1361
|
fetchJSON('/metrics').then(function(data) {
|
|
901
1362
|
if (data.history) updateChartsFromHistory(data.history);
|
|
902
1363
|
if (data.latest) renderPersonas(data.latest.personaDistribution);
|
|
@@ -912,32 +1373,90 @@ function getDashboardHtml() {
|
|
|
912
1373
|
pendingDecisions = pendingDecisions.filter(function(d) {
|
|
913
1374
|
return d.id !== msg.decisionId;
|
|
914
1375
|
});
|
|
915
|
-
$
|
|
1376
|
+
$hPending.textContent = pendingDecisions.length;
|
|
916
1377
|
}
|
|
917
1378
|
break;
|
|
918
1379
|
}
|
|
919
1380
|
};
|
|
920
1381
|
}
|
|
921
1382
|
|
|
922
|
-
//
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
1383
|
+
// -- Advisor actions (event delegation) --
|
|
1384
|
+
document.addEventListener('click', function(e) {
|
|
1385
|
+
var btn = e.target.closest('[data-action]');
|
|
1386
|
+
if (!btn) return;
|
|
1387
|
+
var action = btn.getAttribute('data-action');
|
|
1388
|
+
var id = btn.getAttribute('data-id');
|
|
1389
|
+
var principleId = btn.getAttribute('data-principle');
|
|
1390
|
+
|
|
1391
|
+
// Toggle pending dropdown
|
|
1392
|
+
if (action === 'toggle-pending') {
|
|
1393
|
+
var dropdown = btn.querySelector('.pending-dropdown');
|
|
1394
|
+
if (!dropdown) return;
|
|
1395
|
+
document.querySelectorAll('.pending-dropdown.open').forEach(function(d) {
|
|
1396
|
+
if (d !== dropdown) d.classList.remove('open');
|
|
1397
|
+
});
|
|
1398
|
+
dropdown.classList.toggle('open');
|
|
1399
|
+
e.stopPropagation();
|
|
1400
|
+
return;
|
|
1401
|
+
}
|
|
1402
|
+
|
|
1403
|
+
// Approve from alert card
|
|
1404
|
+
if (action === 'approve-alert' && principleId) {
|
|
1405
|
+
var pd = pendingDecisions.find(function(d) {
|
|
1406
|
+
return d.principleId === principleId || (d.diagnosis?.principle?.id === principleId);
|
|
1407
|
+
});
|
|
1408
|
+
if (pd) {
|
|
1409
|
+
postJSON('/approve', { decisionId: pd.id }).then(function(data) {
|
|
1410
|
+
if (data.ok) {
|
|
1411
|
+
addTerminalLine('<span class="t-tick">[Advisor]</span> <span class="t-check">\\u2705 Approved ' + esc(pd.id) + '</span>');
|
|
1412
|
+
}
|
|
1413
|
+
}).catch(function() {});
|
|
927
1414
|
}
|
|
928
|
-
|
|
929
|
-
|
|
1415
|
+
return;
|
|
1416
|
+
}
|
|
930
1417
|
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
1418
|
+
// Reject from alert card
|
|
1419
|
+
if (action === 'reject-alert' && principleId) {
|
|
1420
|
+
var pd2 = pendingDecisions.find(function(d) {
|
|
1421
|
+
return d.principleId === principleId || (d.diagnosis?.principle?.id === principleId);
|
|
1422
|
+
});
|
|
1423
|
+
if (pd2) {
|
|
1424
|
+
var reason = prompt('Rejection reason (optional):');
|
|
1425
|
+
postJSON('/reject', { decisionId: pd2.id, reason: reason || undefined }).then(function(data) {
|
|
1426
|
+
if (data.ok) {
|
|
1427
|
+
addTerminalLine('<span class="t-tick">[Advisor]</span> <span class="t-fail">\\u274c Rejected ' + esc(pd2.id) + '</span>');
|
|
1428
|
+
}
|
|
1429
|
+
}).catch(function() {});
|
|
936
1430
|
}
|
|
937
|
-
|
|
938
|
-
|
|
1431
|
+
return;
|
|
1432
|
+
}
|
|
1433
|
+
|
|
1434
|
+
if (!id) return;
|
|
1435
|
+
|
|
1436
|
+
if (action === 'approve') {
|
|
1437
|
+
postJSON('/approve', { decisionId: id }).then(function(data) {
|
|
1438
|
+
if (data.ok) {
|
|
1439
|
+
addTerminalLine('<span class="t-tick">[Advisor]</span> <span class="t-check">\\u2705 Approved ' + esc(id) + '</span>');
|
|
1440
|
+
}
|
|
1441
|
+
}).catch(function() {});
|
|
1442
|
+
} else if (action === 'reject') {
|
|
1443
|
+
var reason2 = prompt('Rejection reason (optional):');
|
|
1444
|
+
postJSON('/reject', { decisionId: id, reason: reason2 || undefined }).then(function(data) {
|
|
1445
|
+
if (data.ok) {
|
|
1446
|
+
addTerminalLine('<span class="t-tick">[Advisor]</span> <span class="t-fail">\\u274c Rejected ' + esc(id) + '</span>');
|
|
1447
|
+
}
|
|
1448
|
+
}).catch(function() {});
|
|
1449
|
+
}
|
|
1450
|
+
});
|
|
1451
|
+
|
|
1452
|
+
// Close dropdowns on outside click
|
|
1453
|
+
document.addEventListener('click', function(e) {
|
|
1454
|
+
if (!e.target.closest('.badge-pending')) {
|
|
1455
|
+
document.querySelectorAll('.pending-dropdown.open').forEach(function(d) { d.classList.remove('open'); });
|
|
1456
|
+
}
|
|
1457
|
+
});
|
|
939
1458
|
|
|
940
|
-
//
|
|
1459
|
+
// -- Init --
|
|
941
1460
|
initCharts();
|
|
942
1461
|
loadInitialData();
|
|
943
1462
|
connectWS();
|
|
@@ -1327,6 +1846,7 @@ function createRouteHandler(server) {
|
|
|
1327
1846
|
}
|
|
1328
1847
|
|
|
1329
1848
|
// src/websocket.ts
|
|
1849
|
+
import { timingSafeEqual as timingSafeEqual2 } from "crypto";
|
|
1330
1850
|
import { WebSocketServer, WebSocket } from "ws";
|
|
1331
1851
|
import { validateEconomyState as validateEconomyState2 } from "@agent-e/core";
|
|
1332
1852
|
function send(ws, data) {
|
|
@@ -1377,7 +1897,7 @@ function createWebSocketHandler(httpServer, server) {
|
|
|
1377
1897
|
if (server.apiKey) {
|
|
1378
1898
|
const url = new URL(req.url ?? "/", `http://${req.headers.host ?? "localhost"}`);
|
|
1379
1899
|
const token = url.searchParams.get("token") ?? req.headers["authorization"]?.replace("Bearer ", "");
|
|
1380
|
-
if (token !== server.apiKey) {
|
|
1900
|
+
if (!token || token.length !== server.apiKey.length || !timingSafeEqual2(Buffer.from(token), Buffer.from(server.apiKey))) {
|
|
1381
1901
|
ws.close(1008, "Unauthorized");
|
|
1382
1902
|
return;
|
|
1383
1903
|
}
|
|
@@ -1686,4 +2206,4 @@ var AgentEServer = class {
|
|
|
1686
2206
|
export {
|
|
1687
2207
|
AgentEServer
|
|
1688
2208
|
};
|
|
1689
|
-
//# sourceMappingURL=chunk-
|
|
2209
|
+
//# sourceMappingURL=chunk-AWYCK646.mjs.map
|