@miller-tech/uap 1.135.1 → 1.136.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.tsbuildinfo +1 -1
- package/dist/delivery/acceptance-judge.d.ts +1 -1
- package/dist/delivery/acceptance-judge.d.ts.map +1 -1
- package/dist/delivery/acceptance-judge.js +125 -17
- package/dist/delivery/acceptance-judge.js.map +1 -1
- package/package.json +1 -1
- package/src/policies/enforcers/__pycache__/_common.cpython-312.pyc +0 -0
- package/tools/agents/scripts/__pycache__/toolcall_path_normalizer.cpython-312.pyc +0 -0
- package/tools/agents/scripts/anthropic_proxy.py +121 -0
- package/tools/agents/tests/test_mandate_deliver.py +101 -0
- package/web/dash/charts.js +267 -0
- package/web/dash/core.js +17 -77
- package/web/dash/styles.css +19 -0
- package/web/dash/tab-agents.js +21 -0
- package/web/dash/tab-orchestration.js +21 -0
- package/web/dash/tab-overview.js +21 -0
- package/web/dash/tab-tasks.js +21 -0
- package/web/dashboard.html +1 -0
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
/* UAP Dashboard charts — uPlot chart + sparkline builders.
|
|
2
|
+
*
|
|
3
|
+
* The builder functions (addTooltip, mkChart, initChartTasks, initChartCompression,
|
|
4
|
+
* initSparkline, seedLegends, updateAllCharts) are ported 1:1 from the original
|
|
5
|
+
* monolithic web/dashboard.html inline <script>. On top of them sit the
|
|
6
|
+
* registry-based adapters (syncHero / syncSpark) that the tabbed views use to
|
|
7
|
+
* create-or-update charts idempotently on every snapshot.
|
|
8
|
+
*
|
|
9
|
+
* Loaded BEFORE core.js; exposes window.UAPCharts. core.js re-exports it as
|
|
10
|
+
* UAP.charts for the tab modules. */
|
|
11
|
+
(function () {
|
|
12
|
+
'use strict';
|
|
13
|
+
|
|
14
|
+
var uplotReady = typeof uPlot !== 'undefined';
|
|
15
|
+
|
|
16
|
+
// ── Chart colors (ported 1:1) ──
|
|
17
|
+
const CC = {
|
|
18
|
+
done:'#3fb950', inProg:'#58a6ff', blocked:'#f85149', open:'#484f58', agents:'#bc8cff',
|
|
19
|
+
raw:'#58a6ff', ctx:'#3fb950', hitRate:'#bc8cff', deploy:'#58a6ff', blockRate:'#f85149',
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
function tsUnix(arr) { return arr.map(p => new Date(p.timestamp).getTime() / 1000); }
|
|
23
|
+
|
|
24
|
+
// ── Tooltip (ported 1:1) ──
|
|
25
|
+
function addTooltip(u, colors, labels, fmtVal) {
|
|
26
|
+
const tt = document.createElement('div');
|
|
27
|
+
tt.className = 'u-tooltip'; tt.style.display = 'none';
|
|
28
|
+
u.root.appendChild(tt);
|
|
29
|
+
u.hooks.setCursor = u.hooks.setCursor || [];
|
|
30
|
+
u.hooks.setCursor.push(() => {
|
|
31
|
+
const { idx, left, top } = u.cursor;
|
|
32
|
+
if (idx == null) { tt.style.display = 'none'; return; }
|
|
33
|
+
const date = new Date(u.data[0][idx] * 1000);
|
|
34
|
+
let rows = `<div class="tt-time">${date.toLocaleTimeString()}</div>`;
|
|
35
|
+
for (let i = 1; i < u.series.length; i++) {
|
|
36
|
+
if (!u.series[i].show) continue;
|
|
37
|
+
const val = u.data[i][idx];
|
|
38
|
+
if (val == null) continue;
|
|
39
|
+
const c = colors[i-1] || '#fff', l = labels[i-1] || '';
|
|
40
|
+
const v = fmtVal ? fmtVal(val, i) : val.toLocaleString();
|
|
41
|
+
rows += `<div class="tt-row"><span class="tt-dot" style="background:${c}"></span><span class="tt-label">${l}</span><span class="tt-val">${v}</span></div>`;
|
|
42
|
+
}
|
|
43
|
+
tt.innerHTML = rows; tt.style.display = '';
|
|
44
|
+
tt.style.left = (left + 16) + 'px'; tt.style.top = Math.max(0, top - 10) + 'px';
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// ── Generic chart builder (ported 1:1) ──
|
|
49
|
+
function mkChart(elId, ts, extractors, opts) {
|
|
50
|
+
if (!uplotReady || !ts || ts.length < 2) return null;
|
|
51
|
+
const el = document.getElementById(elId);
|
|
52
|
+
if (!el) return null;
|
|
53
|
+
el.innerHTML = '';
|
|
54
|
+
const timestamps = tsUnix(ts);
|
|
55
|
+
const data = [timestamps, ...extractors.map(fn => ts.map(fn))];
|
|
56
|
+
const u = new uPlot(opts(el.clientWidth), data, el);
|
|
57
|
+
return u;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function initChartTasks(ts) {
|
|
61
|
+
const colors = [CC.done, CC.inProg, CC.blocked, CC.open, CC.agents];
|
|
62
|
+
const labels = ['Done', 'In Progress', 'Blocked', 'Open', 'Active Agents'];
|
|
63
|
+
const extractors = [
|
|
64
|
+
p => p.tasks?.done || 0, p => p.tasks?.inProgress || 0,
|
|
65
|
+
p => p.tasks?.blocked || 0, p => p.tasks?.open || 0,
|
|
66
|
+
p => p.coordination?.activeAgents || 0,
|
|
67
|
+
];
|
|
68
|
+
const u = mkChart('chart-tasks', ts, extractors, (w) => ({
|
|
69
|
+
width: w, height: 260,
|
|
70
|
+
cursor: { sync: { key: 'uap' }, focus: { prox: 30 } },
|
|
71
|
+
scales: { x: { time: true }, y: { min: 0 }, agents: { min: 0 } },
|
|
72
|
+
axes: [
|
|
73
|
+
{ stroke: '#484f58', grid: { stroke: '#21262d' }, ticks: { stroke: '#21262d' }, font: '10px SF Mono,monospace' },
|
|
74
|
+
{ stroke: '#484f58', grid: { stroke: '#21262d' }, ticks: { stroke: '#21262d' }, font: '10px SF Mono,monospace', size: 50 },
|
|
75
|
+
{ stroke: CC.agents, grid: { show: false }, side: 1, font: '10px SF Mono,monospace', size: 50, scale: 'agents' },
|
|
76
|
+
],
|
|
77
|
+
series: [
|
|
78
|
+
{},
|
|
79
|
+
{ label: 'Done', stroke: CC.done, fill: CC.done+'30', width: 2 },
|
|
80
|
+
{ label: 'In Prog', stroke: CC.inProg, fill: CC.inProg+'30', width: 2 },
|
|
81
|
+
{ label: 'Blocked', stroke: CC.blocked, fill: CC.blocked+'20', width: 2 },
|
|
82
|
+
{ label: 'Open', stroke: CC.open, fill: CC.open+'20', width: 1, dash: [4,2] },
|
|
83
|
+
{ label: 'Agents', stroke: CC.agents, width: 2, dash: [6,3], scale: 'agents' },
|
|
84
|
+
],
|
|
85
|
+
}));
|
|
86
|
+
if (u) addTooltip(u, colors, labels);
|
|
87
|
+
return u;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function initChartCompression(ts) {
|
|
91
|
+
const colors = [CC.raw, CC.ctx, CC.hitRate];
|
|
92
|
+
const labels = ['Raw KB', 'Compressed KB', 'Hit Rate %'];
|
|
93
|
+
const extractors = [
|
|
94
|
+
p => Math.round((p.compression?.rawBytes||0)/1024),
|
|
95
|
+
p => Math.round((p.compression?.contextBytes||0)/1024),
|
|
96
|
+
parseHR,
|
|
97
|
+
];
|
|
98
|
+
const u = mkChart('chart-compression', ts, extractors, (w) => ({
|
|
99
|
+
width: w, height: 260,
|
|
100
|
+
cursor: { sync: { key: 'uap' }, focus: { prox: 30 } },
|
|
101
|
+
scales: { x: { time: true }, y: { min: 0 }, pct: { min: 0, max: 100 } },
|
|
102
|
+
axes: [
|
|
103
|
+
{ stroke: '#484f58', grid: { stroke: '#21262d' }, ticks: { stroke: '#21262d' }, font: '10px SF Mono,monospace' },
|
|
104
|
+
{ stroke: '#484f58', grid: { stroke: '#21262d' }, ticks: { stroke: '#21262d' }, font: '10px SF Mono,monospace', size: 50, values: (_,v) => v.map(x => x+' KB') },
|
|
105
|
+
{ stroke: CC.hitRate, grid: { show: false }, side: 1, font: '10px SF Mono,monospace', size: 50, scale: 'pct', values: (_,v) => v.map(x => x+'%') },
|
|
106
|
+
],
|
|
107
|
+
series: [
|
|
108
|
+
{},
|
|
109
|
+
{ label: 'Raw', stroke: CC.raw, fill: CC.raw+'20', width: 2 },
|
|
110
|
+
{ label: 'Compressed', stroke: CC.ctx, fill: CC.ctx+'20', width: 2 },
|
|
111
|
+
{ label: 'Hit Rate', stroke: CC.hitRate, width: 2, dash: [6,3], scale: 'pct' },
|
|
112
|
+
],
|
|
113
|
+
}));
|
|
114
|
+
if (u) addTooltip(u, colors, labels, (v,i) => i===3 ? v+'%' : v+' KB');
|
|
115
|
+
return u;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// ── Sparkline builder (ported 1:1) ──
|
|
119
|
+
function initSparkline(elId, ts, fn, color, label) {
|
|
120
|
+
if (!uplotReady || !ts || ts.length < 2) return null;
|
|
121
|
+
const el = document.getElementById(elId);
|
|
122
|
+
if (!el) return null;
|
|
123
|
+
el.innerHTML = '';
|
|
124
|
+
const timestamps = tsUnix(ts);
|
|
125
|
+
const values = ts.map(fn);
|
|
126
|
+
const data = [timestamps, values];
|
|
127
|
+
const u = new uPlot({
|
|
128
|
+
width: el.clientWidth, height: 60,
|
|
129
|
+
cursor: { sync: { key: 'uap' }, focus: { prox: 30 }, points: { show: false } },
|
|
130
|
+
legend: { show: false },
|
|
131
|
+
scales: { x: { time: true }, y: { min: 0 } },
|
|
132
|
+
axes: [{ show: false }, { show: false }],
|
|
133
|
+
series: [{}, { stroke: color, fill: color+'25', width: 1.5 }],
|
|
134
|
+
}, data, el);
|
|
135
|
+
// Sparkline tooltip
|
|
136
|
+
const tt = document.createElement('div');
|
|
137
|
+
tt.className = 'u-tooltip'; tt.style.display = 'none';
|
|
138
|
+
u.root.appendChild(tt);
|
|
139
|
+
u.hooks.setCursor = u.hooks.setCursor || [];
|
|
140
|
+
u.hooks.setCursor.push(() => {
|
|
141
|
+
const { idx } = u.cursor;
|
|
142
|
+
if (idx == null) { tt.style.display = 'none'; return; }
|
|
143
|
+
tt.innerHTML = `<div class="tt-time">${new Date(data[0][idx]*1000).toLocaleTimeString()}</div><div class="tt-row"><span class="tt-label">${label}</span><span class="tt-val">${typeof values[idx]==='number'?values[idx].toLocaleString():values[idx]}</span></div>`;
|
|
144
|
+
tt.style.display = ''; tt.style.left = (u.cursor.left+12)+'px'; tt.style.top = '0px';
|
|
145
|
+
});
|
|
146
|
+
return u;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// ── Snapshot field parsers (ported 1:1) ──
|
|
150
|
+
const parseHR = p => { const h = p.memoryHitsMisses?.hitRate; return typeof h === 'string' ? parseFloat(h)||0 : h||0; };
|
|
151
|
+
const parseBR = p => { const b = p.compliance?.blockRate; return typeof b === 'string' ? parseFloat(b)||0 : b||0; };
|
|
152
|
+
|
|
153
|
+
// ── Fixed chart set + updateAllCharts (ported 1:1) ──
|
|
154
|
+
let chartTasks = null, chartCompression = null, chartDeploy = null, chartHitrate = null, chartCompliance = null;
|
|
155
|
+
let chartsInit = false;
|
|
156
|
+
// Seed each uPlot legend to the LATEST data point so the values read as
|
|
157
|
+
// live numbers instead of '--' until the user hovers. Fail-safe per chart.
|
|
158
|
+
function seedLegends() {
|
|
159
|
+
[chartTasks, chartCompression, chartDeploy, chartHitrate, chartCompliance].forEach(u => {
|
|
160
|
+
try { if (u && u.data && u.data[0] && u.data[0].length) u.setLegend({ idx: u.data[0].length - 1 }); } catch (e) { /* legend seed best-effort */ }
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
function updateAllCharts(ts) {
|
|
164
|
+
if (!uplotReady || !ts || ts.length < 2) return;
|
|
165
|
+
if (!chartsInit) {
|
|
166
|
+
chartTasks = initChartTasks(ts);
|
|
167
|
+
chartCompression = initChartCompression(ts);
|
|
168
|
+
chartDeploy = initSparkline('chart-deploy', ts, p=>(p.deployBuckets?.queued||0)+(p.deployBuckets?.executing||0)+(p.deployBuckets?.batched||0), CC.deploy, 'Active Deploys');
|
|
169
|
+
chartHitrate = initSparkline('chart-hitrate', ts, parseHR, CC.hitRate, 'Hit Rate %');
|
|
170
|
+
chartCompliance = initSparkline('chart-compliance', ts, parseBR, CC.blockRate, 'Block Rate %');
|
|
171
|
+
chartsInit = true;
|
|
172
|
+
seedLegends();
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
// Update data
|
|
176
|
+
const timestamps = tsUnix(ts);
|
|
177
|
+
if (chartTasks) chartTasks.setData([timestamps, ts.map(p=>p.tasks?.done||0), ts.map(p=>p.tasks?.inProgress||0), ts.map(p=>p.tasks?.blocked||0), ts.map(p=>p.tasks?.open||0), ts.map(p=>p.coordination?.activeAgents||0)]);
|
|
178
|
+
if (chartCompression) chartCompression.setData([timestamps, ts.map(p=>Math.round((p.compression?.rawBytes||0)/1024)), ts.map(p=>Math.round((p.compression?.contextBytes||0)/1024)), ts.map(parseHR)]);
|
|
179
|
+
if (chartDeploy) chartDeploy.setData([timestamps, ts.map(p=>(p.deployBuckets?.queued||0)+(p.deployBuckets?.executing||0)+(p.deployBuckets?.batched||0))]);
|
|
180
|
+
if (chartHitrate) chartHitrate.setData([timestamps, ts.map(parseHR)]);
|
|
181
|
+
if (chartCompliance) chartCompliance.setData([timestamps, ts.map(parseBR)]);
|
|
182
|
+
seedLegends();
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
186
|
+
// Registry-based adapters for the tabbed views. Tab renders are re-entrant
|
|
187
|
+
// (hosts are recreated on tab switch), so charts are keyed by host id and
|
|
188
|
+
// recreated when the host node changes, else updated in place via setData.
|
|
189
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
190
|
+
var chartReg = {}; // id -> { u, host }
|
|
191
|
+
var AXIS = { stroke: '#484f58', grid: { stroke: '#21262d' }, ticks: { stroke: '#21262d' }, font: '10px SF Mono,monospace' };
|
|
192
|
+
function clear(node) { while (node && node.firstChild) node.removeChild(node.firstChild); return node; }
|
|
193
|
+
|
|
194
|
+
function heroOpts(kind, w) {
|
|
195
|
+
if (kind === 'tasks') return {
|
|
196
|
+
width: w, height: 260, cursor: { sync: { key: 'uap' }, focus: { prox: 30 } },
|
|
197
|
+
scales: { x: { time: true }, y: { min: 0 }, agents: { min: 0 } },
|
|
198
|
+
axes: [AXIS, Object.assign({}, AXIS, { size: 50 }), { stroke: CC.agents, grid: { show: false }, side: 1, font: '10px SF Mono,monospace', size: 50, scale: 'agents' }],
|
|
199
|
+
series: [{}, { label: 'Done', stroke: CC.done, fill: CC.done + '30', width: 2 }, { label: 'In Prog', stroke: CC.inProg, fill: CC.inProg + '30', width: 2 }, { label: 'Blocked', stroke: CC.blocked, fill: CC.blocked + '20', width: 2 }, { label: 'Open', stroke: CC.open, fill: CC.open + '20', width: 1, dash: [4, 2] }, { label: 'Agents', stroke: CC.agents, width: 2, dash: [6, 3], scale: 'agents' }],
|
|
200
|
+
};
|
|
201
|
+
return {
|
|
202
|
+
width: w, height: 260, cursor: { sync: { key: 'uap' }, focus: { prox: 30 } },
|
|
203
|
+
scales: { x: { time: true }, y: { min: 0 }, pct: { min: 0, max: 100 } },
|
|
204
|
+
axes: [AXIS, Object.assign({}, AXIS, { size: 50, values: function (_, v) { return v.map(function (x) { return x + ' KB'; }); } }), { stroke: CC.hitRate, grid: { show: false }, side: 1, font: '10px SF Mono,monospace', size: 50, scale: 'pct', values: function (_, v) { return v.map(function (x) { return x + '%'; }); } }],
|
|
205
|
+
series: [{}, { label: 'Raw', stroke: CC.raw, fill: CC.raw + '20', width: 2 }, { label: 'Compressed', stroke: CC.ctx, fill: CC.ctx + '20', width: 2 }, { label: 'Hit Rate', stroke: CC.hitRate, width: 2, dash: [6, 3], scale: 'pct' }],
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
function heroData(kind, ts) {
|
|
209
|
+
var x = tsUnix(ts);
|
|
210
|
+
if (kind === 'tasks') return [x, ts.map(function (p) { return (p.tasks && p.tasks.done) || 0; }), ts.map(function (p) { return (p.tasks && p.tasks.inProgress) || 0; }), ts.map(function (p) { return (p.tasks && p.tasks.blocked) || 0; }), ts.map(function (p) { return (p.tasks && p.tasks.open) || 0; }), ts.map(function (p) { return (p.coordination && p.coordination.activeAgents) || 0; })];
|
|
211
|
+
return [x, ts.map(function (p) { return Math.round(((p.compression && p.compression.rawBytes) || 0) / 1024); }), ts.map(function (p) { return Math.round(((p.compression && p.compression.contextBytes) || 0) / 1024); }), ts.map(parseHR)];
|
|
212
|
+
}
|
|
213
|
+
function syncHero(id, kind, ts) {
|
|
214
|
+
if (!uplotReady || !ts || ts.length < 2) return;
|
|
215
|
+
var host = document.getElementById(id); if (!host) return;
|
|
216
|
+
var reg = chartReg[id], data = heroData(kind, ts);
|
|
217
|
+
if (!reg || reg.host !== host || !document.body.contains(reg.u.root)) {
|
|
218
|
+
if (reg && reg.u) { try { reg.u.destroy(); } catch (e) {} }
|
|
219
|
+
clear(host);
|
|
220
|
+
var u = new uPlot(heroOpts(kind, host.clientWidth || 600), data, host);
|
|
221
|
+
addTooltip(u, kind === 'tasks' ? [CC.done, CC.inProg, CC.blocked, CC.open, CC.agents] : [CC.raw, CC.ctx, CC.hitRate], kind === 'tasks' ? ['Done', 'In Progress', 'Blocked', 'Open', 'Active Agents'] : ['Raw KB', 'Compressed KB', 'Hit Rate %'], kind === 'tasks' ? null : function (v, i) { return i === 3 ? v + '%' : v + ' KB'; });
|
|
222
|
+
chartReg[id] = { u: u, host: host };
|
|
223
|
+
} else { reg.u.setData(data); }
|
|
224
|
+
seedLegend(chartReg[id].u);
|
|
225
|
+
}
|
|
226
|
+
function syncSpark(id, ts, fn, color, label) {
|
|
227
|
+
if (!uplotReady || !ts || ts.length < 2) return;
|
|
228
|
+
var host = document.getElementById(id); if (!host) return;
|
|
229
|
+
var reg = chartReg[id], data = [tsUnix(ts), ts.map(fn)];
|
|
230
|
+
if (!reg || reg.host !== host || !document.body.contains(reg.u.root)) {
|
|
231
|
+
if (reg && reg.u) { try { reg.u.destroy(); } catch (e) {} }
|
|
232
|
+
clear(host);
|
|
233
|
+
var u = new uPlot({ width: host.clientWidth || 300, height: 60, cursor: { sync: { key: 'uap' }, focus: { prox: 30 }, points: { show: false } }, legend: { show: false }, scales: { x: { time: true }, y: { min: 0 } }, axes: [{ show: false }, { show: false }], series: [{}, { stroke: color, fill: color + '25', width: 1.5 }] }, data, host);
|
|
234
|
+
chartReg[id] = { u: u, host: host, label: label };
|
|
235
|
+
} else { reg.u.setData(data); }
|
|
236
|
+
seedLegend(chartReg[id].u);
|
|
237
|
+
}
|
|
238
|
+
function seedLegend(u) { try { if (u && u.data && u.data[0] && u.data[0].length) u.setLegend({ idx: u.data[0].length - 1 }); } catch (e) {} }
|
|
239
|
+
function resetCharts() {
|
|
240
|
+
for (var id in chartReg) { try { chartReg[id].u.destroy(); } catch (e) {} }
|
|
241
|
+
chartReg = {};
|
|
242
|
+
// Also reset the fixed chart set (mirrors the original resize handler).
|
|
243
|
+
chartsInit = false;
|
|
244
|
+
chartTasks = chartCompression = chartDeploy = chartHitrate = chartCompliance = null;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// ── Exports ──
|
|
248
|
+
window.UAPCharts = {
|
|
249
|
+
// 1:1 ported builders
|
|
250
|
+
mkChart: mkChart,
|
|
251
|
+
initSparkline: initSparkline,
|
|
252
|
+
addTooltip: addTooltip,
|
|
253
|
+
updateAllCharts: updateAllCharts,
|
|
254
|
+
initChartTasks: initChartTasks,
|
|
255
|
+
initChartCompression: initChartCompression,
|
|
256
|
+
seedLegends: seedLegends,
|
|
257
|
+
tsUnix: tsUnix,
|
|
258
|
+
// registry adapters + shared parsers/colors
|
|
259
|
+
syncHero: syncHero,
|
|
260
|
+
syncSpark: syncSpark,
|
|
261
|
+
resetCharts: resetCharts,
|
|
262
|
+
parseHR: parseHR,
|
|
263
|
+
parseBR: parseBR,
|
|
264
|
+
CC: CC,
|
|
265
|
+
ready: uplotReady,
|
|
266
|
+
};
|
|
267
|
+
})();
|
package/web/dash/core.js
CHANGED
|
@@ -42,7 +42,8 @@
|
|
|
42
42
|
var MODEL_NAMES = { 'fable-5': 'Fable 5', 'opus-4.8': 'Claude Opus 4.8', 'opus-4.6': 'Claude Opus 4.6', 'sonnet-5': 'Claude Sonnet 5', 'sonnet-4.6': 'Claude Sonnet 4.6', 'haiku-4.5': 'Claude Haiku 4.5', 'qwen36-a3b': 'Qwen 3.6 35B A3B', 'qwen35-a3b': 'Qwen 3.5 35B A3B', 'qwen35': 'Qwen 3.5 35B A3B', 'gpt-5.4': 'GPT 5.4', 'gpt-5.3-codex': 'GPT 5.3 Codex' };
|
|
43
43
|
function modelName(id) { return MODEL_NAMES[id] || id || 'unknown'; }
|
|
44
44
|
|
|
45
|
-
// Hyperscript DOM builder
|
|
45
|
+
// Hyperscript DOM builder — XSS-safe by construction: strings only ever land
|
|
46
|
+
// in textContent / text nodes, never innerHTML.
|
|
46
47
|
function el(tag, attrs) {
|
|
47
48
|
var e = document.createElement(tag), i;
|
|
48
49
|
if (attrs) {
|
|
@@ -52,7 +53,6 @@
|
|
|
52
53
|
if (v == null) continue;
|
|
53
54
|
if (k === 'class') e.className = v;
|
|
54
55
|
else if (k === 'text') e.textContent = v;
|
|
55
|
-
else if (k === 'html') e.innerHTML = v;
|
|
56
56
|
else if (k === 'style' && typeof v === 'object') { for (var sk in v) e.style[sk] = v[sk]; }
|
|
57
57
|
else if (k === 'dataset' && typeof v === 'object') { for (var dk in v) e.dataset[dk] = v[dk]; }
|
|
58
58
|
else if (k.slice(0, 2) === 'on' && typeof v === 'function') e.addEventListener(k.slice(2).toLowerCase(), v);
|
|
@@ -149,7 +149,7 @@
|
|
|
149
149
|
drawerEl.querySelector('#drawer-title').textContent = title;
|
|
150
150
|
var body = drawerEl.querySelector('#drawer-body');
|
|
151
151
|
clear(body);
|
|
152
|
-
if (typeof contentNode === 'string') body.
|
|
152
|
+
if (typeof contentNode === 'string') body.textContent = contentNode; else if (contentNode) body.appendChild(contentNode);
|
|
153
153
|
drawerEl.classList.add('open');
|
|
154
154
|
drawerBackdrop.classList.add('open');
|
|
155
155
|
}
|
|
@@ -161,7 +161,7 @@
|
|
|
161
161
|
var open = !!opts.open;
|
|
162
162
|
var header = el('div', { class: 'collapsible-header' + (open ? ' open' : '') }, title);
|
|
163
163
|
var inner = el('div', { class: 'cb-inner' });
|
|
164
|
-
if (typeof bodyNode === 'string') inner.
|
|
164
|
+
if (typeof bodyNode === 'string') inner.textContent = bodyNode; else if (bodyNode) inner.appendChild(bodyNode);
|
|
165
165
|
var body = el('div', { class: 'collapsible-body' + (open ? ' open' : '') }, inner);
|
|
166
166
|
header.addEventListener('click', function () { header.classList.toggle('open'); body.classList.toggle('open'); });
|
|
167
167
|
return el('div', { class: 'collapsible' }, header, body);
|
|
@@ -179,79 +179,19 @@
|
|
|
179
179
|
}).catch(function (e) { toast(e.message || 'Request failed', 'err'); throw e; });
|
|
180
180
|
}
|
|
181
181
|
|
|
182
|
-
// ─────────────────────────── charts
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
for (var i = 1; i < u.series.length; i++) {
|
|
196
|
-
if (!u.series[i].show) continue;
|
|
197
|
-
var val = u.data[i][idx]; if (val == null) continue;
|
|
198
|
-
var c = colors[i - 1] || '#fff', l = labels[i - 1] || '';
|
|
199
|
-
var v = fmtVal ? fmtVal(val, i) : val.toLocaleString();
|
|
200
|
-
rows += '<div class="tt-row"><span class="tt-dot" style="background:' + c + '"></span><span class="tt-label">' + l + '</span><span class="tt-val">' + v + '</span></div>';
|
|
201
|
-
}
|
|
202
|
-
tt.innerHTML = rows; tt.style.display = ''; tt.style.left = (left + 16) + 'px'; tt.style.top = Math.max(0, top - 10) + 'px';
|
|
203
|
-
});
|
|
204
|
-
}
|
|
205
|
-
var AXIS = { stroke: '#484f58', grid: { stroke: '#21262d' }, ticks: { stroke: '#21262d' }, font: '10px SF Mono,monospace' };
|
|
206
|
-
function heroOpts(kind, w) {
|
|
207
|
-
if (kind === 'tasks') return {
|
|
208
|
-
width: w, height: 260, cursor: { sync: { key: 'uap' }, focus: { prox: 30 } },
|
|
209
|
-
scales: { x: { time: true }, y: { min: 0 }, agents: { min: 0 } },
|
|
210
|
-
axes: [AXIS, Object.assign({}, AXIS, { size: 50 }), { stroke: CC.agents, grid: { show: false }, side: 1, font: '10px SF Mono,monospace', size: 50, scale: 'agents' }],
|
|
211
|
-
series: [{}, { label: 'Done', stroke: CC.done, fill: CC.done + '30', width: 2 }, { label: 'In Prog', stroke: CC.inProg, fill: CC.inProg + '30', width: 2 }, { label: 'Blocked', stroke: CC.blocked, fill: CC.blocked + '20', width: 2 }, { label: 'Open', stroke: CC.open, fill: CC.open + '20', width: 1, dash: [4, 2] }, { label: 'Agents', stroke: CC.agents, width: 2, dash: [6, 3], scale: 'agents' }],
|
|
212
|
-
};
|
|
213
|
-
return {
|
|
214
|
-
width: w, height: 260, cursor: { sync: { key: 'uap' }, focus: { prox: 30 } },
|
|
215
|
-
scales: { x: { time: true }, y: { min: 0 }, pct: { min: 0, max: 100 } },
|
|
216
|
-
axes: [AXIS, Object.assign({}, AXIS, { size: 50, values: function (_, v) { return v.map(function (x) { return x + ' KB'; }); } }), { stroke: CC.hitRate, grid: { show: false }, side: 1, font: '10px SF Mono,monospace', size: 50, scale: 'pct', values: function (_, v) { return v.map(function (x) { return x + '%'; }); } }],
|
|
217
|
-
series: [{}, { label: 'Raw', stroke: CC.raw, fill: CC.raw + '20', width: 2 }, { label: 'Compressed', stroke: CC.ctx, fill: CC.ctx + '20', width: 2 }, { label: 'Hit Rate', stroke: CC.hitRate, width: 2, dash: [6, 3], scale: 'pct' }],
|
|
218
|
-
};
|
|
219
|
-
}
|
|
220
|
-
var parseHR = function (p) { var h = p.memoryHitsMisses && p.memoryHitsMisses.hitRate; return typeof h === 'string' ? parseFloat(h) || 0 : h || 0; };
|
|
221
|
-
var parseBR = function (p) { var b = p.compliance && p.compliance.blockRate; return typeof b === 'string' ? parseFloat(b) || 0 : b || 0; };
|
|
222
|
-
function heroData(kind, ts) {
|
|
223
|
-
var x = tsUnix(ts);
|
|
224
|
-
if (kind === 'tasks') return [x, ts.map(function (p) { return (p.tasks && p.tasks.done) || 0; }), ts.map(function (p) { return (p.tasks && p.tasks.inProgress) || 0; }), ts.map(function (p) { return (p.tasks && p.tasks.blocked) || 0; }), ts.map(function (p) { return (p.tasks && p.tasks.open) || 0; }), ts.map(function (p) { return (p.coordination && p.coordination.activeAgents) || 0; })];
|
|
225
|
-
return [x, ts.map(function (p) { return Math.round(((p.compression && p.compression.rawBytes) || 0) / 1024); }), ts.map(function (p) { return Math.round(((p.compression && p.compression.contextBytes) || 0) / 1024); }), ts.map(parseHR)];
|
|
226
|
-
}
|
|
227
|
-
function syncHero(id, kind, ts) {
|
|
228
|
-
if (!uplotReady || !ts || ts.length < 2) return;
|
|
229
|
-
var host = document.getElementById(id); if (!host) return;
|
|
230
|
-
var reg = chartReg[id], data = heroData(kind, ts);
|
|
231
|
-
if (!reg || reg.host !== host || !document.body.contains(reg.u.root)) {
|
|
232
|
-
if (reg && reg.u) { try { reg.u.destroy(); } catch (e) {} }
|
|
233
|
-
clear(host);
|
|
234
|
-
var u = new uPlot(heroOpts(kind, host.clientWidth || 600), data, host);
|
|
235
|
-
addTooltip(u, kind === 'tasks' ? [CC.done, CC.inProg, CC.blocked, CC.open, CC.agents] : [CC.raw, CC.ctx, CC.hitRate], kind === 'tasks' ? ['Done', 'In Progress', 'Blocked', 'Open', 'Active Agents'] : ['Raw KB', 'Compressed KB', 'Hit Rate %'], kind === 'tasks' ? null : function (v, i) { return i === 3 ? v + '%' : v + ' KB'; });
|
|
236
|
-
chartReg[id] = { u: u, host: host };
|
|
237
|
-
} else { reg.u.setData(data); }
|
|
238
|
-
seedLegend(chartReg[id].u);
|
|
239
|
-
}
|
|
240
|
-
function syncSpark(id, ts, fn, color, label) {
|
|
241
|
-
if (!uplotReady || !ts || ts.length < 2) return;
|
|
242
|
-
var host = document.getElementById(id); if (!host) return;
|
|
243
|
-
var reg = chartReg[id], data = [tsUnix(ts), ts.map(fn)];
|
|
244
|
-
if (!reg || reg.host !== host || !document.body.contains(reg.u.root)) {
|
|
245
|
-
if (reg && reg.u) { try { reg.u.destroy(); } catch (e) {} }
|
|
246
|
-
clear(host);
|
|
247
|
-
var u = new uPlot({ width: host.clientWidth || 300, height: 60, cursor: { sync: { key: 'uap' }, focus: { prox: 30 }, points: { show: false } }, legend: { show: false }, scales: { x: { time: true }, y: { min: 0 } }, axes: [{ show: false }, { show: false }], series: [{}, { stroke: color, fill: color + '25', width: 1.5 }] }, data, host);
|
|
248
|
-
chartReg[id] = { u: u, host: host, label: label };
|
|
249
|
-
} else { reg.u.setData(data); }
|
|
250
|
-
seedLegend(chartReg[id].u);
|
|
251
|
-
}
|
|
252
|
-
function seedLegend(u) { try { if (u && u.data && u.data[0] && u.data[0].length) u.setLegend({ idx: u.data[0].length - 1 }); } catch (e) {} }
|
|
253
|
-
function resetCharts() { for (var id in chartReg) { try { chartReg[id].u.destroy(); } catch (e) {} } chartReg = {}; }
|
|
254
|
-
var charts = { syncHero: syncHero, syncSpark: syncSpark, parseHR: parseHR, parseBR: parseBR, CC: CC, ready: uplotReady };
|
|
182
|
+
// ─────────────────────────── charts ───────────────────────────
|
|
183
|
+
// uPlot chart + sparkline builders live in charts.js (loaded before core.js):
|
|
184
|
+
// mkChart / initSparkline / addTooltip / updateAllCharts ported 1:1 from the
|
|
185
|
+
// original dashboard, plus the syncHero/syncSpark registry adapters the tabs
|
|
186
|
+
// use. Re-exported here as UAP.charts so tab modules have a single entry point.
|
|
187
|
+
var charts = window.UAPCharts || {
|
|
188
|
+
ready: false, CC: {},
|
|
189
|
+
syncHero: function () {}, syncSpark: function () {}, resetCharts: function () {},
|
|
190
|
+
mkChart: function () { return null; }, initSparkline: function () { return null; },
|
|
191
|
+
addTooltip: function () {}, updateAllCharts: function () {},
|
|
192
|
+
parseHR: function () { return 0; }, parseBR: function () { return 0; },
|
|
193
|
+
};
|
|
194
|
+
function resetCharts() { charts.resetCharts(); }
|
|
255
195
|
|
|
256
196
|
// ─────────────────────────── tab registry + router ───────────────────────────
|
|
257
197
|
var tabs = {}; // id -> { label, render, update }
|
package/web/dash/styles.css
CHANGED
|
@@ -269,6 +269,9 @@ label.field { display: flex; flex-direction: column; gap: 4px; font-size: 11px;
|
|
|
269
269
|
.toast.ok { border-color: var(--green); color: var(--green); }
|
|
270
270
|
.toast.err { border-color: var(--red); color: var(--red); }
|
|
271
271
|
.toast.warn { border-color: var(--yellow); color: var(--yellow); }
|
|
272
|
+
/* Legacy aliases from the original dashboard (kept for 1:1 parity). */
|
|
273
|
+
.toast.success { border-color: var(--green); color: var(--green); }
|
|
274
|
+
.toast.error { border-color: var(--red); color: var(--red); }
|
|
272
275
|
@keyframes toast-in { from { opacity: 0; transform: translateX(20px); } to { opacity: 1; transform: translateX(0); } }
|
|
273
276
|
|
|
274
277
|
/* ── Collapsible ── */
|
|
@@ -305,3 +308,19 @@ label.field { display: flex; flex-direction: column; gap: 4px; font-size: 11px;
|
|
|
305
308
|
.section-note { color: var(--fg3); font-size: 11px; margin-bottom: 8px; }
|
|
306
309
|
.grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
|
|
307
310
|
@media (max-width: 800px) { .grid-2 { grid-template-columns: 1fr; } }
|
|
311
|
+
|
|
312
|
+
/* ── Tab stubs (placeholder views for not-yet-built tabs) ── */
|
|
313
|
+
.coming-soon, .tab-coming-soon { display: flex; flex-direction: column; align-items: center; justify-content: center; min-height: 300px; color: var(--fg2); text-align: center; }
|
|
314
|
+
.coming-soon .icon { font-size: 48px; margin-bottom: 16px; opacity: 0.5; }
|
|
315
|
+
.coming-soon h2 { color: var(--cyan); margin-bottom: 8px; }
|
|
316
|
+
.coming-soon p { font-size: 12px; }
|
|
317
|
+
|
|
318
|
+
/* ── Spinner ── */
|
|
319
|
+
.spinner { display: inline-block; width: 16px; height: 16px; border: 2px solid var(--border); border-top-color: var(--cyan); border-radius: 50%; animation: spin 0.6s linear infinite; }
|
|
320
|
+
@keyframes spin { to { transform: rotate(360deg); } }
|
|
321
|
+
|
|
322
|
+
/* ── Scrollbar ── */
|
|
323
|
+
::-webkit-scrollbar { width: 6px; height: 6px; }
|
|
324
|
+
::-webkit-scrollbar-track { background: var(--bg); }
|
|
325
|
+
::-webkit-scrollbar-thumb { background: var(--bg3); border-radius: 3px; }
|
|
326
|
+
::-webkit-scrollbar-thumb:hover { background: var(--fg3); }
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* tab-agents.js — Stub tab: Agents
|
|
3
|
+
* Registers with UAP.registerTab and renders a 'coming soon' message.
|
|
4
|
+
*/
|
|
5
|
+
(function () {
|
|
6
|
+
'use strict';
|
|
7
|
+
|
|
8
|
+
function render() {
|
|
9
|
+
var el = document.createElement('div');
|
|
10
|
+
el.className = 'coming-soon';
|
|
11
|
+
el.innerHTML =
|
|
12
|
+
'<div class="icon">🤖</div>' +
|
|
13
|
+
'<h2>Agents</h2>' +
|
|
14
|
+
'<p>Coming soon</p>';
|
|
15
|
+
return el;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if (typeof UAP !== 'undefined' && typeof UAP.registerTab === 'function') {
|
|
19
|
+
UAP.registerTab('agents', { render: render });
|
|
20
|
+
}
|
|
21
|
+
})();
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* tab-orchestration.js — Stub tab: Orchestration
|
|
3
|
+
* Registers with UAP.registerTab and renders a 'coming soon' message.
|
|
4
|
+
*/
|
|
5
|
+
(function () {
|
|
6
|
+
'use strict';
|
|
7
|
+
|
|
8
|
+
function render() {
|
|
9
|
+
var el = document.createElement('div');
|
|
10
|
+
el.className = 'coming-soon';
|
|
11
|
+
el.innerHTML =
|
|
12
|
+
'<div class="icon">🔄</div>' +
|
|
13
|
+
'<h2>Orchestration</h2>' +
|
|
14
|
+
'<p>Coming soon</p>';
|
|
15
|
+
return el;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if (typeof UAP !== 'undefined' && typeof UAP.registerTab === 'function') {
|
|
19
|
+
UAP.registerTab('orchestration', { render: render });
|
|
20
|
+
}
|
|
21
|
+
})();
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* tab-overview.js — Stub tab: Overview
|
|
3
|
+
* Registers with UAP.registerTab and renders a 'coming soon' message.
|
|
4
|
+
*/
|
|
5
|
+
(function () {
|
|
6
|
+
'use strict';
|
|
7
|
+
|
|
8
|
+
function render() {
|
|
9
|
+
var el = document.createElement('div');
|
|
10
|
+
el.className = 'coming-soon';
|
|
11
|
+
el.innerHTML =
|
|
12
|
+
'<div class="icon">📊</div>' +
|
|
13
|
+
'<h2>Overview</h2>' +
|
|
14
|
+
'<p>Coming soon</p>';
|
|
15
|
+
return el;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if (typeof UAP !== 'undefined' && typeof UAP.registerTab === 'function') {
|
|
19
|
+
UAP.registerTab('overview', { render: render });
|
|
20
|
+
}
|
|
21
|
+
})();
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* tab-tasks.js — Stub tab: Tasks
|
|
3
|
+
* Registers with UAP.registerTab and renders a 'coming soon' message.
|
|
4
|
+
*/
|
|
5
|
+
(function () {
|
|
6
|
+
'use strict';
|
|
7
|
+
|
|
8
|
+
function render() {
|
|
9
|
+
var el = document.createElement('div');
|
|
10
|
+
el.className = 'coming-soon';
|
|
11
|
+
el.innerHTML =
|
|
12
|
+
'<div class="icon">📋</div>' +
|
|
13
|
+
'<h2>Tasks</h2>' +
|
|
14
|
+
'<p>Coming soon</p>';
|
|
15
|
+
return el;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if (typeof UAP !== 'undefined' && typeof UAP.registerTab === 'function') {
|
|
19
|
+
UAP.registerTab('tasks', { render: render });
|
|
20
|
+
}
|
|
21
|
+
})();
|
package/web/dashboard.html
CHANGED