@miller-tech/uap 1.133.2 → 1.135.1

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.
Files changed (66) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/bin/cli.js +4 -1
  3. package/dist/bin/cli.js.map +1 -1
  4. package/dist/cli/config-command.d.ts +49 -0
  5. package/dist/cli/config-command.d.ts.map +1 -0
  6. package/dist/cli/config-command.js +465 -0
  7. package/dist/cli/config-command.js.map +1 -0
  8. package/dist/cli/config-wizard.d.ts +13 -0
  9. package/dist/cli/config-wizard.d.ts.map +1 -0
  10. package/dist/cli/config-wizard.js +134 -0
  11. package/dist/cli/config-wizard.js.map +1 -0
  12. package/dist/cli/deliver.js +11 -2
  13. package/dist/cli/deliver.js.map +1 -1
  14. package/dist/cli/guided-setup.d.ts.map +1 -1
  15. package/dist/cli/guided-setup.js +11 -0
  16. package/dist/cli/guided-setup.js.map +1 -1
  17. package/dist/cli/policy.d.ts.map +1 -1
  18. package/dist/cli/policy.js +34 -0
  19. package/dist/cli/policy.js.map +1 -1
  20. package/dist/cli/setup.d.ts +1 -1
  21. package/dist/cli/setup.d.ts.map +1 -1
  22. package/dist/cli/setup.js +22 -0
  23. package/dist/cli/setup.js.map +1 -1
  24. package/dist/config/policy-recommendations.d.ts +29 -0
  25. package/dist/config/policy-recommendations.d.ts.map +1 -0
  26. package/dist/config/policy-recommendations.js +108 -0
  27. package/dist/config/policy-recommendations.js.map +1 -0
  28. package/dist/config/settings-registry.d.ts +65 -0
  29. package/dist/config/settings-registry.d.ts.map +1 -0
  30. package/dist/config/settings-registry.js +334 -0
  31. package/dist/config/settings-registry.js.map +1 -0
  32. package/dist/dashboard/controls.d.ts +66 -0
  33. package/dist/dashboard/controls.d.ts.map +1 -0
  34. package/dist/dashboard/controls.js +188 -0
  35. package/dist/dashboard/controls.js.map +1 -0
  36. package/dist/dashboard/data-service.d.ts +20 -0
  37. package/dist/dashboard/data-service.d.ts.map +1 -1
  38. package/dist/dashboard/data-service.js +47 -0
  39. package/dist/dashboard/data-service.js.map +1 -1
  40. package/dist/dashboard/server.d.ts.map +1 -1
  41. package/dist/dashboard/server.js +81 -5
  42. package/dist/dashboard/server.js.map +1 -1
  43. package/dist/delivery/convergence-loop.d.ts +5 -0
  44. package/dist/delivery/convergence-loop.d.ts.map +1 -1
  45. package/dist/delivery/convergence-loop.js +11 -0
  46. package/dist/delivery/convergence-loop.js.map +1 -1
  47. package/dist/delivery/run-state.d.ts +12 -0
  48. package/dist/delivery/run-state.d.ts.map +1 -1
  49. package/dist/delivery/run-state.js +58 -1
  50. package/dist/delivery/run-state.js.map +1 -1
  51. package/docs/DASHBOARD_UPLIFT_SPEC.md +171 -0
  52. package/docs/INDEX.md +2 -0
  53. package/docs/guides/POLICY_SELECTION.md +98 -0
  54. package/docs/reference/CONFIGURATION_REFERENCE.md +688 -0
  55. package/package.json +1 -1
  56. package/src/policies/enforcers/__pycache__/_common.cpython-312.pyc +0 -0
  57. package/tools/agents/scripts/__pycache__/toolcall_path_normalizer.cpython-312.pyc +0 -0
  58. package/tools/agents/scripts/anthropic_proxy.py +164 -12
  59. package/web/dash/core.js +398 -0
  60. package/web/dash/styles.css +307 -0
  61. package/web/dash/tab-deliver.js +12 -0
  62. package/web/dash/tab-memory.js +12 -0
  63. package/web/dash/tab-models.js +12 -0
  64. package/web/dash/tab-policies.js +12 -0
  65. package/web/dash/tabs.js +581 -0
  66. package/web/dashboard.html +12 -1303
