web-agent-bridge 3.0.0 → 3.3.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/LICENSE +72 -21
- package/README.ar.md +1286 -1073
- package/README.md +1764 -1535
- package/bin/agent-runner.js +474 -474
- package/bin/cli.js +237 -138
- package/bin/wab.js +80 -80
- package/examples/bidi-agent.js +119 -119
- package/examples/cross-site-agent.js +91 -91
- package/examples/mcp-agent.js +94 -94
- package/examples/next-app-router/README.md +44 -44
- package/examples/puppeteer-agent.js +108 -108
- package/examples/saas-dashboard/README.md +55 -55
- package/examples/shopify-hydrogen/README.md +74 -74
- package/examples/vision-agent.js +171 -171
- package/examples/wordpress-elementor/README.md +77 -77
- package/package.json +17 -3
- package/public/.well-known/agent-tools.json +180 -180
- package/public/.well-known/ai-assets.json +59 -59
- package/public/.well-known/ai-plugin.json +28 -0
- package/public/.well-known/security.txt +8 -0
- package/public/agent-workspace.html +349 -347
- package/public/ai.html +198 -196
- package/public/api.html +413 -0
- package/public/browser.html +486 -484
- package/public/commander-dashboard.html +243 -243
- package/public/cookies.html +210 -208
- package/public/css/agent-workspace.css +1713 -1713
- package/public/css/premium.css +317 -317
- package/public/css/styles.css +1235 -1235
- package/public/dashboard.html +706 -704
- package/public/demo.html +1770 -1
- package/public/dns.html +507 -0
- package/public/docs.html +587 -585
- package/public/feed.xml +89 -89
- package/public/growth.html +463 -0
- package/public/index.html +341 -9
- package/public/integrations.html +556 -0
- package/public/js/agent-workspace.js +1740 -1740
- package/public/js/auth-nav.js +31 -31
- package/public/js/auth-redirect.js +12 -12
- package/public/js/cookie-consent.js +56 -56
- package/public/js/wab-demo-page.js +721 -721
- package/public/js/ws-client.js +74 -74
- package/public/llms-full.txt +360 -309
- package/public/llms.txt +125 -86
- package/public/login.html +85 -83
- package/public/mesh-dashboard.html +328 -328
- package/public/openapi.json +580 -580
- package/public/phone-shield.html +281 -0
- package/public/premium-dashboard.html +2489 -2487
- package/public/premium.html +793 -791
- package/public/privacy.html +297 -295
- package/public/register.html +105 -103
- package/public/robots.txt +87 -87
- package/public/script/wab-consent.d.ts +36 -36
- package/public/script/wab-consent.js +104 -104
- package/public/script/wab-schema.js +131 -131
- package/public/script/wab.d.ts +108 -108
- package/public/script/wab.min.js +580 -580
- package/public/security.txt +8 -0
- package/public/terms.html +256 -254
- package/script/ai-agent-bridge.js +1754 -1754
- package/sdk/README.md +99 -99
- package/sdk/agent-mesh.js +449 -449
- package/sdk/commander.js +262 -262
- package/sdk/index.d.ts +464 -464
- package/sdk/index.js +18 -1
- package/sdk/multi-agent.js +318 -318
- package/sdk/package.json +12 -1
- package/sdk/safety-shield.js +219 -0
- package/sdk/schema-discovery.js +83 -83
- package/server/adapters/index.js +520 -520
- package/server/config/plans.js +367 -367
- package/server/config/secrets.js +102 -102
- package/server/control-plane/index.js +301 -301
- package/server/data-plane/index.js +354 -354
- package/server/index.js +175 -19
- package/server/llm/index.js +404 -404
- package/server/middleware/adminAuth.js +35 -35
- package/server/middleware/auth.js +50 -50
- package/server/middleware/featureGate.js +88 -88
- package/server/middleware/rateLimits.js +100 -100
- package/server/middleware/sensitiveAction.js +157 -0
- package/server/migrations/001_add_analytics_indexes.sql +7 -7
- package/server/migrations/002_premium_features.sql +418 -418
- package/server/migrations/003_ads_integer_cents.sql +33 -33
- package/server/migrations/004_agent_os.sql +158 -158
- package/server/migrations/005_marketplace_metering.sql +126 -126
- package/server/models/adapters/index.js +33 -33
- package/server/models/adapters/mysql.js +183 -183
- package/server/models/adapters/postgresql.js +172 -172
- package/server/models/adapters/sqlite.js +7 -7
- package/server/models/db.js +681 -681
- package/server/observability/failure-analysis.js +337 -337
- package/server/observability/index.js +394 -394
- package/server/protocol/capabilities.js +223 -223
- package/server/protocol/index.js +243 -243
- package/server/protocol/schema.js +584 -584
- package/server/registry/certification.js +271 -271
- package/server/registry/index.js +326 -326
- package/server/routes/admin-premium.js +671 -671
- package/server/routes/admin.js +261 -261
- package/server/routes/ads.js +130 -130
- package/server/routes/agent-workspace.js +540 -378
- package/server/routes/api.js +150 -150
- package/server/routes/auth.js +71 -71
- package/server/routes/billing.js +45 -45
- package/server/routes/commander.js +316 -316
- package/server/routes/demo-showcase.js +332 -0
- package/server/routes/demo-store.js +154 -0
- package/server/routes/discovery.js +417 -406
- package/server/routes/gateway.js +173 -0
- package/server/routes/license.js +251 -240
- package/server/routes/mesh.js +469 -469
- package/server/routes/noscript.js +543 -543
- package/server/routes/premium-v2.js +686 -686
- package/server/routes/premium.js +724 -724
- package/server/routes/runtime.js +2148 -2147
- package/server/routes/sovereign.js +465 -385
- package/server/routes/universal.js +200 -177
- package/server/routes/wab-api.js +850 -491
- package/server/runtime/container-worker.js +111 -111
- package/server/runtime/container.js +448 -448
- package/server/runtime/distributed-worker.js +362 -362
- package/server/runtime/event-bus.js +210 -210
- package/server/runtime/index.js +253 -253
- package/server/runtime/queue.js +599 -599
- package/server/runtime/replay.js +666 -666
- package/server/runtime/sandbox.js +266 -266
- package/server/runtime/scheduler.js +534 -534
- package/server/runtime/session-engine.js +293 -293
- package/server/runtime/state-manager.js +188 -188
- package/server/security/cross-site-redactor.js +196 -0
- package/server/security/dry-run.js +180 -0
- package/server/security/human-gate-rate-limit.js +147 -0
- package/server/security/human-gate-transports.js +178 -0
- package/server/security/human-gate.js +281 -0
- package/server/security/index.js +368 -368
- package/server/security/intent-engine.js +245 -0
- package/server/security/reward-guard.js +171 -0
- package/server/security/rollback-store.js +239 -0
- package/server/security/token-scope.js +404 -0
- package/server/security/url-policy.js +139 -0
- package/server/services/agent-chat.js +506 -506
- package/server/services/agent-learning.js +601 -575
- package/server/services/agent-memory.js +625 -625
- package/server/services/agent-mesh.js +555 -539
- package/server/services/agent-symphony.js +717 -717
- package/server/services/agent-tasks.js +1807 -1807
- package/server/services/api-key-engine.js +292 -0
- package/server/services/cluster.js +894 -894
- package/server/services/commander.js +738 -738
- package/server/services/edge-compute.js +440 -440
- package/server/services/email.js +204 -204
- package/server/services/hosted-runtime.js +205 -205
- package/server/services/lfd.js +635 -616
- package/server/services/local-ai.js +389 -389
- package/server/services/marketplace.js +270 -270
- package/server/services/metering.js +182 -182
- package/server/services/modules/affiliate-intelligence.js +93 -0
- package/server/services/modules/agent-firewall.js +90 -0
- package/server/services/modules/bounty.js +89 -0
- package/server/services/modules/collective-bargaining.js +92 -0
- package/server/services/modules/dark-pattern.js +66 -0
- package/server/services/modules/gov-intelligence.js +45 -0
- package/server/services/modules/neural.js +55 -0
- package/server/services/modules/notary.js +49 -0
- package/server/services/modules/price-time-machine.js +86 -0
- package/server/services/modules/protocol.js +104 -0
- package/server/services/negotiation.js +439 -439
- package/server/services/plugins.js +771 -771
- package/server/services/premium.js +1 -1
- package/server/services/price-intelligence.js +566 -565
- package/server/services/price-shield.js +1137 -1137
- package/server/services/reputation.js +465 -465
- package/server/services/search-engine.js +357 -357
- package/server/services/security.js +513 -513
- package/server/services/self-healing.js +843 -843
- package/server/services/sovereign-shield.js +542 -0
- package/server/services/stripe.js +192 -192
- package/server/services/swarm.js +788 -788
- package/server/services/universal-scraper.js +662 -661
- package/server/services/verification.js +481 -481
- package/server/services/vision.js +1163 -1163
- package/server/utils/cache.js +125 -125
- package/server/utils/migrate.js +81 -81
- package/server/utils/safe-fetch.js +228 -0
- package/server/utils/secureFields.js +50 -50
- package/server/ws.js +161 -161
- package/templates/artisan-marketplace.yaml +104 -104
- package/templates/book-price-scout.yaml +98 -98
- package/templates/electronics-price-tracker.yaml +108 -108
- package/templates/flight-deal-hunter.yaml +113 -113
- package/templates/freelancer-direct.yaml +116 -116
- package/templates/grocery-price-compare.yaml +93 -93
- package/templates/hotel-direct-booking.yaml +113 -113
- package/templates/local-services.yaml +98 -98
- package/templates/olive-oil-tunisia.yaml +88 -88
- package/templates/organic-farm-fresh.yaml +101 -101
- package/templates/restaurant-direct.yaml +97 -97
- package/server/services/fairness-engine.js +0 -409
- package/server/services/fairness.js +0 -420
|
@@ -1,328 +1,328 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="UTF-8">
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
-
<title>Private Agent Mesh — Dashboard</title>
|
|
7
|
-
<style>
|
|
8
|
-
*{margin:0;padding:0;box-sizing:border-box}
|
|
9
|
-
body{font-family:'Segoe UI',system-ui,sans-serif;background:#0f1117;color:#e1e4ea;min-height:100vh}
|
|
10
|
-
.header{background:linear-gradient(135deg,#1a1d29 0%,#252a3a 100%);padding:24px 32px;border-bottom:1px solid #2a2f3f;display:flex;justify-content:space-between;align-items:center}
|
|
11
|
-
.header h1{font-size:22px;color:#a78bfa;font-weight:600}
|
|
12
|
-
.header .meta{font-size:13px;color:#888;display:flex;gap:20px}
|
|
13
|
-
.header .meta span{display:flex;align-items:center;gap:6px}
|
|
14
|
-
.dot{width:8px;height:8px;border-radius:50%;background:#10b981;display:inline-block}
|
|
15
|
-
.tabs{display:flex;background:#1a1d29;border-bottom:1px solid #2a2f3f;padding:0 32px;gap:4px}
|
|
16
|
-
.tab{padding:12px 20px;cursor:pointer;border-bottom:2px solid transparent;color:#888;transition:.2s;font-size:14px}
|
|
17
|
-
.tab:hover{color:#e1e4ea;background:#ffffff08}
|
|
18
|
-
.tab.active{color:#a78bfa;border-bottom-color:#a78bfa}
|
|
19
|
-
.content{padding:24px 32px;max-width:1400px}
|
|
20
|
-
.panel{display:none}
|
|
21
|
-
.panel.active{display:block}
|
|
22
|
-
.grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(200px,1fr));gap:16px;margin-bottom:24px}
|
|
23
|
-
.card{background:#1a1d29;border:1px solid #2a2f3f;border-radius:10px;padding:18px}
|
|
24
|
-
.card .label{font-size:12px;color:#888;text-transform:uppercase;letter-spacing:.5px;margin-bottom:6px}
|
|
25
|
-
.card .value{font-size:28px;font-weight:700;color:#e1e4ea}
|
|
26
|
-
.card .sub{font-size:12px;color:#666;margin-top:4px}
|
|
27
|
-
table{width:100%;border-collapse:collapse;margin-top:12px}
|
|
28
|
-
th{text-align:left;font-size:12px;color:#888;text-transform:uppercase;letter-spacing:.5px;padding:10px 14px;border-bottom:1px solid #2a2f3f}
|
|
29
|
-
td{padding:10px 14px;border-bottom:1px solid #1e2230;font-size:14px}
|
|
30
|
-
tr:hover{background:#ffffff05}
|
|
31
|
-
.badge{display:inline-block;padding:2px 10px;border-radius:12px;font-size:12px;font-weight:500}
|
|
32
|
-
.badge-green{background:#10b98120;color:#10b981}
|
|
33
|
-
.badge-yellow{background:#f59e0b20;color:#f59e0b}
|
|
34
|
-
.badge-red{background:#ef444420;color:#ef4444}
|
|
35
|
-
.badge-blue{background:#3b82f620;color:#3b82f6}
|
|
36
|
-
.badge-purple{background:#a78bfa20;color:#a78bfa}
|
|
37
|
-
.chart-bar{display:flex;align-items:flex-end;gap:3px;height:80px;margin-top:8px}
|
|
38
|
-
.chart-bar .bar{flex:1;background:linear-gradient(to top,#a78bfa,#7c3aed);border-radius:3px 3px 0 0;min-width:4px;transition:height .3s}
|
|
39
|
-
.section{margin-bottom:28px}
|
|
40
|
-
.section h3{font-size:16px;color:#ccc;margin-bottom:12px;display:flex;align-items:center;gap:8px}
|
|
41
|
-
.empty{color:#555;font-style:italic;padding:20px 0}
|
|
42
|
-
.refresh-btn{background:#a78bfa20;border:1px solid #a78bfa40;color:#a78bfa;padding:6px 14px;border-radius:6px;cursor:pointer;font-size:13px}
|
|
43
|
-
.refresh-btn:hover{background:#a78bfa30}
|
|
44
|
-
</style>
|
|
45
|
-
</head>
|
|
46
|
-
<body>
|
|
47
|
-
<div class="header">
|
|
48
|
-
<h1>🛰 Private Agent Mesh</h1>
|
|
49
|
-
<div class="meta">
|
|
50
|
-
<span><span class="dot"></span> <span id="status-text">Connecting...</span></span>
|
|
51
|
-
<span>Last update: <span id="last-update">—</span></span>
|
|
52
|
-
<button class="refresh-btn" onclick="loadAll()">↻ Refresh</button>
|
|
53
|
-
</div>
|
|
54
|
-
</div>
|
|
55
|
-
|
|
56
|
-
<div class="tabs">
|
|
57
|
-
<div class="tab active" data-tab="agents">Agents</div>
|
|
58
|
-
<div class="tab" data-tab="messages">Messages</div>
|
|
59
|
-
<div class="tab" data-tab="knowledge">Knowledge</div>
|
|
60
|
-
<div class="tab" data-tab="learning">Learning</div>
|
|
61
|
-
<div class="tab" data-tab="symphony">Symphony</div>
|
|
62
|
-
</div>
|
|
63
|
-
|
|
64
|
-
<div class="content">
|
|
65
|
-
<!-- AGENTS TAB -->
|
|
66
|
-
<div class="panel active" id="panel-agents">
|
|
67
|
-
<div class="grid" id="agent-stats"></div>
|
|
68
|
-
<div class="section">
|
|
69
|
-
<h3>Active Agents</h3>
|
|
70
|
-
<table><thead><tr><th>Name</th><th>Role</th><th>Status</th><th>Capabilities</th><th>Sent</th><th>Recv</th><th>Knowledge</th><th>Last Heartbeat</th></tr></thead>
|
|
71
|
-
<tbody id="agents-table"></tbody></table>
|
|
72
|
-
</div>
|
|
73
|
-
</div>
|
|
74
|
-
|
|
75
|
-
<!-- MESSAGES TAB -->
|
|
76
|
-
<div class="panel" id="panel-messages">
|
|
77
|
-
<div class="grid" id="msg-stats"></div>
|
|
78
|
-
<div class="section">
|
|
79
|
-
<h3>Recent Messages</h3>
|
|
80
|
-
<table><thead><tr><th>Type</th><th>Subject</th><th>Sender</th><th>Channel</th><th>Priority</th><th>Time</th></tr></thead>
|
|
81
|
-
<tbody id="messages-table"></tbody></table>
|
|
82
|
-
</div>
|
|
83
|
-
</div>
|
|
84
|
-
|
|
85
|
-
<!-- KNOWLEDGE TAB -->
|
|
86
|
-
<div class="panel" id="panel-knowledge">
|
|
87
|
-
<div class="grid" id="knowledge-stats"></div>
|
|
88
|
-
<div class="section">
|
|
89
|
-
<h3>Knowledge Domains</h3>
|
|
90
|
-
<div id="knowledge-domains"></div>
|
|
91
|
-
</div>
|
|
92
|
-
<div class="section">
|
|
93
|
-
<h3>Recent Knowledge</h3>
|
|
94
|
-
<table><thead><tr><th>Key</th><th>Type</th><th>Domain</th><th>Confidence</th><th>Verified</th><th>Agent</th><th>Updated</th></tr></thead>
|
|
95
|
-
<tbody id="knowledge-table"></tbody></table>
|
|
96
|
-
</div>
|
|
97
|
-
</div>
|
|
98
|
-
|
|
99
|
-
<!-- LEARNING TAB -->
|
|
100
|
-
<div class="panel" id="panel-learning">
|
|
101
|
-
<div class="grid" id="learning-stats"></div>
|
|
102
|
-
<div class="section">
|
|
103
|
-
<h3>Reward History</h3>
|
|
104
|
-
<div class="chart-bar" id="reward-chart"></div>
|
|
105
|
-
</div>
|
|
106
|
-
</div>
|
|
107
|
-
|
|
108
|
-
<!-- SYMPHONY TAB -->
|
|
109
|
-
<div class="panel" id="panel-symphony">
|
|
110
|
-
<div class="grid" id="symphony-stats"></div>
|
|
111
|
-
<div class="section">
|
|
112
|
-
<h3>Recent Compositions</h3>
|
|
113
|
-
<table><thead><tr><th>Template</th><th>Status</th><th>Phases</th><th>Duration</th><th>Agents</th><th>Created</th></tr></thead>
|
|
114
|
-
<tbody id="symphony-table"></tbody></table>
|
|
115
|
-
</div>
|
|
116
|
-
</div>
|
|
117
|
-
</div>
|
|
118
|
-
|
|
119
|
-
<script>
|
|
120
|
-
// XSS-safe text escaping
|
|
121
|
-
function esc(str) {
|
|
122
|
-
if (str == null) return '';
|
|
123
|
-
const d = document.createElement('div');
|
|
124
|
-
d.textContent = String(str);
|
|
125
|
-
return d.innerHTML;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
function badge(text, color) { return '<span class="badge badge-' + color + '">' + esc(text) + '</span>'; }
|
|
129
|
-
function timeAgo(ts) {
|
|
130
|
-
if (!ts) return '—';
|
|
131
|
-
const d = Date.now() - new Date(ts).getTime();
|
|
132
|
-
if (d < 60000) return Math.floor(d / 1000) + 's ago';
|
|
133
|
-
if (d < 3600000) return Math.floor(d / 60000) + 'm ago';
|
|
134
|
-
return Math.floor(d / 3600000) + 'h ago';
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
const BASE = '';
|
|
138
|
-
|
|
139
|
-
async function api(path) {
|
|
140
|
-
try {
|
|
141
|
-
const r = await fetch(BASE + '/api/mesh' + path);
|
|
142
|
-
if (!r.ok) return null;
|
|
143
|
-
return r.json();
|
|
144
|
-
} catch (_) { return null; }
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
// ─── Tab switching ──────────────────────────────────────────────
|
|
148
|
-
document.querySelectorAll('.tab').forEach(t => {
|
|
149
|
-
t.addEventListener('click', () => {
|
|
150
|
-
document.querySelectorAll('.tab').forEach(x => x.classList.remove('active'));
|
|
151
|
-
document.querySelectorAll('.panel').forEach(x => x.classList.remove('active'));
|
|
152
|
-
t.classList.add('active');
|
|
153
|
-
document.getElementById('panel-' + t.dataset.tab).classList.add('active');
|
|
154
|
-
});
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
// ─── Load Agents Tab ────────────────────────────────────────────
|
|
158
|
-
async function loadAgents() {
|
|
159
|
-
const [ad, sd] = await Promise.all([api('/agents'), api('/stats')]);
|
|
160
|
-
const agents = ad?.agents || [];
|
|
161
|
-
const stats = sd?.stats || {};
|
|
162
|
-
|
|
163
|
-
document.getElementById('agent-stats').innerHTML =
|
|
164
|
-
'<div class="card"><div class="label">Active Agents</div><div class="value">' + agents.length + '</div></div>' +
|
|
165
|
-
'<div class="card"><div class="label">Total Messages</div><div class="value">' + (stats.totalMessages || 0) + '</div></div>' +
|
|
166
|
-
'<div class="card"><div class="label">Knowledge Items</div><div class="value">' + (stats.totalKnowledge || 0) + '</div></div>' +
|
|
167
|
-
'<div class="card"><div class="label">Channels</div><div class="value">' + (stats.channelCount || 0) + '</div></div>';
|
|
168
|
-
|
|
169
|
-
let html = '';
|
|
170
|
-
for (const a of agents) {
|
|
171
|
-
const statusColor = a.status === 'active' ? 'green' : a.status === 'idle' ? 'yellow' : 'red';
|
|
172
|
-
const caps = JSON.parse(a.capabilities || '[]');
|
|
173
|
-
html += '<tr>' +
|
|
174
|
-
'<td>' + esc(a.display_name || a.id.slice(0, 8)) + '</td>' +
|
|
175
|
-
'<td>' + badge(a.agent_role, 'purple') + '</td>' +
|
|
176
|
-
'<td>' + badge(a.status, statusColor) + '</td>' +
|
|
177
|
-
'<td>' + caps.map(c => esc(c)).join(', ') + '</td>' +
|
|
178
|
-
'<td>' + a.messages_sent + '</td>' +
|
|
179
|
-
'<td>' + a.messages_received + '</td>' +
|
|
180
|
-
'<td>' + a.knowledge_count + '</td>' +
|
|
181
|
-
'<td>' + timeAgo(a.last_heartbeat) + '</td></tr>';
|
|
182
|
-
}
|
|
183
|
-
document.getElementById('agents-table').innerHTML = html || '<tr><td colspan="8" class="empty">No active agents</td></tr>';
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
// ─── Load Messages Tab ──────────────────────────────────────────
|
|
187
|
-
async function loadMessages() {
|
|
188
|
-
const [md, sd] = await Promise.all([api('/messages?limit=50'), api('/stats')]);
|
|
189
|
-
const msgs = md?.messages || [];
|
|
190
|
-
const stats = sd?.stats || {};
|
|
191
|
-
|
|
192
|
-
document.getElementById('msg-stats').innerHTML =
|
|
193
|
-
'<div class="card"><div class="label">Total Messages</div><div class="value">' + (stats.totalMessages || 0) + '</div></div>' +
|
|
194
|
-
'<div class="card"><div class="label">Alerts</div><div class="value">' + msgs.filter(m => m.message_type === 'alert').length + '</div></div>';
|
|
195
|
-
|
|
196
|
-
let html = '';
|
|
197
|
-
for (const m of msgs) {
|
|
198
|
-
const typeColor = m.message_type === 'alert' ? 'red' : m.message_type === 'vote' ? 'blue' : 'green';
|
|
199
|
-
html += '<tr>' +
|
|
200
|
-
'<td>' + badge(m.message_type, typeColor) + '</td>' +
|
|
201
|
-
'<td>' + esc(m.subject) + '</td>' +
|
|
202
|
-
'<td>' + esc(m.sender_id?.slice(0, 8)) + '</td>' +
|
|
203
|
-
'<td>' + esc(m.channel_id?.slice(0, 8) || 'general') + '</td>' +
|
|
204
|
-
'<td>' + (m.priority || 0) + '</td>' +
|
|
205
|
-
'<td>' + timeAgo(m.created_at) + '</td></tr>';
|
|
206
|
-
}
|
|
207
|
-
document.getElementById('messages-table').innerHTML = html || '<tr><td colspan="6" class="empty">No messages yet</td></tr>';
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
// ─── Load Knowledge Tab ─────────────────────────────────────────
|
|
211
|
-
async function loadKnowledge() {
|
|
212
|
-
const [kd, dd] = await Promise.all([api('/knowledge-recent?limit=30'), api('/knowledge-domains')]);
|
|
213
|
-
const items = kd?.knowledge || [];
|
|
214
|
-
const domains = dd?.domains || [];
|
|
215
|
-
|
|
216
|
-
document.getElementById('knowledge-stats').innerHTML =
|
|
217
|
-
'<div class="card"><div class="label">Knowledge Items</div><div class="value">' + items.length + '</div></div>' +
|
|
218
|
-
'<div class="card"><div class="label">Domains</div><div class="value">' + domains.length + '</div></div>' +
|
|
219
|
-
'<div class="card"><div class="label">Avg Confidence</div><div class="value">' +
|
|
220
|
-
(items.length ? (items.reduce((s, k) => s + k.confidence, 0) / items.length).toFixed(2) : '—') + '</div></div>';
|
|
221
|
-
|
|
222
|
-
// Domains cards
|
|
223
|
-
let domHtml = '<div class="grid">';
|
|
224
|
-
for (const d of domains) {
|
|
225
|
-
domHtml += '<div class="card"><div class="label">' + esc(d.domain || 'global') + '</div><div class="value">' + d.count + '</div>' +
|
|
226
|
-
'<div class="sub">avg conf: ' + (d.avg_confidence || 0).toFixed(2) + '</div></div>';
|
|
227
|
-
}
|
|
228
|
-
domHtml += '</div>';
|
|
229
|
-
document.getElementById('knowledge-domains').innerHTML = domHtml || '<div class="empty">No domains yet</div>';
|
|
230
|
-
|
|
231
|
-
// Knowledge table
|
|
232
|
-
let html = '';
|
|
233
|
-
for (const k of items) {
|
|
234
|
-
const confColor = k.confidence >= 0.8 ? 'green' : k.confidence >= 0.5 ? 'yellow' : 'red';
|
|
235
|
-
html += '<tr>' +
|
|
236
|
-
'<td>' + esc(k.key) + '</td>' +
|
|
237
|
-
'<td>' + badge(k.knowledge_type, 'blue') + '</td>' +
|
|
238
|
-
'<td>' + esc(k.domain || '—') + '</td>' +
|
|
239
|
-
'<td>' + badge((k.confidence || 0).toFixed(2), confColor) + '</td>' +
|
|
240
|
-
'<td>' + (k.verification_count || 0) + 'x</td>' +
|
|
241
|
-
'<td>' + esc(k.agent_id?.slice(0, 8)) + '</td>' +
|
|
242
|
-
'<td>' + timeAgo(k.updated_at) + '</td></tr>';
|
|
243
|
-
}
|
|
244
|
-
document.getElementById('knowledge-table').innerHTML = html || '<tr><td colspan="7" class="empty">No knowledge shared yet</td></tr>';
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
// ─── Load Learning Tab ──────────────────────────────────────────
|
|
248
|
-
async function loadLearning() {
|
|
249
|
-
const [sd, rd] = await Promise.all([
|
|
250
|
-
api('/learning/stats?siteId=default&agentId=symphony'),
|
|
251
|
-
api('/learning/rewards?siteId=default&agentId=symphony&limit=30')
|
|
252
|
-
]);
|
|
253
|
-
const stats = sd?.stats || {};
|
|
254
|
-
const rewards = rd?.rewardHistory || stats.rewardHistory || [];
|
|
255
|
-
|
|
256
|
-
document.getElementById('learning-stats').innerHTML =
|
|
257
|
-
'<div class="card"><div class="label">Total Decisions</div><div class="value">' + (stats.total_decisions || 0) + '</div></div>' +
|
|
258
|
-
'<div class="card"><div class="label">Accepted</div><div class="value">' + (stats.accepted || 0) + '</div></div>' +
|
|
259
|
-
'<div class="card"><div class="label">Rejected</div><div class="value">' + (stats.rejected || 0) + '</div></div>' +
|
|
260
|
-
'<div class="card"><div class="label">Accept Rate</div><div class="value">' + ((stats.acceptRate || 0) * 100).toFixed(0) + '%</div></div>' +
|
|
261
|
-
'<div class="card"><div class="label">Avg Reward</div><div class="value">' + (stats.avg_reward || 0).toFixed(2) + '</div></div>' +
|
|
262
|
-
'<div class="card"><div class="label">Policy Domains</div><div class="value">' + (stats.policy_domains || 0) + '</div></div>' +
|
|
263
|
-
'<div class="card"><div class="label">Patterns Found</div><div class="value">' + (stats.total_patterns || 0) + '</div></div>' +
|
|
264
|
-
'<div class="card"><div class="label">Accuracy</div><div class="value">' + ((stats.recentAccuracy || 0) * 100).toFixed(0) + '%</div></div>';
|
|
265
|
-
|
|
266
|
-
// Reward chart
|
|
267
|
-
let chartHtml = '';
|
|
268
|
-
if (rewards.length > 0) {
|
|
269
|
-
const maxR = Math.max(...rewards.map(r => Math.abs(r.reward)), 0.1);
|
|
270
|
-
for (const r of rewards) {
|
|
271
|
-
const h = Math.max(2, Math.round((Math.abs(r.reward) / maxR) * 70));
|
|
272
|
-
const color = r.reward >= 0 ? 'linear-gradient(to top,#10b981,#059669)' : 'linear-gradient(to top,#ef4444,#dc2626)';
|
|
273
|
-
chartHtml += '<div class="bar" style="height:' + h + 'px;background:' + color + '" title="' + esc(r.reward) + '"></div>';
|
|
274
|
-
}
|
|
275
|
-
} else {
|
|
276
|
-
chartHtml = '<div class="empty" style="height:80px;display:flex;align-items:center">No reward data yet</div>';
|
|
277
|
-
}
|
|
278
|
-
document.getElementById('reward-chart').innerHTML = chartHtml;
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
// ─── Load Symphony Tab ──────────────────────────────────────────
|
|
282
|
-
async function loadSymphony() {
|
|
283
|
-
const [cd, sd] = await Promise.all([
|
|
284
|
-
api('/symphony/compositions?siteId=default&limit=20'),
|
|
285
|
-
api('/symphony/stats?siteId=default')
|
|
286
|
-
]);
|
|
287
|
-
const comps = cd?.compositions || [];
|
|
288
|
-
const stats = sd?.stats || {};
|
|
289
|
-
|
|
290
|
-
document.getElementById('symphony-stats').innerHTML =
|
|
291
|
-
'<div class="card"><div class="label">Total Compositions</div><div class="value">' + (stats.total || 0) + '</div></div>' +
|
|
292
|
-
'<div class="card"><div class="label">Completed</div><div class="value">' + (stats.completed || 0) + '</div></div>' +
|
|
293
|
-
'<div class="card"><div class="label">Failed</div><div class="value">' + (stats.failed || 0) + '</div></div>' +
|
|
294
|
-
'<div class="card"><div class="label">Success Rate</div><div class="value">' + ((stats.successRate || 0) * 100).toFixed(0) + '%</div></div>' +
|
|
295
|
-
'<div class="card"><div class="label">Avg Duration</div><div class="value">' + (stats.avgDuration || 0) + 'ms</div></div>' +
|
|
296
|
-
'<div class="card"><div class="label">Templates Used</div><div class="value">' + (stats.templatesUsed || 0) + '</div></div>';
|
|
297
|
-
|
|
298
|
-
let html = '';
|
|
299
|
-
for (const c of comps) {
|
|
300
|
-
const statusColor = c.status === 'completed' ? 'green' : c.status === 'failed' ? 'red' : 'yellow';
|
|
301
|
-
const phases = c.phases_completed || [];
|
|
302
|
-
html += '<tr>' +
|
|
303
|
-
'<td>' + badge(c.template, 'purple') + '</td>' +
|
|
304
|
-
'<td>' + badge(c.status, statusColor) + '</td>' +
|
|
305
|
-
'<td>' + esc(phases.join(' → ')) + '</td>' +
|
|
306
|
-
'<td>' + (c.duration_ms || '—') + 'ms</td>' +
|
|
307
|
-
'<td>' + (c.agent_count || 0) + '</td>' +
|
|
308
|
-
'<td>' + timeAgo(c.created_at) + '</td></tr>';
|
|
309
|
-
}
|
|
310
|
-
document.getElementById('symphony-table').innerHTML = html || '<tr><td colspan="6" class="empty">No compositions yet</td></tr>';
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
// ─── Load All ───────────────────────────────────────────────────
|
|
314
|
-
async function loadAll() {
|
|
315
|
-
try {
|
|
316
|
-
await Promise.all([loadAgents(), loadMessages(), loadKnowledge(), loadLearning(), loadSymphony()]);
|
|
317
|
-
document.getElementById('status-text').textContent = 'Connected';
|
|
318
|
-
document.getElementById('last-update').textContent = new Date().toLocaleTimeString();
|
|
319
|
-
} catch (e) {
|
|
320
|
-
document.getElementById('status-text').textContent = 'Error';
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
loadAll();
|
|
325
|
-
setInterval(loadAll, 10000);
|
|
326
|
-
</script>
|
|
327
|
-
</body>
|
|
328
|
-
</html>
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>Private Agent Mesh — Dashboard</title>
|
|
7
|
+
<style>
|
|
8
|
+
*{margin:0;padding:0;box-sizing:border-box}
|
|
9
|
+
body{font-family:'Segoe UI',system-ui,sans-serif;background:#0f1117;color:#e1e4ea;min-height:100vh}
|
|
10
|
+
.header{background:linear-gradient(135deg,#1a1d29 0%,#252a3a 100%);padding:24px 32px;border-bottom:1px solid #2a2f3f;display:flex;justify-content:space-between;align-items:center}
|
|
11
|
+
.header h1{font-size:22px;color:#a78bfa;font-weight:600}
|
|
12
|
+
.header .meta{font-size:13px;color:#888;display:flex;gap:20px}
|
|
13
|
+
.header .meta span{display:flex;align-items:center;gap:6px}
|
|
14
|
+
.dot{width:8px;height:8px;border-radius:50%;background:#10b981;display:inline-block}
|
|
15
|
+
.tabs{display:flex;background:#1a1d29;border-bottom:1px solid #2a2f3f;padding:0 32px;gap:4px}
|
|
16
|
+
.tab{padding:12px 20px;cursor:pointer;border-bottom:2px solid transparent;color:#888;transition:.2s;font-size:14px}
|
|
17
|
+
.tab:hover{color:#e1e4ea;background:#ffffff08}
|
|
18
|
+
.tab.active{color:#a78bfa;border-bottom-color:#a78bfa}
|
|
19
|
+
.content{padding:24px 32px;max-width:1400px}
|
|
20
|
+
.panel{display:none}
|
|
21
|
+
.panel.active{display:block}
|
|
22
|
+
.grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(200px,1fr));gap:16px;margin-bottom:24px}
|
|
23
|
+
.card{background:#1a1d29;border:1px solid #2a2f3f;border-radius:10px;padding:18px}
|
|
24
|
+
.card .label{font-size:12px;color:#888;text-transform:uppercase;letter-spacing:.5px;margin-bottom:6px}
|
|
25
|
+
.card .value{font-size:28px;font-weight:700;color:#e1e4ea}
|
|
26
|
+
.card .sub{font-size:12px;color:#666;margin-top:4px}
|
|
27
|
+
table{width:100%;border-collapse:collapse;margin-top:12px}
|
|
28
|
+
th{text-align:left;font-size:12px;color:#888;text-transform:uppercase;letter-spacing:.5px;padding:10px 14px;border-bottom:1px solid #2a2f3f}
|
|
29
|
+
td{padding:10px 14px;border-bottom:1px solid #1e2230;font-size:14px}
|
|
30
|
+
tr:hover{background:#ffffff05}
|
|
31
|
+
.badge{display:inline-block;padding:2px 10px;border-radius:12px;font-size:12px;font-weight:500}
|
|
32
|
+
.badge-green{background:#10b98120;color:#10b981}
|
|
33
|
+
.badge-yellow{background:#f59e0b20;color:#f59e0b}
|
|
34
|
+
.badge-red{background:#ef444420;color:#ef4444}
|
|
35
|
+
.badge-blue{background:#3b82f620;color:#3b82f6}
|
|
36
|
+
.badge-purple{background:#a78bfa20;color:#a78bfa}
|
|
37
|
+
.chart-bar{display:flex;align-items:flex-end;gap:3px;height:80px;margin-top:8px}
|
|
38
|
+
.chart-bar .bar{flex:1;background:linear-gradient(to top,#a78bfa,#7c3aed);border-radius:3px 3px 0 0;min-width:4px;transition:height .3s}
|
|
39
|
+
.section{margin-bottom:28px}
|
|
40
|
+
.section h3{font-size:16px;color:#ccc;margin-bottom:12px;display:flex;align-items:center;gap:8px}
|
|
41
|
+
.empty{color:#555;font-style:italic;padding:20px 0}
|
|
42
|
+
.refresh-btn{background:#a78bfa20;border:1px solid #a78bfa40;color:#a78bfa;padding:6px 14px;border-radius:6px;cursor:pointer;font-size:13px}
|
|
43
|
+
.refresh-btn:hover{background:#a78bfa30}
|
|
44
|
+
</style>
|
|
45
|
+
</head>
|
|
46
|
+
<body>
|
|
47
|
+
<div class="header">
|
|
48
|
+
<h1>🛰 Private Agent Mesh</h1>
|
|
49
|
+
<div class="meta">
|
|
50
|
+
<span><span class="dot"></span> <span id="status-text">Connecting...</span></span>
|
|
51
|
+
<span>Last update: <span id="last-update">—</span></span>
|
|
52
|
+
<button class="refresh-btn" onclick="loadAll()">↻ Refresh</button>
|
|
53
|
+
</div>
|
|
54
|
+
</div>
|
|
55
|
+
|
|
56
|
+
<div class="tabs">
|
|
57
|
+
<div class="tab active" data-tab="agents">Agents</div>
|
|
58
|
+
<div class="tab" data-tab="messages">Messages</div>
|
|
59
|
+
<div class="tab" data-tab="knowledge">Knowledge</div>
|
|
60
|
+
<div class="tab" data-tab="learning">Learning</div>
|
|
61
|
+
<div class="tab" data-tab="symphony">Symphony</div>
|
|
62
|
+
</div>
|
|
63
|
+
|
|
64
|
+
<div class="content">
|
|
65
|
+
<!-- AGENTS TAB -->
|
|
66
|
+
<div class="panel active" id="panel-agents">
|
|
67
|
+
<div class="grid" id="agent-stats"></div>
|
|
68
|
+
<div class="section">
|
|
69
|
+
<h3>Active Agents</h3>
|
|
70
|
+
<table><thead><tr><th>Name</th><th>Role</th><th>Status</th><th>Capabilities</th><th>Sent</th><th>Recv</th><th>Knowledge</th><th>Last Heartbeat</th></tr></thead>
|
|
71
|
+
<tbody id="agents-table"></tbody></table>
|
|
72
|
+
</div>
|
|
73
|
+
</div>
|
|
74
|
+
|
|
75
|
+
<!-- MESSAGES TAB -->
|
|
76
|
+
<div class="panel" id="panel-messages">
|
|
77
|
+
<div class="grid" id="msg-stats"></div>
|
|
78
|
+
<div class="section">
|
|
79
|
+
<h3>Recent Messages</h3>
|
|
80
|
+
<table><thead><tr><th>Type</th><th>Subject</th><th>Sender</th><th>Channel</th><th>Priority</th><th>Time</th></tr></thead>
|
|
81
|
+
<tbody id="messages-table"></tbody></table>
|
|
82
|
+
</div>
|
|
83
|
+
</div>
|
|
84
|
+
|
|
85
|
+
<!-- KNOWLEDGE TAB -->
|
|
86
|
+
<div class="panel" id="panel-knowledge">
|
|
87
|
+
<div class="grid" id="knowledge-stats"></div>
|
|
88
|
+
<div class="section">
|
|
89
|
+
<h3>Knowledge Domains</h3>
|
|
90
|
+
<div id="knowledge-domains"></div>
|
|
91
|
+
</div>
|
|
92
|
+
<div class="section">
|
|
93
|
+
<h3>Recent Knowledge</h3>
|
|
94
|
+
<table><thead><tr><th>Key</th><th>Type</th><th>Domain</th><th>Confidence</th><th>Verified</th><th>Agent</th><th>Updated</th></tr></thead>
|
|
95
|
+
<tbody id="knowledge-table"></tbody></table>
|
|
96
|
+
</div>
|
|
97
|
+
</div>
|
|
98
|
+
|
|
99
|
+
<!-- LEARNING TAB -->
|
|
100
|
+
<div class="panel" id="panel-learning">
|
|
101
|
+
<div class="grid" id="learning-stats"></div>
|
|
102
|
+
<div class="section">
|
|
103
|
+
<h3>Reward History</h3>
|
|
104
|
+
<div class="chart-bar" id="reward-chart"></div>
|
|
105
|
+
</div>
|
|
106
|
+
</div>
|
|
107
|
+
|
|
108
|
+
<!-- SYMPHONY TAB -->
|
|
109
|
+
<div class="panel" id="panel-symphony">
|
|
110
|
+
<div class="grid" id="symphony-stats"></div>
|
|
111
|
+
<div class="section">
|
|
112
|
+
<h3>Recent Compositions</h3>
|
|
113
|
+
<table><thead><tr><th>Template</th><th>Status</th><th>Phases</th><th>Duration</th><th>Agents</th><th>Created</th></tr></thead>
|
|
114
|
+
<tbody id="symphony-table"></tbody></table>
|
|
115
|
+
</div>
|
|
116
|
+
</div>
|
|
117
|
+
</div>
|
|
118
|
+
|
|
119
|
+
<script>
|
|
120
|
+
// XSS-safe text escaping
|
|
121
|
+
function esc(str) {
|
|
122
|
+
if (str == null) return '';
|
|
123
|
+
const d = document.createElement('div');
|
|
124
|
+
d.textContent = String(str);
|
|
125
|
+
return d.innerHTML;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function badge(text, color) { return '<span class="badge badge-' + color + '">' + esc(text) + '</span>'; }
|
|
129
|
+
function timeAgo(ts) {
|
|
130
|
+
if (!ts) return '—';
|
|
131
|
+
const d = Date.now() - new Date(ts).getTime();
|
|
132
|
+
if (d < 60000) return Math.floor(d / 1000) + 's ago';
|
|
133
|
+
if (d < 3600000) return Math.floor(d / 60000) + 'm ago';
|
|
134
|
+
return Math.floor(d / 3600000) + 'h ago';
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const BASE = '';
|
|
138
|
+
|
|
139
|
+
async function api(path) {
|
|
140
|
+
try {
|
|
141
|
+
const r = await fetch(BASE + '/api/mesh' + path);
|
|
142
|
+
if (!r.ok) return null;
|
|
143
|
+
return r.json();
|
|
144
|
+
} catch (_) { return null; }
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// ─── Tab switching ──────────────────────────────────────────────
|
|
148
|
+
document.querySelectorAll('.tab').forEach(t => {
|
|
149
|
+
t.addEventListener('click', () => {
|
|
150
|
+
document.querySelectorAll('.tab').forEach(x => x.classList.remove('active'));
|
|
151
|
+
document.querySelectorAll('.panel').forEach(x => x.classList.remove('active'));
|
|
152
|
+
t.classList.add('active');
|
|
153
|
+
document.getElementById('panel-' + t.dataset.tab).classList.add('active');
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
// ─── Load Agents Tab ────────────────────────────────────────────
|
|
158
|
+
async function loadAgents() {
|
|
159
|
+
const [ad, sd] = await Promise.all([api('/agents'), api('/stats')]);
|
|
160
|
+
const agents = ad?.agents || [];
|
|
161
|
+
const stats = sd?.stats || {};
|
|
162
|
+
|
|
163
|
+
document.getElementById('agent-stats').innerHTML =
|
|
164
|
+
'<div class="card"><div class="label">Active Agents</div><div class="value">' + agents.length + '</div></div>' +
|
|
165
|
+
'<div class="card"><div class="label">Total Messages</div><div class="value">' + (stats.totalMessages || 0) + '</div></div>' +
|
|
166
|
+
'<div class="card"><div class="label">Knowledge Items</div><div class="value">' + (stats.totalKnowledge || 0) + '</div></div>' +
|
|
167
|
+
'<div class="card"><div class="label">Channels</div><div class="value">' + (stats.channelCount || 0) + '</div></div>';
|
|
168
|
+
|
|
169
|
+
let html = '';
|
|
170
|
+
for (const a of agents) {
|
|
171
|
+
const statusColor = a.status === 'active' ? 'green' : a.status === 'idle' ? 'yellow' : 'red';
|
|
172
|
+
const caps = JSON.parse(a.capabilities || '[]');
|
|
173
|
+
html += '<tr>' +
|
|
174
|
+
'<td>' + esc(a.display_name || a.id.slice(0, 8)) + '</td>' +
|
|
175
|
+
'<td>' + badge(a.agent_role, 'purple') + '</td>' +
|
|
176
|
+
'<td>' + badge(a.status, statusColor) + '</td>' +
|
|
177
|
+
'<td>' + caps.map(c => esc(c)).join(', ') + '</td>' +
|
|
178
|
+
'<td>' + a.messages_sent + '</td>' +
|
|
179
|
+
'<td>' + a.messages_received + '</td>' +
|
|
180
|
+
'<td>' + a.knowledge_count + '</td>' +
|
|
181
|
+
'<td>' + timeAgo(a.last_heartbeat) + '</td></tr>';
|
|
182
|
+
}
|
|
183
|
+
document.getElementById('agents-table').innerHTML = html || '<tr><td colspan="8" class="empty">No active agents</td></tr>';
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// ─── Load Messages Tab ──────────────────────────────────────────
|
|
187
|
+
async function loadMessages() {
|
|
188
|
+
const [md, sd] = await Promise.all([api('/messages?limit=50'), api('/stats')]);
|
|
189
|
+
const msgs = md?.messages || [];
|
|
190
|
+
const stats = sd?.stats || {};
|
|
191
|
+
|
|
192
|
+
document.getElementById('msg-stats').innerHTML =
|
|
193
|
+
'<div class="card"><div class="label">Total Messages</div><div class="value">' + (stats.totalMessages || 0) + '</div></div>' +
|
|
194
|
+
'<div class="card"><div class="label">Alerts</div><div class="value">' + msgs.filter(m => m.message_type === 'alert').length + '</div></div>';
|
|
195
|
+
|
|
196
|
+
let html = '';
|
|
197
|
+
for (const m of msgs) {
|
|
198
|
+
const typeColor = m.message_type === 'alert' ? 'red' : m.message_type === 'vote' ? 'blue' : 'green';
|
|
199
|
+
html += '<tr>' +
|
|
200
|
+
'<td>' + badge(m.message_type, typeColor) + '</td>' +
|
|
201
|
+
'<td>' + esc(m.subject) + '</td>' +
|
|
202
|
+
'<td>' + esc(m.sender_id?.slice(0, 8)) + '</td>' +
|
|
203
|
+
'<td>' + esc(m.channel_id?.slice(0, 8) || 'general') + '</td>' +
|
|
204
|
+
'<td>' + (m.priority || 0) + '</td>' +
|
|
205
|
+
'<td>' + timeAgo(m.created_at) + '</td></tr>';
|
|
206
|
+
}
|
|
207
|
+
document.getElementById('messages-table').innerHTML = html || '<tr><td colspan="6" class="empty">No messages yet</td></tr>';
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// ─── Load Knowledge Tab ─────────────────────────────────────────
|
|
211
|
+
async function loadKnowledge() {
|
|
212
|
+
const [kd, dd] = await Promise.all([api('/knowledge-recent?limit=30'), api('/knowledge-domains')]);
|
|
213
|
+
const items = kd?.knowledge || [];
|
|
214
|
+
const domains = dd?.domains || [];
|
|
215
|
+
|
|
216
|
+
document.getElementById('knowledge-stats').innerHTML =
|
|
217
|
+
'<div class="card"><div class="label">Knowledge Items</div><div class="value">' + items.length + '</div></div>' +
|
|
218
|
+
'<div class="card"><div class="label">Domains</div><div class="value">' + domains.length + '</div></div>' +
|
|
219
|
+
'<div class="card"><div class="label">Avg Confidence</div><div class="value">' +
|
|
220
|
+
(items.length ? (items.reduce((s, k) => s + k.confidence, 0) / items.length).toFixed(2) : '—') + '</div></div>';
|
|
221
|
+
|
|
222
|
+
// Domains cards
|
|
223
|
+
let domHtml = '<div class="grid">';
|
|
224
|
+
for (const d of domains) {
|
|
225
|
+
domHtml += '<div class="card"><div class="label">' + esc(d.domain || 'global') + '</div><div class="value">' + d.count + '</div>' +
|
|
226
|
+
'<div class="sub">avg conf: ' + (d.avg_confidence || 0).toFixed(2) + '</div></div>';
|
|
227
|
+
}
|
|
228
|
+
domHtml += '</div>';
|
|
229
|
+
document.getElementById('knowledge-domains').innerHTML = domHtml || '<div class="empty">No domains yet</div>';
|
|
230
|
+
|
|
231
|
+
// Knowledge table
|
|
232
|
+
let html = '';
|
|
233
|
+
for (const k of items) {
|
|
234
|
+
const confColor = k.confidence >= 0.8 ? 'green' : k.confidence >= 0.5 ? 'yellow' : 'red';
|
|
235
|
+
html += '<tr>' +
|
|
236
|
+
'<td>' + esc(k.key) + '</td>' +
|
|
237
|
+
'<td>' + badge(k.knowledge_type, 'blue') + '</td>' +
|
|
238
|
+
'<td>' + esc(k.domain || '—') + '</td>' +
|
|
239
|
+
'<td>' + badge((k.confidence || 0).toFixed(2), confColor) + '</td>' +
|
|
240
|
+
'<td>' + (k.verification_count || 0) + 'x</td>' +
|
|
241
|
+
'<td>' + esc(k.agent_id?.slice(0, 8)) + '</td>' +
|
|
242
|
+
'<td>' + timeAgo(k.updated_at) + '</td></tr>';
|
|
243
|
+
}
|
|
244
|
+
document.getElementById('knowledge-table').innerHTML = html || '<tr><td colspan="7" class="empty">No knowledge shared yet</td></tr>';
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// ─── Load Learning Tab ──────────────────────────────────────────
|
|
248
|
+
async function loadLearning() {
|
|
249
|
+
const [sd, rd] = await Promise.all([
|
|
250
|
+
api('/learning/stats?siteId=default&agentId=symphony'),
|
|
251
|
+
api('/learning/rewards?siteId=default&agentId=symphony&limit=30')
|
|
252
|
+
]);
|
|
253
|
+
const stats = sd?.stats || {};
|
|
254
|
+
const rewards = rd?.rewardHistory || stats.rewardHistory || [];
|
|
255
|
+
|
|
256
|
+
document.getElementById('learning-stats').innerHTML =
|
|
257
|
+
'<div class="card"><div class="label">Total Decisions</div><div class="value">' + (stats.total_decisions || 0) + '</div></div>' +
|
|
258
|
+
'<div class="card"><div class="label">Accepted</div><div class="value">' + (stats.accepted || 0) + '</div></div>' +
|
|
259
|
+
'<div class="card"><div class="label">Rejected</div><div class="value">' + (stats.rejected || 0) + '</div></div>' +
|
|
260
|
+
'<div class="card"><div class="label">Accept Rate</div><div class="value">' + ((stats.acceptRate || 0) * 100).toFixed(0) + '%</div></div>' +
|
|
261
|
+
'<div class="card"><div class="label">Avg Reward</div><div class="value">' + (stats.avg_reward || 0).toFixed(2) + '</div></div>' +
|
|
262
|
+
'<div class="card"><div class="label">Policy Domains</div><div class="value">' + (stats.policy_domains || 0) + '</div></div>' +
|
|
263
|
+
'<div class="card"><div class="label">Patterns Found</div><div class="value">' + (stats.total_patterns || 0) + '</div></div>' +
|
|
264
|
+
'<div class="card"><div class="label">Accuracy</div><div class="value">' + ((stats.recentAccuracy || 0) * 100).toFixed(0) + '%</div></div>';
|
|
265
|
+
|
|
266
|
+
// Reward chart
|
|
267
|
+
let chartHtml = '';
|
|
268
|
+
if (rewards.length > 0) {
|
|
269
|
+
const maxR = Math.max(...rewards.map(r => Math.abs(r.reward)), 0.1);
|
|
270
|
+
for (const r of rewards) {
|
|
271
|
+
const h = Math.max(2, Math.round((Math.abs(r.reward) / maxR) * 70));
|
|
272
|
+
const color = r.reward >= 0 ? 'linear-gradient(to top,#10b981,#059669)' : 'linear-gradient(to top,#ef4444,#dc2626)';
|
|
273
|
+
chartHtml += '<div class="bar" style="height:' + h + 'px;background:' + color + '" title="' + esc(r.reward) + '"></div>';
|
|
274
|
+
}
|
|
275
|
+
} else {
|
|
276
|
+
chartHtml = '<div class="empty" style="height:80px;display:flex;align-items:center">No reward data yet</div>';
|
|
277
|
+
}
|
|
278
|
+
document.getElementById('reward-chart').innerHTML = chartHtml;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// ─── Load Symphony Tab ──────────────────────────────────────────
|
|
282
|
+
async function loadSymphony() {
|
|
283
|
+
const [cd, sd] = await Promise.all([
|
|
284
|
+
api('/symphony/compositions?siteId=default&limit=20'),
|
|
285
|
+
api('/symphony/stats?siteId=default')
|
|
286
|
+
]);
|
|
287
|
+
const comps = cd?.compositions || [];
|
|
288
|
+
const stats = sd?.stats || {};
|
|
289
|
+
|
|
290
|
+
document.getElementById('symphony-stats').innerHTML =
|
|
291
|
+
'<div class="card"><div class="label">Total Compositions</div><div class="value">' + (stats.total || 0) + '</div></div>' +
|
|
292
|
+
'<div class="card"><div class="label">Completed</div><div class="value">' + (stats.completed || 0) + '</div></div>' +
|
|
293
|
+
'<div class="card"><div class="label">Failed</div><div class="value">' + (stats.failed || 0) + '</div></div>' +
|
|
294
|
+
'<div class="card"><div class="label">Success Rate</div><div class="value">' + ((stats.successRate || 0) * 100).toFixed(0) + '%</div></div>' +
|
|
295
|
+
'<div class="card"><div class="label">Avg Duration</div><div class="value">' + (stats.avgDuration || 0) + 'ms</div></div>' +
|
|
296
|
+
'<div class="card"><div class="label">Templates Used</div><div class="value">' + (stats.templatesUsed || 0) + '</div></div>';
|
|
297
|
+
|
|
298
|
+
let html = '';
|
|
299
|
+
for (const c of comps) {
|
|
300
|
+
const statusColor = c.status === 'completed' ? 'green' : c.status === 'failed' ? 'red' : 'yellow';
|
|
301
|
+
const phases = c.phases_completed || [];
|
|
302
|
+
html += '<tr>' +
|
|
303
|
+
'<td>' + badge(c.template, 'purple') + '</td>' +
|
|
304
|
+
'<td>' + badge(c.status, statusColor) + '</td>' +
|
|
305
|
+
'<td>' + esc(phases.join(' → ')) + '</td>' +
|
|
306
|
+
'<td>' + (c.duration_ms || '—') + 'ms</td>' +
|
|
307
|
+
'<td>' + (c.agent_count || 0) + '</td>' +
|
|
308
|
+
'<td>' + timeAgo(c.created_at) + '</td></tr>';
|
|
309
|
+
}
|
|
310
|
+
document.getElementById('symphony-table').innerHTML = html || '<tr><td colspan="6" class="empty">No compositions yet</td></tr>';
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// ─── Load All ───────────────────────────────────────────────────
|
|
314
|
+
async function loadAll() {
|
|
315
|
+
try {
|
|
316
|
+
await Promise.all([loadAgents(), loadMessages(), loadKnowledge(), loadLearning(), loadSymphony()]);
|
|
317
|
+
document.getElementById('status-text').textContent = 'Connected';
|
|
318
|
+
document.getElementById('last-update').textContent = new Date().toLocaleTimeString();
|
|
319
|
+
} catch (e) {
|
|
320
|
+
document.getElementById('status-text').textContent = 'Error';
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
loadAll();
|
|
325
|
+
setInterval(loadAll, 10000);
|
|
326
|
+
</script>
|
|
327
|
+
</body>
|
|
328
|
+
</html>
|