@@ -0,0 +1,581 @@
1
+ /* UAP Dashboard tabs — 8 views registered against the core UAP shell.
2
+ * Loaded after core.js; calls UAP.start() at the end. */
3
+ (function () {
4
+ 'use strict';
5
+ var U = window.UAP;
6
+ var el = U.el, esc = U.esc, fmtNum = U.fmtNum, fmtUsd = U.fmtUsd, fmtKB = U.fmtKB, modelName = U.modelName;
7
+
8
+ // ── shared render helpers ──
9
+ function h(tag, cls, txt) { return el(tag, { class: cls, text: txt }); }
10
+ function kv(label, value, cls) { return el('div', { class: 'kv' }, el('span', { class: 'label', text: label }), el('span', { class: 'value ' + (cls || ''), text: value })); }
11
+ function empty(msg) { return el('div', { class: 'empty', text: msg || 'No data' }); }
12
+ function panel(title, actions) {
13
+ var head = el('h2', {}, title);
14
+ if (actions) { var wrap = el('span', { class: 'h2-actions' }); actions.forEach(function (a) { wrap.appendChild(a); }); head.appendChild(wrap); }
15
+ var p = el('div', { class: 'panel' }, head);
16
+ return p;
17
+ }
18
+ function statusBadge(s) { s = s || 'idle'; var map = { active: 'active', in_progress: 'active', completed: 'completed', done: 'completed', running: 'running', delivered: 'delivered', failed: 'failed', interrupted: 'interrupted', blocked: 'failed', idle: 'idle', open: 'idle', pending: 'idle', wont_do: 'idle' }; return el('span', { class: 'badge ' + (map[s] || 'idle'), text: s }); }
19
+ // Build a <table> node from header list + row builder returning an array of cells (nodes/strings).
20
+ function tableNode(headers, items, rowFn, rowClick) {
21
+ var thead = el('tr', {}); headers.forEach(function (hd) { thead.appendChild(el('th', { text: hd })); });
22
+ var t = el('table', {}, thead);
23
+ items.forEach(function (it, i) {
24
+ var cells = rowFn(it, i);
25
+ var tr = el('tr', rowClick ? { class: 'clickable', onclick: function () { rowClick(it); } } : {});
26
+ cells.forEach(function (c) { tr.appendChild(el('td', {}, c)); });
27
+ t.appendChild(tr);
28
+ });
29
+ return el('div', { class: 'table-wrap' }, t);
30
+ }
31
+ function sig(obj) { try { return JSON.stringify(obj); } catch (e) { return String(Date.now()); } }
32
+ // DeliverRunState has no scalar 'phase'; derive a display string from phases/checkpoint.
33
+ function runPhase(r) {
34
+ if (r.phases && r.phases.length) { var i = r.phaseIndex || 0; var ph = r.phases[Math.min(i, r.phases.length - 1)]; if (ph) return 'phase ' + (i + 1) + '/' + r.phases.length + ': ' + (ph.title || ph.id); }
35
+ if (r.checkpoint && r.checkpoint.turn) return 'turn ' + r.checkpoint.turn;
36
+ return '-';
37
+ }
38
+
39
+ // ═══════════════════════════ OVERVIEW ═══════════════════════════
40
+ U.registerTab('overview', (function () {
41
+ function tile(label, value, cls, sub, goId) {
42
+ return el('div', { class: 'tile', tabindex: '0', role: 'button', onclick: function () { if (goId) U.goto(goId); }, onkeydown: function (e) { if ((e.key === 'Enter' || e.key === ' ') && goId) { e.preventDefault(); U.goto(goId); } } },
43
+ el('div', { class: 'tile-label', text: label }), el('div', { class: 'tile-value ' + (cls || ''), text: value }), sub ? el('div', { class: 'tile-sub', text: sub }) : null);
44
+ }
45
+ function fillTiles(host, d) {
46
+ var t = d.tasks || {}, coord = d.coordination || {}, runs = d.deliverRuns || [], led = d.orchestrationTree && d.orchestrationTree.ledger, sv = d.savingsByInfluence || {}, mem = d.memory || {}, comp = d.compliance || {};
47
+ var pct = t.total ? Math.round((t.done / t.total) * 100) : 0;
48
+ var running = runs.filter(function (r) { return r.status === 'running'; }).length;
49
+ var orchPct = led ? (led.total ? Math.round((led.done / led.total) * 100) : 0) : ((d.orchestrationTree && d.orchestrationTree.ledger) ? d.orchestrationTree.ledger.pct : 0);
50
+ var memEntries = ((mem.l1 && mem.l1.entries) || 0) + ((mem.l2 && mem.l2.entries) || 0) + ((mem.l4 && mem.l4.entities) || 0);
51
+ U.clear(host);
52
+ [
53
+ tile('Tasks', pct + '%', pct >= 100 ? 'green' : 'cyan', (t.done || 0) + '/' + (t.total || 0) + ' done', 'tasks'),
54
+ tile('Active Agents', String(coord.activeAgents || 0), 'purple', (coord.totalAgents || 0) + ' total', 'agents'),
55
+ tile('Deliver Runs', String(running), running ? 'cyan' : '', (runs.length) + ' tracked', 'deliver'),
56
+ tile('Orchestration', orchPct + '%', orchPct >= 100 ? 'green' : 'cyan', led ? (led.done + '/' + led.total + ' items') : 'no active build', 'orchestration'),
57
+ tile('Tokens Saved', fmtNum(sv.totalTokensSaved || 0), 'green', fmtUsd(sv.totalCostSavedUsd || 0) + ' saved', 'memory'),
58
+ tile('Memory', fmtNum(memEntries), 'cyan', ((mem.l3 && mem.l3.status) || 'Qdrant') + '', 'memory'),
59
+ tile('Policy Blocks', String(comp.totalBlocks || 0), (comp.totalBlocks ? 'red' : 'green'), (comp.blockRate || '0%') + ' rate', 'policies'),
60
+ tile('Worktrees', String(coord.activeWorktrees || 0), '', (coord.activeClaims || 0) + ' claims', 'agents'),
61
+ ].forEach(function (n) { host.appendChild(n); });
62
+ }
63
+ function fillSummaries(host, d) {
64
+ U.clear(host);
65
+ var coord = d.coordination || {}, agents = (coord.agents || []).slice(0, 8);
66
+ var ap = panel('Active Agents');
67
+ ap.appendChild(agents.length ? tableNode(['Name', 'Status', 'Task', 'Started'], agents, function (a) {
68
+ return [a.name || a.id || '?', statusBadge(a.status), el('span', { class: 'muted', text: (a.task || '-') }), U.timeAgo(a.startedAt)];
69
+ }, function () { U.goto('agents'); }) : empty('No active agents'));
70
+ host.appendChild(ap);
71
+
72
+ var runs = (d.deliverRuns || []).slice(0, 6);
73
+ var rp = panel('Deliver Runs');
74
+ rp.appendChild(runs.length ? tableNode(['Run', 'Status', 'Phase', 'Updated'], runs, function (r) {
75
+ return [el('span', { class: 'mono-sm', text: (r.runId || '').slice(0, 10) }), statusBadge(r.status), runPhase(r), U.timeAgo(r.updatedAt || r.createdAt)];
76
+ }, function () { U.goto('deliver'); }) : empty('No deliver runs tracked'));
77
+ host.appendChild(rp);
78
+
79
+ var led = d.orchestrationTree && d.orchestrationTree.ledger;
80
+ var op = panel('Orchestration');
81
+ if (led && led.total) {
82
+ var pct = led.pct != null ? led.pct : Math.round((led.done / led.total) * 100);
83
+ op.appendChild(el('div', {}, el('div', { class: 'kv' }, el('span', { class: 'label', text: (led.mission || 'Active build').slice(0, 60) }), el('span', { class: 'value cyan', text: led.done + '/' + led.total + ' (' + pct + '%)' })),
84
+ el('div', { class: 'progress-track' }, el('div', { class: 'progress-fill', style: { width: pct + '%' } }))));
85
+ } else op.appendChild(empty('No active orchestration'));
86
+ host.appendChild(op);
87
+
88
+ var ev = (U.liveEvents || []).slice(0, 10);
89
+ var ep = panel('Recent Activity');
90
+ var feed = el('div', { class: 'event-feed' });
91
+ if (ev.length) ev.forEach(function (e) { feed.appendChild(eventRow(e)); }); else feed.appendChild(empty('Waiting for events…'));
92
+ ep.appendChild(feed); host.appendChild(ep);
93
+ }
94
+ function build(root, d) {
95
+ var tiles = el('div', { class: 'tile-grid', id: 'ov-tiles' });
96
+ var hero = el('div', { class: 'hero-row' },
97
+ panelWith('Tasks & Agents', el('div', { class: 'chart-container chart-hero', id: 'ov-chart-tasks' })),
98
+ panelWith('Compression & Memory', el('div', { class: 'chart-container chart-hero', id: 'ov-chart-comp' })));
99
+ var summaries = el('div', { class: 'metrics-row', id: 'ov-summaries', style: { gridTemplateColumns: 'repeat(auto-fit,minmax(280px,1fr))' } });
100
+ root.appendChild(tiles); root.appendChild(hero); root.appendChild(summaries);
101
+ fillTiles(tiles, d); fillSummaries(summaries, d); paintCharts(d);
102
+ }
103
+ function panelWith(title, node) { var p = panel(title); p.appendChild(node); return p; }
104
+ function paintCharts(d) { var ts = d.timeSeries || []; U.charts.syncHero('ov-chart-tasks', 'tasks', ts); U.charts.syncHero('ov-chart-comp', 'comp', ts); }
105
+ return {
106
+ label: 'Overview',
107
+ render: function (root, d) { build(root, d); },
108
+ update: function (root, d) { var t = document.getElementById('ov-tiles'), s = document.getElementById('ov-summaries'); if (!t || !s) { U.clear(root); build(root, d); return; } fillTiles(t, d); fillSummaries(s, d); paintCharts(d); },
109
+ };
110
+ })());
111
+
112
+ function eventRow(ev) {
113
+ var cat = ev.category || ev.type || 'system';
114
+ var ts = ev.timestamp ? new Date(ev.timestamp).toLocaleTimeString() : '';
115
+ var title = ev.title || ev.message || '';
116
+ var detail = ev.detail || '';
117
+ var msg = (title && detail) ? (title + ' — ' + detail) : (title || detail || String((ev.data && JSON.stringify(ev.data)) || '').slice(0, 80));
118
+ return el('div', { class: 'event-row' }, el('span', { class: 'event-time', text: ts }), el('span', { class: 'event-cat cat-' + cat, text: cat }), el('span', { class: 'event-msg', text: msg }));
119
+ }
120
+
121
+ // ═══════════════════════════ TASKS & EPICS ═══════════════════════════
122
+ U.registerTab('tasks', (function () {
123
+ var TYPE_ICONS = { task: '◆', bug: '🐛', feature: '✨', epic: '🎯', chore: '🔧', story: '📖' };
124
+ var PRIO = ['P0', 'P1', 'P2', 'P3', 'P4'];
125
+ var COLS = [['open', 'Open'], ['in_progress', 'In Progress'], ['blocked', 'Blocked'], ['done', 'Done'], ['wont_do', "Won't Do"]];
126
+ var filterStatus = 'all', lastSig = null;
127
+
128
+ function openTaskDrawer(task, d) {
129
+ var body = el('div', {});
130
+ body.appendChild(kv('ID', task.id, 'cyan'));
131
+ body.appendChild(kv('Type', task.type || 'task'));
132
+ body.appendChild(kv('Status', task.status || '-'));
133
+ body.appendChild(kv('Priority', PRIO[task.priority] || 'P2'));
134
+ body.appendChild(kv('Assignee', task.assignee || 'unassigned'));
135
+ if (task.parentTitle) body.appendChild(kv('Parent', task.parentTitle));
136
+ body.appendChild(el('h3', {}, 'Title'));
137
+ body.appendChild(el('div', { class: 'muted', text: task.title || '-' }));
138
+
139
+ body.appendChild(el('h3', {}, 'Change status'));
140
+ var statusRow = el('div', { class: 'toolbar' });
141
+ ['open', 'in_progress', 'blocked', 'done', 'wont_do'].forEach(function (s) {
142
+ statusRow.appendChild(el('button', { class: 'btn' + (s === task.status ? ' btn-primary' : ''), onclick: function () { U.api('/api/tasks/' + encodeURIComponent(task.id) + '/update', { status: s }).then(function () { U.toast('Status → ' + s, 'ok'); U.closeDrawer(); }); } }, s));
143
+ });
144
+ body.appendChild(statusRow);
145
+
146
+ body.appendChild(el('h3', {}, 'Actions'));
147
+ var actions = el('div', { class: 'toolbar' });
148
+ actions.appendChild(el('button', { class: 'btn', onclick: function () {
149
+ U.form('Set assignee', [{ name: 'assignee', label: 'Assignee', value: task.assignee || '' }]).then(function (v) { if (v) U.api('/api/tasks/' + encodeURIComponent(task.id) + '/update', { assignee: v.assignee }).then(function () { U.toast('Assignee updated', 'ok'); U.closeDrawer(); }); });
150
+ } }, 'Set assignee'));
151
+ actions.appendChild(el('button', { class: 'btn', onclick: function () {
152
+ U.form('Edit title', [{ name: 'title', label: 'Title', value: task.title || '' }]).then(function (v) { if (v) U.api('/api/tasks/' + encodeURIComponent(task.id) + '/update', { title: v.title }).then(function () { U.toast('Title updated', 'ok'); U.closeDrawer(); }); });
153
+ } }, 'Edit title'));
154
+ if (task.status !== 'done') actions.appendChild(el('button', { class: 'btn btn-primary', onclick: function () { U.api('/api/tasks/' + encodeURIComponent(task.id) + '/close', {}).then(function () { U.toast('Task closed', 'ok'); U.closeDrawer(); }); } }, 'Close (done)'));
155
+ actions.appendChild(el('button', { class: 'btn btn-danger', onclick: function () {
156
+ U.confirm('Delete task ' + task.id + '? This removes its dependencies, history and activity. This cannot be undone.', { danger: true }).then(function (ok) { if (ok) U.api('/api/tasks/' + encodeURIComponent(task.id) + '/delete', {}).then(function () { U.toast('Task deleted', 'ok'); U.closeDrawer(); }); });
157
+ } }, 'Delete'));
158
+ body.appendChild(actions);
159
+
160
+ var epicLed = d.orchestrationTree && d.orchestrationTree.ledger;
161
+ if (task.type === 'epic' && epicLed && epicLed.items) {
162
+ body.appendChild(el('h3', {}, 'Epic ledger'));
163
+ body.appendChild(ledgerItems(epicLed));
164
+ }
165
+ U.drawer((TYPE_ICONS[task.type] || '◆') + ' ' + (task.title || task.id).slice(0, 40), body);
166
+ }
167
+
168
+ function card(item, d) {
169
+ var pc = 'p' + (item.priority != null ? item.priority : 2);
170
+ var crumb = (item.depth || 0) > 1 && item.parentId ? el('div', { class: 'card-crumb', text: '↳ ' + (item.parentTitle || item.parentId) }) : null;
171
+ var c = el('div', { class: 'kanban-card' + ((item.depth || 0) > 0 ? ' card-child' : ''), dataset: { id: item.id }, onclick: function () { openTaskDrawer(item, d); } },
172
+ el('div', { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center' } }, el('span', { class: 'card-id', text: item.id }), el('span', {}, TYPE_ICONS[item.type] || '◆')),
173
+ crumb,
174
+ el('div', { class: 'card-title', text: item.title || 'Untitled' }),
175
+ el('div', { class: 'card-meta' }, el('span', { class: 'card-priority ' + pc, text: PRIO[item.priority] || 'P2' }), el('span', { text: (item.type || 'task') + (item.assignee ? ' · ' + item.assignee : '') })));
176
+ return c;
177
+ }
178
+ function renderBoard(board, d) {
179
+ var tasks = d.tasks || {}, items = tasks.items || [];
180
+ if (filterStatus !== 'all') items = items.filter(function (i) { return i.status === filterStatus; });
181
+ var byCol = {}; COLS.forEach(function (c) { byCol[c[0]] = []; });
182
+ var groupCounts = {}, families = {};
183
+ items.forEach(function (i) { if (byCol[i.status]) byCol[i.status].push(i); var g = i.groupId || i.id; groupCounts[g] = (groupCounts[g] || 0) + 1; if ((i.depth || 0) > 0) families[g] = true; });
184
+ for (var g in groupCounts) if (groupCounts[g] > 1) families[g] = true;
185
+ U.clear(board);
186
+ COLS.forEach(function (col) {
187
+ var list = byCol[col[0]] || [];
188
+ var colEl = el('div', { class: 'kanban-col' }, el('div', { class: 'kanban-col-header ' + col[0] }, el('span', { text: col[1] }), el('span', { class: 'col-count', text: String(list.length) })));
189
+ var cards = el('div', { class: 'kanban-cards' });
190
+ if (!list.length) cards.appendChild(el('div', { class: 'kanban-empty', text: 'No tasks' }));
191
+ else {
192
+ var sorted = list.slice().sort(function (a, b) {
193
+ var ga = families[a.groupId || a.id] ? 0 : 1, gb = families[b.groupId || b.id] ? 0 : 1;
194
+ if (ga !== gb) return ga - gb;
195
+ if ((a.groupId || a.id) !== (b.groupId || b.id)) return String(a.groupTitle || a.title || '').localeCompare(String(b.groupTitle || b.title || '')) || String(a.groupId || a.id).localeCompare(String(b.groupId || b.id));
196
+ if ((a.depth || 0) !== (b.depth || 0)) return (a.depth || 0) - (b.depth || 0);
197
+ return (a.priority != null ? a.priority : 2) - (b.priority != null ? b.priority : 2);
198
+ });
199
+ var lastGroup = null;
200
+ sorted.forEach(function (item) {
201
+ var gid = item.groupId || item.id;
202
+ if (families[gid] && gid !== lastGroup) cards.appendChild(el('div', { class: 'kanban-group', text: '▸ ' + (item.groupTitle || gid) }));
203
+ lastGroup = gid;
204
+ cards.appendChild(card(item, d));
205
+ });
206
+ }
207
+ colEl.appendChild(cards); board.appendChild(colEl);
208
+ });
209
+ }
210
+ function build(root, d) {
211
+ var tasks = d.tasks || {};
212
+ var filterSel = el('select', { onchange: function (e) { filterStatus = e.target.value; var b = document.getElementById('kanban-board'); if (b) renderBoard(b, U.state || d); } });
213
+ [['all', 'All statuses']].concat(COLS.map(function (c) { return [c[0], c[1]]; })).forEach(function (o) { filterSel.appendChild(el('option', { value: o[0] }, o[1])); });
214
+ filterSel.value = filterStatus;
215
+ var p = panel('Task Board', [
216
+ el('button', { class: 'btn btn-primary', onclick: function () {
217
+ U.form('New task', [
218
+ { name: 'title', label: 'Title', placeholder: 'What needs doing' },
219
+ { name: 'type', label: 'Type', type: 'select', options: ['task', 'feature', 'bug', 'epic', 'chore', 'story'] },
220
+ { name: 'priority', label: 'Priority (0-4)', type: 'number', value: '2' },
221
+ { name: 'assignee', label: 'Assignee (optional)' },
222
+ ]).then(function (v) { if (v && v.title) U.api('/api/tasks', { title: v.title, type: v.type, priority: Number(v.priority) || 2, assignee: v.assignee || undefined }).then(function () { U.toast('Task created', 'ok'); }); });
223
+ } }, '+ New Task'),
224
+ ]);
225
+ var counts = el('div', { class: 'section-note', text: (tasks.total || 0) + ' tasks · ' + (tasks.done || 0) + ' done · ' + (tasks.inProgress || 0) + ' active · ' + (tasks.blocked || 0) + ' blocked · ' + (tasks.open || 0) + ' open' });
226
+ p.appendChild(counts);
227
+ p.appendChild(el('div', { class: 'toolbar' }, el('span', { class: 'label', text: 'Filter:' }), filterSel));
228
+ var board = el('div', { class: 'kanban', id: 'kanban-board' });
229
+ p.appendChild(board);
230
+ root.appendChild(p);
231
+ lastSig = sig((d.tasks || {}).items);
232
+ renderBoard(board, d);
233
+ }
234
+ return {
235
+ label: 'Tasks & Epics',
236
+ render: function (root, d) { build(root, d); },
237
+ update: function (root, d) { var b = document.getElementById('kanban-board'); if (!b) { U.clear(root); build(root, d); return; } var s = sig((d.tasks || {}).items); if (s !== lastSig) { lastSig = s; renderBoard(b, d); } },
238
+ };
239
+ })());
240
+
241
+ function ledgerItems(led) {
242
+ var wrap = el('div', {});
243
+ (led.items || []).forEach(function (it) {
244
+ var row = el('div', { class: 'kv' },
245
+ el('span', { class: 'label' }, (it.kind === 'epic' ? '🎯 ' : '• ') + esc0(it.title || it.id), el('span', { class: 'mono-sm', text: ' ' + (it.status || '') })),
246
+ el('span', {},
247
+ el('button', { class: 'btn', title: 'Mark done', onclick: function () { U.api('/api/ledger/item/' + encodeURIComponent(it.id), { status: 'done' }).then(function () { U.toast('Item done', 'ok'); }); } }, '✓'),
248
+ ' ',
249
+ el('button', { class: 'btn btn-danger', title: 'Mark failed', onclick: function () { U.api('/api/ledger/item/' + encodeURIComponent(it.id), { status: 'failed' }).then(function () { U.toast('Item failed', 'warn'); }); } }, '✗')));
250
+ wrap.appendChild(row);
251
+ });
252
+ return wrap;
253
+ }
254
+ function esc0(s) { return s == null ? '' : String(s); }
255
+
256
+ // ═══════════════════════════ AGENTS & SESSIONS ═══════════════════════════
257
+ U.registerTab('agents', (function () {
258
+ var lastSig = null;
259
+ function mergeAgents(d) {
260
+ var out = {}, order = [];
261
+ var coord = (d.coordination && d.coordination.agents) || [];
262
+ coord.forEach(function (a) { out[a.id] = { id: a.id, name: a.name, status: a.status, task: a.task || a.type || '', type: a.type || 'main' }; order.push(a.id); });
263
+ var sess = (d.session && d.session.agents) || [];
264
+ sess.forEach(function (a) { var e = out[a.id] || { id: a.id, name: a.name, status: a.status, task: a.task, type: a.type }; e.tokensIn = a.tokensIn; e.tokensOut = a.tokensOut; e.tokensUsed = a.tokensUsed; e.model = a.model; e.cost = a.cost; e.durationMs = a.durationMs; e.taskCount = a.taskCount; if (!out[a.id]) order.push(a.id); out[a.id] = e; });
265
+ return order.map(function (id) { return out[id]; });
266
+ }
267
+ function agentDrawer(a, d) {
268
+ var body = el('div', {});
269
+ body.appendChild(kv('ID', a.id, 'cyan'));
270
+ body.appendChild(kv('Name', a.name || '-'));
271
+ body.appendChild(kv('Type', a.type || 'main'));
272
+ body.appendChild(kv('Status', a.status || '-'));
273
+ body.appendChild(kv('Task', a.task || '-'));
274
+ if (a.model) body.appendChild(kv('Model', modelName(a.model), 'purple'));
275
+ body.appendChild(el('h3', {}, 'Tokens & cost'));
276
+ body.appendChild(kv('Tokens In', fmtNum(a.tokensIn || 0), 'cyan'));
277
+ body.appendChild(kv('Tokens Out', fmtNum(a.tokensOut || 0), 'green'));
278
+ body.appendChild(kv('Total', fmtNum((a.tokensIn || 0) + (a.tokensOut || 0) || a.tokensUsed || 0), 'purple'));
279
+ body.appendChild(kv('Cost', fmtUsd(a.cost || 0), 'green'));
280
+ if (a.taskCount != null) body.appendChild(kv('Tasks', String(a.taskCount)));
281
+ if (a.durationMs) body.appendChild(kv('Duration', U.fmtDur(a.durationMs)));
282
+ var skills = (d.coordination && d.coordination.skillsPerAgent && d.coordination.skillsPerAgent[a.id]) || [];
283
+ if (skills.length) { body.appendChild(el('h3', {}, 'Skills')); var sc = el('div', { class: 'chip-list' }); skills.forEach(function (s) { sc.appendChild(el('span', { class: 'chip', text: s })); }); body.appendChild(sc); }
284
+ var pats = (d.coordination && d.coordination.patternsPerAgent && d.coordination.patternsPerAgent[a.id]) || [];
285
+ if (pats.length) { body.appendChild(el('h3', {}, 'Patterns')); var pc = el('div', { class: 'chip-list' }); pats.forEach(function (p) { pc.appendChild(el('span', { class: 'chip', text: (p.id || p) + (p.uses ? ' ×' + p.uses : '') })); }); body.appendChild(pc); }
286
+ body.appendChild(el('h3', {}, 'Actions'));
287
+ body.appendChild(el('div', { class: 'toolbar' }, el('button', { class: 'btn btn-danger', onclick: function () {
288
+ U.confirm('Deregister agent ' + (a.name || a.id) + '? Marks its registry row completed and releases its claims (does not kill the OS process).', { danger: true, okLabel: 'Deregister' }).then(function (ok) { if (ok) U.api('/api/agents/' + encodeURIComponent(a.id) + '/deregister', {}).then(function () { U.toast('Agent deregistered', 'ok'); U.closeDrawer(); }); });
289
+ } }, 'Deregister')));
290
+ U.drawer('Agent: ' + (a.name || a.id).slice(0, 40), body);
291
+ }
292
+ function sessionDrawer(s, d) {
293
+ var body = el('div', {});
294
+ var isLive = d.session && d.session.sessionId === s.sessionId;
295
+ var src = isLive ? d.session : s;
296
+ body.appendChild(kv('Session', s.sessionId, 'cyan'));
297
+ body.appendChild(kv('Status', s.status || '-'));
298
+ body.appendChild(kv('Model', modelName(s.model || (src.modelBreakdown && src.modelBreakdown[0] && src.modelBreakdown[0].modelId) || '-'), 'purple'));
299
+ body.appendChild(kv('Tokens In', fmtNum(s.tokensIn || src.tokensIn || 0), 'cyan'));
300
+ body.appendChild(kv('Tokens Out', fmtNum(s.tokensOut || src.tokensOut || 0), 'green'));
301
+ body.appendChild(kv('Cost', fmtUsd(s.totalCost != null ? s.totalCost : src.totalCostUsd || 0), 'green'));
302
+ body.appendChild(kv('Agents', String(s.agentCount != null ? s.agentCount : (src.agents ? src.agents.length : 0))));
303
+ body.appendChild(kv('Tasks', String(s.taskCount || 0)));
304
+ if (isLive && src.agents && src.agents.length) {
305
+ body.appendChild(el('h3', {}, 'Agents (live session)'));
306
+ body.appendChild(tableNode(['Agent', 'Model', 'In', 'Out', 'Cost'], src.agents, function (a) { return [a.name || a.id, modelName(a.model || '?'), fmtNum(a.tokensIn || 0), fmtNum(a.tokensOut || 0), fmtUsd(a.cost || 0)]; }));
307
+ }
308
+ U.drawer('Session: ' + (s.sessionId || '').slice(0, 20), body);
309
+ }
310
+ function build(root, d) {
311
+ var agents = mergeAgents(d);
312
+ var ap = panel('Agents', [el('button', { class: 'btn', onclick: function () { U.confirm('Clean up stale agents (stale heartbeats)?').then(function (ok) { if (ok) U.api('/api/agents/clean', {}).then(function (r) { U.toast('Cleaned ' + ((r && r.cleaned) || 0) + ' stale agents', 'ok'); }); }); } }, 'Clean stale')]);
313
+ if (!agents.length) ap.appendChild(empty('No agents registered'));
314
+ else { var grid = el('div', { class: 'card-grid' }); agents.forEach(function (a) {
315
+ grid.appendChild(el('div', { class: 'entity-card', tabindex: '0', role: 'button', onclick: function () { agentDrawer(a, d); }, onkeydown: function (e) { if (e.key === 'Enter') agentDrawer(a, d); } },
316
+ el('div', { class: 'ec-head' }, el('span', { class: 'badge ' + (a.type || 'main') }, a.type || 'agent'), el('span', { class: 'ec-name', text: a.name || a.id }), statusBadge(a.status)),
317
+ el('div', { class: 'ec-task', text: a.task || '-' }),
318
+ el('div', { class: 'ec-metrics' }, el('span', {}, fmtNum((a.tokensIn || 0) + (a.tokensOut || 0) || a.tokensUsed || 0) + ' tok'), a.model ? el('span', { class: 'muted', text: modelName(a.model) }) : null, el('span', {}, fmtUsd(a.cost || 0)))));
319
+ }); ap.appendChild(grid); }
320
+ root.appendChild(ap);
321
+
322
+ var sessions = d.sessions || [];
323
+ var sp = panel('Session History');
324
+ sp.appendChild(sessions.length ? tableNode(['Session', 'Status', 'Model', 'In', 'Out', 'Cost', 'Agents', 'Tasks'], sessions, function (s) {
325
+ return [el('span', {}, (s.sessionId || '-').slice(0, 14), s.status === 'active' ? el('span', { class: 'mono-sm', text: ' (live)' }) : null), statusBadge(s.status), modelName(s.model || '-'), fmtNum(s.tokensIn || 0), fmtNum(s.tokensOut || 0), fmtUsd(s.totalCost || 0), String(s.agentCount || 0), String(s.taskCount || 0)];
326
+ }, function (s) { sessionDrawer(s, d); }) : empty('No session history'));
327
+ root.appendChild(sp);
328
+ }
329
+ return {
330
+ label: 'Agents & Sessions',
331
+ render: function (root, d) { build(root, d); },
332
+ update: function (root, d) { var s = sig({ a: (d.coordination || {}).agents, se: (d.session || {}).agents, h: d.sessions }); if (s !== lastSig) { lastSig = s; U.clear(root); build(root, d); } },
333
+ };
334
+ })());
335
+
336
+ // ═══════════════════════════ ORCHESTRATION ═══════════════════════════
337
+ U.registerTab('orchestration', (function () {
338
+ function node(n, depth) {
339
+ var sc = { done: 'green', in_progress: 'cyan', blocked: 'red', failed: 'red', open: '', pending: '' }[n.status] || '';
340
+ var icon = n.type === 'epic' ? '🎯' : n.type === 'feature' ? '✨' : n.type === 'phase' ? '▶' : '•';
341
+ var agents = (n.agents && n.agents.length) ? el('span', { class: 'mono-sm', text: ' [' + n.agents.join(', ') + ']' }) : null;
342
+ var line = el('div', { style: { padding: '3px 0 3px ' + (depth * 16) + 'px', borderLeft: '1px solid var(--border)' } },
343
+ el('span', { class: 'value ' + sc, text: '●' }), ' ' + icon + ' ', el('span', { text: (n.title || n.id).slice(0, 70) }), el('span', { class: 'mono-sm', text: ' ' + (n.status || '') }), agents);
344
+ var wrap = el('div', {}, line);
345
+ (n.children || []).forEach(function (c) { wrap.appendChild(node(c, depth + 1)); });
346
+ return wrap;
347
+ }
348
+ function build(root, d) {
349
+ var ot = d.orchestrationTree || { missions: [], ledger: null, agents: [], hasHierarchy: false };
350
+ var cur = d.orchestrate || 'auto';
351
+ var p = panel('Orchestrator');
352
+ var ctl = el('div', { class: 'toolbar' }, el('span', { class: 'label', text: 'Mode:' }));
353
+ ['on', 'off', 'auto'].forEach(function (st) { ctl.appendChild(el('button', { class: 'btn' + (st === cur ? ' btn-primary' : ''), onclick: function () { U.api('/api/orchestrator', { state: st }).then(function () { U.toast('Orchestrator: ' + st, 'ok'); }); } }, st)); });
354
+ p.appendChild(ctl);
355
+ p.appendChild(el('div', { class: 'section-note', text: (ot.missions || []).length + ' root mission(s) · ' + (ot.agents || []).length + ' agent(s)' }));
356
+ root.appendChild(p);
357
+
358
+ var led = ot.ledger;
359
+ if (led && led.total) {
360
+ var lp = panel('Active Build Ledger');
361
+ var pct = led.pct != null ? led.pct : Math.round((led.done / led.total) * 100);
362
+ lp.appendChild(el('div', { class: 'kv' }, el('span', { class: 'label', text: (led.mission || '-').slice(0, 70) }), el('span', { class: 'value cyan', text: led.done + '/' + led.total + ' (' + pct + '%)' })));
363
+ lp.appendChild(el('div', { class: 'progress-track' }, el('div', { class: 'progress-fill', style: { width: pct + '%' } })));
364
+ lp.appendChild(ledgerItems(led));
365
+ lp.appendChild(el('div', { class: 'toolbar' }, el('button', { class: 'btn btn-danger', onclick: function () { U.confirm('Reset the completion ledger? Clears the active multi-epic build state.', { danger: true, okLabel: 'Reset' }).then(function (ok) { if (ok) U.api('/api/ledger/reset', {}).then(function () { U.toast('Ledger reset', 'ok'); }); }); } }, 'Reset ledger')));
366
+ root.appendChild(lp);
367
+ }
368
+
369
+ var tp = panel('Mission Hierarchy');
370
+ var withKids = (ot.missions || []).filter(function (m) { return (m.children || []).length > 0; });
371
+ var shown = (withKids.length ? withKids : (ot.missions || [])).slice(0, 30);
372
+ if (!shown.length) tp.appendChild(empty('No orchestrations yet — run an epic/orchestrated build'));
373
+ else shown.forEach(function (m) { tp.appendChild(node(m, 0)); });
374
+ root.appendChild(tp);
375
+ }
376
+ return { label: 'Orchestration', render: build };
377
+ })());
378
+
379
+ // ═══════════════════════════ DELIVER ═══════════════════════════
380
+ U.registerTab('deliver', (function () {
381
+ function runDrawer(r) {
382
+ var body = el('div', {});
383
+ body.appendChild(kv('Run ID', r.runId, 'cyan'));
384
+ body.appendChild(kv('Status', r.status || '-'));
385
+ if (r.instruction || r.mission) { body.appendChild(el('h3', {}, 'Instruction')); body.appendChild(el('div', { class: 'muted', text: r.instruction || r.mission })); }
386
+ var _ph = runPhase(r); if (_ph !== '-') body.appendChild(kv('Phase', _ph));
387
+ if (r.taskId) body.appendChild(kv('Task', r.taskId));
388
+ if (r.pid) body.appendChild(kv('PID', String(r.pid)));
389
+ body.appendChild(kv('Started', U.shortTime(r.createdAt)));
390
+ body.appendChild(kv('Updated', U.timeAgo(r.updatedAt || r.createdAt)));
391
+ if (Array.isArray(r.phases) && r.phases.length) { body.appendChild(el('h3', {}, 'Phases')); r.phases.forEach(function (p, i) { body.appendChild(kv((i + 1) + '. ' + (p.title || p.id || '-'), i === (r.phaseIndex || 0) ? 'active' : '')); }); }
392
+ body.appendChild(el('h3', {}, 'Actions'));
393
+ var actions = el('div', { class: 'toolbar' });
394
+ if (r.status === 'running') actions.appendChild(el('button', { class: 'btn btn-danger', onclick: function () { U.confirm('Cancel deliver run ' + r.runId + '? Writes a stop-file (cooperative) and signals the process.', { danger: true, okLabel: 'Cancel run' }).then(function (ok) { if (ok) U.api('/api/deliver/' + encodeURIComponent(r.runId) + '/cancel', {}).then(function () { U.toast('Cancel requested', 'ok'); U.closeDrawer(); }); }); } }, 'Cancel'));
395
+ if (r.status === 'interrupted' || r.status === 'failed') actions.appendChild(el('button', { class: 'btn btn-primary', onclick: function () { U.api('/api/deliver/' + encodeURIComponent(r.runId) + '/resume', {}).then(function () { U.toast('Resume launched', 'ok'); U.closeDrawer(); }); } }, 'Resume'));
396
+ body.appendChild(actions);
397
+ U.drawer('Deliver run ' + (r.runId || '').slice(0, 16), body);
398
+ }
399
+ function build(root, d) {
400
+ var runs = d.deliverRuns || [];
401
+ var p = panel('Deliver Runs', [el('button', { class: 'btn btn-primary', onclick: function () {
402
+ U.form('Launch deliver run', [
403
+ { name: 'instruction', label: 'Instruction', type: 'textarea', placeholder: 'One-line description of what to build' },
404
+ { name: 'model', label: 'Model preset (optional)', placeholder: 'qwen35-a3b' },
405
+ { name: 'maxTurns', label: 'Max turns', type: 'number', value: '5' },
406
+ ]).then(function (v) {
407
+ if (!v || !v.instruction) return;
408
+ U.confirm('Launch a deliver run on this host? This spawns a `uap deliver` subprocess that writes files and runs gates.', { okLabel: 'Launch' }).then(function (ok) {
409
+ if (ok) U.api('/api/deliver/launch', { instruction: v.instruction, model: v.model || undefined, maxTurns: Number(v.maxTurns) || undefined }).then(function (res) { U.toast('Launched' + (res && res.runId ? ' (' + String(res.runId).slice(0, 10) + ')' : ''), 'ok'); });
410
+ });
411
+ });
412
+ } }, '▶ Launch run')]);
413
+ p.appendChild(el('div', { class: 'section-note', text: 'Runs are read from .uap/deliver-runs. Launch spawns a host process; cancel writes a cooperative stop-file and signals the PID.' }));
414
+ p.appendChild(runs.length ? tableNode(['Run', 'Status', 'Phase', 'Task', 'Updated', ''], runs, function (r) {
415
+ var act = el('span', {});
416
+ if (r.status === 'running') act.appendChild(el('button', { class: 'btn btn-danger', onclick: function (e) { e.stopPropagation(); U.confirm('Cancel run ' + r.runId + '?', { danger: true, okLabel: 'Cancel run' }).then(function (ok) { if (ok) U.api('/api/deliver/' + encodeURIComponent(r.runId) + '/cancel', {}).then(function () { U.toast('Cancel requested', 'ok'); }); }); } }, 'Cancel'));
417
+ else if (r.status === 'interrupted' || r.status === 'failed') act.appendChild(el('button', { class: 'btn', onclick: function (e) { e.stopPropagation(); U.api('/api/deliver/' + encodeURIComponent(r.runId) + '/resume', {}).then(function () { U.toast('Resume launched', 'ok'); }); } }, 'Resume'));
418
+ return [el('span', { class: 'mono-sm', text: (r.runId || '').slice(0, 12) }), statusBadge(r.status), runPhase(r), el('span', { class: 'mono-sm', text: r.taskId || '-' }), U.timeAgo(r.updatedAt || r.createdAt), act];
419
+ }, function (r) { runDrawer(r); }) : empty('No deliver runs tracked yet'));
420
+ root.appendChild(p);
421
+ }
422
+ return { label: 'Deliver', render: build };
423
+ })());
424
+
425
+ // ═══════════════════════════ POLICIES & COMPLIANCE ═══════════════════════════
426
+ U.registerTab('policies', (function () {
427
+ function build(root, d) {
428
+ var policies = d.policies || [], policyFiles = d.policyFiles || [], compliance = d.compliance || {}, audit = d.auditTrail || [];
429
+ var pm = {}, order = [];
430
+ policies.forEach(function (p) { var key = p.name || p.id; pm[key] = Object.assign({}, p, { source: 'db' }); order.push(key); });
431
+ policyFiles.forEach(function (pf) { if (!pm[pf.name]) { pm[pf.name] = { id: pf.filename, name: pf.name, category: pf.category, level: '-', enforcementStage: '-', isActive: null, source: 'file' }; order.push(pf.name); } });
432
+ var all = order.map(function (k) { return pm[k]; });
433
+
434
+ var pp = panel('Policies');
435
+ var thead = el('tr', {}); ['Name', 'Category', 'Level', 'Stage', 'Status', 'Actions'].forEach(function (x) { thead.appendChild(el('th', { text: x })); });
436
+ var tbl = el('table', {}, thead);
437
+ all.forEach(function (p) {
438
+ var isDb = p.source === 'db';
439
+ var actions = el('span', {});
440
+ if (isDb) {
441
+ actions.appendChild(el('button', { class: 'btn', onclick: function () { U.api('/api/policy/' + encodeURIComponent(p.id) + '/toggle', undefined).then(function (r) { U.toast('Policy ' + (r.isActive ? 'enabled' : 'disabled'), 'ok'); }); } }, p.isActive ? 'Disable' : 'Enable'));
442
+ var stageSel = el('select', { onchange: function (e) { U.api('/api/policy/' + encodeURIComponent(p.id) + '/stage', { stage: e.target.value }).then(function () { U.toast('Stage: ' + e.target.value, 'ok'); }); } });
443
+ ['pre-exec', 'post-exec', 'review', 'always'].forEach(function (s) { stageSel.appendChild(el('option', { value: s }, s)); }); stageSel.value = p.enforcementStage;
444
+ var lvlSel = el('select', { onchange: function (e) { U.api('/api/policy/' + encodeURIComponent(p.id) + '/level', { level: e.target.value }).then(function () { U.toast('Level: ' + e.target.value, 'ok'); }); } });
445
+ ['REQUIRED', 'RECOMMENDED', 'OPTIONAL'].forEach(function (l) { lvlSel.appendChild(el('option', { value: l }, l)); }); lvlSel.value = p.level;
446
+ actions.appendChild(document.createTextNode(' ')); actions.appendChild(stageSel); actions.appendChild(document.createTextNode(' ')); actions.appendChild(lvlSel);
447
+ } else actions.appendChild(el('span', { class: 'mono-sm', text: 'file-only' }));
448
+ var status = isDb ? el('span', { class: 'badge ' + (p.isActive ? 'on' : 'off') }, p.isActive ? 'ON' : 'OFF') : el('span', { class: 'badge file-only' }, 'FILE');
449
+ var tr = el('tr', {},
450
+ el('td', {}, el('span', { style: { fontWeight: '500' }, text: p.name || '-' })),
451
+ el('td', { text: p.category || '-' }),
452
+ el('td', {}, el('span', { class: 'badge ' + (p.level || '').toLowerCase() }, p.level || '-')),
453
+ el('td', { text: p.enforcementStage || '-' }),
454
+ el('td', {}, status),
455
+ el('td', {}, actions));
456
+ tbl.appendChild(tr);
457
+ });
458
+ pp.appendChild(all.length ? el('div', { class: 'table-wrap' }, tbl) : empty('No policies'));
459
+ root.appendChild(pp);
460
+
461
+ var cp = panel('Compliance & Audit');
462
+ cp.appendChild(el('h3', {}, 'Block Rate Trend'));
463
+ cp.appendChild(el('div', { class: 'chart-container chart-spark', id: 'pol-chart-block' }));
464
+ cp.appendChild(el('h3', {}, 'Failures by Mechanism'));
465
+ var fbm = compliance.failuresByMechanism || {}; var me = Object.keys(fbm).map(function (k) { return [k, fbm[k]]; }).sort(function (a, b) { return b[1] - a[1]; });
466
+ if (me.length) { var max = Math.max.apply(null, me.map(function (x) { return x[1]; }).concat([1])); var mb = el('div', {}); me.forEach(function (m) { mb.appendChild(el('div', { class: 'bar-container' }, el('span', { class: 'bar-label', style: { width: '120px' }, text: m[0] }), el('div', { class: 'bar red', style: { width: Math.round((m[1] / max) * 180) + 'px' } }), el('span', { class: 'bar-value', text: String(m[1]) }))); }); cp.appendChild(mb); }
467
+ else cp.appendChild(empty('No failure mechanisms'));
468
+ cp.appendChild(el('h3', {}, 'Recent Failures'));
469
+ var rf = compliance.recentFailures || [];
470
+ cp.appendChild(rf.length ? tableNode(['Time', 'Policy', 'Op', 'Mechanism', 'Reason'], rf.slice(0, 10), function (f) { return [el('span', { class: 'mono-sm', text: (f.executedAt || '').slice(11, 19) }), el('span', { class: 'value red', text: f.policyName || f.policyId || '-' }), f.operation || '-', el('span', { class: 'badge required' }, f.defeatedMechanism || '-'), el('span', { class: 'muted', text: (f.reason || '-').slice(0, 60) })]; }) : empty('No recent failures'));
471
+ cp.appendChild(el('h3', {}, 'Audit Trail'));
472
+ var at = el('div', {});
473
+ if (audit.length) audit.slice(0, 15).forEach(function (e) { var ts = (typeof e.executedAt === 'string' && e.executedAt.length >= 19) ? e.executedAt.slice(11, 19) : (e.executedAt || '-'); at.appendChild(el('div', { class: 'audit-row' }, el('span', { class: 'audit-time', text: ts }), el('span', { class: 'audit-icon ' + (e.allowed ? 'pass' : 'block'), text: e.allowed ? 'PASS' : 'BLOCK' }), el('span', { class: 'audit-policy', text: (e.policyId || '').slice(0, 8) }), el('span', { class: 'audit-op', text: e.operation || '' }), el('span', { class: 'audit-reason', text: e.reason || '' }))); });
474
+ else at.appendChild(empty('No audit entries'));
475
+ cp.appendChild(at);
476
+ cp.appendChild(el('h3', {}, 'Live Events'));
477
+ var feed = el('div', { class: 'event-feed', id: 'pol-live-events' });
478
+ fillEvents(feed);
479
+ cp.appendChild(feed);
480
+ root.appendChild(cp);
481
+ U.charts.syncSpark('pol-chart-block', d.timeSeries || [], U.charts.parseBR, U.charts.CC.blockRate, 'Block Rate %');
482
+ }
483
+ function fillEvents(feed) {
484
+ U.clear(feed);
485
+ var ev = (U.liveEvents || []).slice(0, 20);
486
+ if (!ev.length) { feed.appendChild(empty('Waiting for events…')); return; }
487
+ ev.forEach(function (e) { feed.appendChild(eventRow(e)); });
488
+ }
489
+ U.onEvents(function () { var f = document.getElementById('pol-live-events'); if (f) fillEvents(f); });
490
+ return { label: 'Policies', render: build };
491
+ })());
492
+
493
+ // ═══════════════════════════ MODELS ═══════════════════════════
494
+ U.registerTab('models', (function () {
495
+ function build(root, d) {
496
+ var m = d.models || { roles: {}, sessionUsage: [], totalCost: 0, strategy: 'unknown' };
497
+ var p = panel('Models & Routing');
498
+ p.appendChild(el('h3', {}, 'Roles'));
499
+ var roles = m.roles || {}; var rk = Object.keys(roles);
500
+ if (rk.length) rk.forEach(function (role) { var c = role === 'planner' ? 'green' : role === 'executor' ? 'cyan' : role === 'reviewer' ? 'purple' : ''; p.appendChild(kv(U.capitalize(role), roles[role] ? modelName(String(roles[role])) : '—', c)); });
501
+ else p.appendChild(empty('No roles configured'));
502
+ p.appendChild(el('h3', {}, 'Router'));
503
+ p.appendChild(kv('Strategy', m.strategy || 'unknown', 'cyan'));
504
+ p.appendChild(kv('Available', String((m.availableModels || []).length)));
505
+ p.appendChild(kv('Enabled', m.enabled ? 'Yes' : 'No', m.enabled ? 'green' : 'yellow'));
506
+ p.appendChild(kv('Total Cost', fmtUsd(m.totalCost || 0), 'green'));
507
+ var co = m.costOptimization || {};
508
+ p.appendChild(el('h3', {}, 'Cost Optimization'));
509
+ p.appendChild(kv('Enabled', co.enabled ? 'Yes' : 'No', co.enabled ? 'green' : 'yellow'));
510
+ p.appendChild(kv('Target Reduction', co.targetReduction ? co.targetReduction.toFixed(0) + '%' : 'N/A'));
511
+ p.appendChild(kv('Max Degradation', co.maxPerformanceDegradation ? co.maxPerformanceDegradation.toFixed(0) + '%' : 'N/A'));
512
+ root.appendChild(p);
513
+
514
+ var mx = m.routingMatrix || {}; var mk = Object.keys(mx);
515
+ var rp = panel('Routing Matrix');
516
+ if (mk.length) rp.appendChild(tableNode(['Task Type', 'Planner', 'Executor'], mk, function (k) { var v = mx[k]; var isObj = v && typeof v === 'object'; return [k, el('span', { class: 'value green', text: isObj ? modelName(v.planner || '-') : '-' }), el('span', { class: 'value cyan', text: isObj ? modelName(v.executor || '-') : modelName(String(v)) })]; }));
517
+ else rp.appendChild(empty('No routing matrix'));
518
+ root.appendChild(rp);
519
+
520
+ var rd = m.recentRoutingDecisions || [];
521
+ var dp = panel('Recent Routing Decisions');
522
+ dp.appendChild(rd.length ? tableNode(['Time', 'Model', 'Task', 'Tokens', 'Cost', 'Result'], rd.slice(0, 12), function (r) { return [el('span', { class: 'mono-sm', text: (r.timestamp || '').slice(11, 19) }), el('span', { class: 'value purple', text: modelName(r.modelUsed) }), r.taskType || '?', fmtNum((r.tokensIn || 0) + (r.tokensOut || 0)), el('span', { class: 'value green', text: fmtUsd(r.cost || 0) }), el('span', { class: 'value ' + (r.success ? 'green' : 'red'), text: r.success ? 'OK' : 'FAIL' })]; }) : empty('No routing decisions yet'));
523
+ root.appendChild(dp);
524
+
525
+ var usage = m.sessionUsage || [];
526
+ var up = panel('Session Usage by Model');
527
+ up.appendChild(usage.length ? tableNode(['Model', 'Tasks', 'In', 'Out', 'Cost', 'Success'], usage, function (u) { var rate = typeof u.successRate === 'number' ? (u.successRate * 100).toFixed(0) : '0'; return [el('span', { class: 'value cyan', text: modelName(u.modelId) }), String(u.taskCount || 0), fmtNum(u.totalTokensIn || 0), fmtNum(u.totalTokensOut || 0), fmtUsd(u.totalCost || 0), el('span', { class: 'value ' + (rate >= 90 ? 'green' : rate >= 70 ? 'yellow' : 'red'), text: rate + '%' })]; }) : empty('No usage recorded'));
528
+ root.appendChild(up);
529
+ }
530
+ return { label: 'Models', render: build };
531
+ })());
532
+
533
+ // ═══════════════════════════ MEMORY ═══════════════════════════
534
+ U.registerTab('memory', (function () {
535
+ function build(root, d) {
536
+ var mem = d.memory || {}, l1 = mem.l1 || {}, l2 = mem.l2 || {}, l3 = mem.l3 || {}, l4 = mem.l4 || {}, comp = mem.compression || {}, hm = mem.hitsMisses || {};
537
+ var g = el('div', { class: 'grid-2' });
538
+ var lp = panel('Memory Tiers');
539
+ lp.appendChild(kv('L1 Working', (l1.entries || 0) + ' entries (' + (l1.sizeKB || 0) + ' KB)'));
540
+ lp.appendChild(kv('L2 Session', (l2.entries || 0) + ' entries'));
541
+ lp.appendChild(kv('L3 Semantic', l3.status === 'Running' ? 'Qdrant ' + (l3.uptime || '') : (l3.status || 'Stopped'), l3.status === 'Running' ? 'green' : 'yellow'));
542
+ lp.appendChild(kv('L4 Knowledge', (l4.entities || 0) + ' entities, ' + (l4.relationships || 0) + ' rels'));
543
+ lp.appendChild(el('h3', {}, 'Hit Rate'));
544
+ var hrStr = String(hm.hitRate || 'N/A'); var hrVal = parseFloat(hrStr) || 0;
545
+ lp.appendChild(kv('Hits', String(hm.hits || 0), 'green'));
546
+ lp.appendChild(kv('Misses', String(hm.misses || 0), 'red'));
547
+ lp.appendChild(kv('Rate', hrStr.indexOf('%') >= 0 ? hrStr : (hrStr === 'N/A' ? hrStr : hrStr + '%'), hrVal > 80 ? 'green' : hrVal > 50 ? 'yellow' : 'red'));
548
+ lp.appendChild(el('div', { class: 'gauge-track' }, el('div', { class: 'gauge-fill', style: { width: Math.min(hrVal, 100) + '%', background: hrVal > 80 ? 'var(--green)' : hrVal > 50 ? 'var(--yellow)' : 'var(--red)' } })));
549
+ lp.appendChild(el('div', { class: 'chart-container chart-spark', id: 'mem-chart-hit' }));
550
+ g.appendChild(lp);
551
+
552
+ var cp = panel('Context Compression');
553
+ cp.appendChild(kv('Raw', fmtKB(comp.rawBytes || 0)));
554
+ cp.appendChild(kv('Compressed', fmtKB(comp.contextBytes || 0)));
555
+ cp.appendChild(kv('Savings', comp.savingsPercent || '0%', 'green'));
556
+ cp.appendChild(kv('Tool Calls', String(comp.totalCalls || 0)));
557
+ cp.appendChild(el('h3', {}, 'Recent Queries'));
558
+ var rq = mem.recentQueries || [];
559
+ cp.appendChild(rq.length ? tableNode(['Type', 'Query', 'Time'], rq.slice(0, 10), function (q) { return [el('span', { class: 'badge active' }, q.type || 'memory'), el('span', { class: 'muted', text: (q.query || '-').slice(0, 40) }), el('span', { class: 'mono-sm', text: (q.timestamp || '').slice(11, 19) })]; }) : empty('No recent queries'));
560
+ g.appendChild(cp);
561
+ root.appendChild(g);
562
+
563
+ var sv = d.savingsByInfluence || { influences: [], totalTokensSaved: 0, totalCostSavedUsd: 0 };
564
+ var sp = panel('Token Savings by Influence');
565
+ sp.appendChild(el('div', { style: { marginBottom: '12px', fontSize: '14px' } }, el('strong', { class: 'value cyan', style: { fontSize: '18px' }, text: fmtNum(sv.totalTokensSaved || 0) }), ' tokens · ', el('strong', { class: 'value green', style: { fontSize: '18px' }, text: fmtUsd(sv.totalCostSavedUsd || 0) }), ' saved across all UAP influences'));
566
+ sp.appendChild((sv.influences || []).length ? tableNode(['Influence', 'Tokens saved', 'Cost saved', 'Quality', 'Detail'], sv.influences, function (i) {
567
+ var idle = i.quality === 'unmeasured';
568
+ var tok = idle ? el('span', { class: 'muted', text: '—' }) : fmtNum(i.tokensSaved);
569
+ var cost = idle ? el('span', { class: 'muted', text: '—' }) : el('span', { class: 'value green', text: fmtUsd(i.costSavedUsd || 0) });
570
+ var qc = i.quality === 'measured' ? 'green' : i.quality === 'estimated' ? 'yellow' : '';
571
+ return [i.influence, tok, cost, el('span', { class: 'value ' + qc, text: i.quality }), el('span', { class: 'muted', text: i.detail || '' })];
572
+ }) : empty('No savings data'));
573
+ root.appendChild(sp);
574
+ U.charts.syncSpark('mem-chart-hit', d.timeSeries || [], U.charts.parseHR, U.charts.CC.hitRate, 'Hit Rate %');
575
+ }
576
+ return { label: 'Memory', render: build };
577
+ })());
578
+
579
+ // ── boot ──
580
+ U.start();
581
+ })();