web-agent-bridge 2.3.0 → 2.3.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 (35) hide show
  1. package/package.json +12 -4
  2. package/public/commander-dashboard.html +243 -0
  3. package/public/css/premium.css +317 -317
  4. package/public/demo.html +259 -259
  5. package/public/index.html +644 -644
  6. package/public/mesh-dashboard.html +309 -382
  7. package/public/premium-dashboard.html +2487 -2487
  8. package/public/premium.html +791 -791
  9. package/public/script/wab.min.js +124 -87
  10. package/script/ai-agent-bridge.js +154 -84
  11. package/sdk/agent-mesh.js +287 -171
  12. package/sdk/commander.js +262 -0
  13. package/sdk/index.js +260 -260
  14. package/server/index.js +8 -1
  15. package/server/migrations/002_premium_features.sql +418 -418
  16. package/server/models/db.js +24 -5
  17. package/server/routes/admin-premium.js +671 -671
  18. package/server/routes/commander.js +316 -0
  19. package/server/routes/mesh.js +370 -201
  20. package/server/routes/premium-v2.js +686 -686
  21. package/server/routes/premium.js +724 -724
  22. package/server/services/agent-learning.js +230 -77
  23. package/server/services/agent-memory.js +625 -625
  24. package/server/services/agent-mesh.js +260 -67
  25. package/server/services/agent-symphony.js +548 -518
  26. package/server/services/commander.js +738 -0
  27. package/server/services/edge-compute.js +440 -0
  28. package/server/services/local-ai.js +389 -0
  29. package/server/services/plugins.js +747 -747
  30. package/server/services/self-healing.js +843 -843
  31. package/server/services/swarm.js +788 -788
  32. package/server/services/vision.js +871 -871
  33. package/public/admin/dashboard.html +0 -848
  34. package/public/admin/login.html +0 -84
  35. package/public/video/tutorial.mp4 +0 -0
@@ -1,401 +1,328 @@
1
1
  <!DOCTYPE html>
2
2
  <html lang="en">
3
3
  <head>
4
- <meta charset="UTF-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>Agent Mesh — Web Agent Bridge</title>
7
- <link rel="preconnect" href="https://fonts.googleapis.com">
8
- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
9
- <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
10
- <link rel="stylesheet" href="/css/styles.css">
11
- <style>
12
- :root { --mesh-green: #10b981; --mesh-blue: #3b82f6; --mesh-purple: #8b5cf6; --mesh-orange: #f59e0b; --mesh-red: #ef4444; }
13
-
14
- .mesh-layout { max-width: 1200px; margin: 0 auto; padding: 32px 24px; }
15
- .mesh-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 32px; flex-wrap: wrap; gap: 16px; }
16
- .mesh-header h1 { font-size: 1.8rem; font-weight: 800; }
17
- .mesh-header h1 span { color: var(--mesh-purple); }
18
- .mesh-subtitle { color: var(--text-muted); font-size: 0.9rem; margin-top: 4px; }
19
-
20
- .mesh-stats { display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px; margin-bottom: 32px; }
21
- .stat-card { background: var(--bg-card); border: 1px solid var(--border-primary); border-radius: 12px; padding: 20px; text-align: center; }
22
- .stat-card .stat-value { font-size: 2rem; font-weight: 800; color: var(--text-primary); }
23
- .stat-card .stat-label { font-size: 0.8rem; color: var(--text-muted); margin-top: 4px; }
24
- .stat-card.green .stat-value { color: var(--mesh-green); }
25
- .stat-card.blue .stat-value { color: var(--mesh-blue); }
26
- .stat-card.purple .stat-value { color: var(--mesh-purple); }
27
- .stat-card.orange .stat-value { color: var(--mesh-orange); }
28
-
29
- .mesh-tabs { display: flex; gap: 4px; margin-bottom: 24px; background: var(--bg-card); border-radius: 10px; padding: 4px; border: 1px solid var(--border-primary); }
30
- .mesh-tab { padding: 10px 20px; border-radius: 8px; cursor: pointer; font-weight: 600; font-size: 0.85rem; color: var(--text-muted); border: none; background: transparent; transition: all 0.2s; }
31
- .mesh-tab.active { background: var(--mesh-purple); color: white; }
32
- .mesh-tab:hover:not(.active) { background: var(--bg-secondary); color: var(--text-primary); }
33
-
34
- .tab-content { display: none; }
35
- .tab-content.active { display: block; }
36
-
37
- .section-title { font-size: 1.1rem; font-weight: 700; margin: 0 0 16px; }
38
-
39
- .agents-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 16px; margin-bottom: 24px; }
40
- .agent-card { background: var(--bg-card); border: 1px solid var(--border-primary); border-radius: 12px; padding: 20px; position: relative; overflow: hidden; }
41
- .agent-card::before { content: ''; position: absolute; top: 0; left: 0; right: 0; height: 3px; }
42
- .agent-card.researcher::before { background: var(--mesh-blue); }
43
- .agent-card.negotiator::before { background: var(--mesh-orange); }
44
- .agent-card.analyst::before { background: var(--mesh-purple); }
45
- .agent-card.guardian::before { background: var(--mesh-green); }
46
- .agent-card .agent-role { font-weight: 700; font-size: 0.95rem; margin-bottom: 4px; text-transform: capitalize; }
47
- .agent-card .agent-name { color: var(--text-muted); font-size: 0.8rem; margin-bottom: 12px; }
48
- .agent-card .agent-stats { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
49
- .agent-card .agent-stat { font-size: 0.75rem; color: var(--text-secondary); }
50
- .agent-card .agent-stat strong { display: block; font-size: 1rem; color: var(--text-primary); }
51
- .agent-status { display: inline-block; padding: 2px 8px; border-radius: 100px; font-size: 0.7rem; font-weight: 600; }
52
- .agent-status.active { background: rgba(16,185,129,0.15); color: var(--mesh-green); }
53
- .agent-status.idle { background: rgba(100,116,139,0.15); color: var(--text-muted); }
54
-
55
- .channels-list { display: grid; gap: 12px; margin-bottom: 24px; }
56
- .channel-card { background: var(--bg-card); border: 1px solid var(--border-primary); border-radius: 10px; padding: 16px 20px; display: flex; justify-content: space-between; align-items: center; }
57
- .channel-name { font-weight: 700; font-size: 0.95rem; }
58
- .channel-desc { color: var(--text-muted); font-size: 0.8rem; margin-top: 2px; }
59
- .channel-badge { background: var(--mesh-purple); color: white; padding: 4px 12px; border-radius: 100px; font-size: 0.75rem; font-weight: 600; }
60
-
61
- .messages-list { max-height: 400px; overflow-y: auto; display: grid; gap: 8px; }
62
- .message-item { background: var(--bg-card); border: 1px solid var(--border-primary); border-radius: 8px; padding: 12px 16px; }
63
- .message-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 6px; }
64
- .message-type { font-size: 0.7rem; font-weight: 600; padding: 2px 8px; border-radius: 100px; }
65
- .message-type.alert { background: rgba(239,68,68,0.15); color: var(--mesh-red); }
66
- .message-type.discovery { background: rgba(59,130,246,0.15); color: var(--mesh-blue); }
67
- .message-type.tactic { background: rgba(139,92,246,0.15); color: var(--mesh-purple); }
68
- .message-type.request { background: rgba(245,158,11,0.15); color: var(--mesh-orange); }
69
- .message-subject { font-weight: 600; font-size: 0.85rem; }
70
- .message-sender { font-size: 0.75rem; color: var(--text-muted); }
71
- .message-time { font-size: 0.7rem; color: var(--text-muted); }
72
-
73
- .symphony-grid { display: grid; gap: 16px; margin-bottom: 24px; }
74
- .symphony-card { background: var(--bg-card); border: 1px solid var(--border-primary); border-radius: 12px; padding: 20px; }
75
- .symphony-name { font-weight: 700; font-size: 1rem; margin-bottom: 4px; }
76
- .symphony-desc { color: var(--text-muted); font-size: 0.8rem; margin-bottom: 12px; }
77
- .symphony-phases { display: flex; gap: 8px; flex-wrap: wrap; }
78
- .phase-tag { font-size: 0.7rem; padding: 3px 10px; border-radius: 100px; font-weight: 600; background: var(--bg-secondary); color: var(--text-secondary); }
79
-
80
- .learning-chart { background: var(--bg-card); border: 1px solid var(--border-primary); border-radius: 12px; padding: 24px; margin-bottom: 24px; }
81
- .learning-bars { display: flex; gap: 4px; align-items: flex-end; height: 120px; }
82
- .learning-bar { flex: 1; background: var(--mesh-purple); border-radius: 4px 4px 0 0; min-width: 8px; transition: height 0.3s; opacity: 0.7; }
83
- .learning-bar:hover { opacity: 1; }
84
-
85
- .knowledge-table { width: 100%; border-collapse: collapse; }
86
- .knowledge-table th { text-align: left; font-size: 0.75rem; color: var(--text-muted); padding: 10px 12px; border-bottom: 1px solid var(--border-primary); }
87
- .knowledge-table td { padding: 10px 12px; font-size: 0.85rem; border-bottom: 1px solid var(--border-primary); }
88
- .confidence-bar { height: 6px; border-radius: 3px; background: var(--bg-secondary); overflow: hidden; width: 80px; display: inline-block; }
89
- .confidence-fill { height: 100%; border-radius: 3px; }
90
-
91
- .demo-btn { background: var(--mesh-purple); color: white; border: none; padding: 10px 24px; border-radius: 8px; font-weight: 600; font-size: 0.85rem; cursor: pointer; }
92
- .demo-btn:hover { opacity: 0.9; }
93
- .demo-output { background: var(--bg-secondary); border-radius: 10px; padding: 16px; margin-top: 16px; font-family: 'JetBrains Mono', monospace; font-size: 0.8rem; max-height: 300px; overflow-y: auto; white-space: pre-wrap; }
94
-
95
- @media (max-width: 768px) {
96
- .mesh-stats { grid-template-columns: repeat(2, 1fr); }
97
- .mesh-tabs { flex-wrap: wrap; }
98
- }
99
- </style>
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>
100
45
  </head>
101
46
  <body>
102
- <nav class="nav-header">
103
- <div class="nav-inner">
104
- <a href="/" class="nav-logo">WAB</a>
105
- <div class="nav-links">
106
- <a href="/dashboard">Dashboard</a>
107
- <a href="/premium-dashboard">Sovereign</a>
108
- <a href="/mesh-dashboard" class="nav-active">Agent Mesh</a>
109
- <a href="/docs">Docs</a>
110
- </div>
111
- </div>
112
- </nav>
113
-
114
- <div class="mesh-layout">
115
- <div class="mesh-header">
116
- <div>
117
- <h1>Agent <span>Mesh</span></h1>
118
- <div class="mesh-subtitle">Private Agent Communication, Autonomous Learning, and Symphony Orchestration</div>
119
- </div>
120
- <button class="demo-btn" onclick="runDemo()">Run Symphony Demo</button>
121
- </div>
122
-
123
- <!-- Stats -->
124
- <div class="mesh-stats">
125
- <div class="stat-card green"><div class="stat-value" id="stat-agents">0</div><div class="stat-label">Active Agents</div></div>
126
- <div class="stat-card blue"><div class="stat-value" id="stat-messages">0</div><div class="stat-label">Active Messages</div></div>
127
- <div class="stat-card purple"><div class="stat-value" id="stat-knowledge">0</div><div class="stat-label">Knowledge Items</div></div>
128
- <div class="stat-card orange"><div class="stat-value" id="stat-domains">0</div><div class="stat-label">Known Domains</div></div>
129
- </div>
130
-
131
- <!-- Tabs -->
132
- <div class="mesh-tabs">
133
- <button class="mesh-tab active" data-tab="agents">Agents</button>
134
- <button class="mesh-tab" data-tab="channels">Channels</button>
135
- <button class="mesh-tab" data-tab="knowledge">Knowledge</button>
136
- <button class="mesh-tab" data-tab="symphony">Symphony</button>
137
- <button class="mesh-tab" data-tab="learning">Learning</button>
138
- </div>
139
-
140
- <!-- Agents Tab -->
141
- <div class="tab-content active" id="tab-agents">
142
- <h3 class="section-title">Active Mesh Agents</h3>
143
- <div class="agents-grid" id="agents-grid"></div>
47
+ <div class="header">
48
+ <h1>&#128752; 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()">&#8635; 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>
144
72
  </div>
73
+ </div>
145
74
 
146
- <!-- Channels Tab -->
147
- <div class="tab-content" id="tab-channels">
148
- <h3 class="section-title">Communication Channels</h3>
149
- <div class="channels-list" id="channels-list"></div>
150
- <h3 class="section-title" style="margin-top: 24px">Recent Messages</h3>
151
- <div class="messages-list" id="messages-list"></div>
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>
152
82
  </div>
83
+ </div>
153
84
 
154
- <!-- Knowledge Tab -->
155
- <div class="tab-content" id="tab-knowledge">
156
- <h3 class="section-title">Shared Knowledge Base</h3>
157
- <div style="background: var(--bg-card); border: 1px solid var(--border-primary); border-radius: 12px; overflow: hidden;">
158
- <table class="knowledge-table" id="knowledge-table">
159
- <thead>
160
- <tr><th>Domain</th><th>Key</th><th>Type</th><th>Source</th><th>Confidence</th><th>Accessed</th></tr>
161
- </thead>
162
- <tbody id="knowledge-body"></tbody>
163
- </table>
164
- </div>
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>
165
91
  </div>
166
-
167
- <!-- Symphony Tab -->
168
- <div class="tab-content" id="tab-symphony">
169
- <h3 class="section-title">Symphony Templates</h3>
170
- <div class="symphony-grid" id="symphony-templates"></div>
171
- <h3 class="section-title" style="margin-top: 24px">Recent Compositions</h3>
172
- <div class="symphony-grid" id="symphony-history"></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>
173
96
  </div>
97
+ </div>
174
98
 
175
- <!-- Learning Tab -->
176
- <div class="tab-content" id="tab-learning">
177
- <h3 class="section-title">Learning Performance</h3>
178
- <div class="mesh-stats" style="grid-template-columns: repeat(3, 1fr); margin-bottom: 24px;">
179
- <div class="stat-card green"><div class="stat-value" id="learn-accuracy">0%</div><div class="stat-label">Prediction Accuracy</div></div>
180
- <div class="stat-card blue"><div class="stat-value" id="learn-decisions">0</div><div class="stat-label">Total Decisions</div></div>
181
- <div class="stat-card purple"><div class="stat-value" id="learn-patterns">0</div><div class="stat-label">Patterns Discovered</div></div>
182
- </div>
183
- <div class="learning-chart">
184
- <h3 class="section-title">Reward History</h3>
185
- <div class="learning-bars" id="learning-bars"></div>
186
- </div>
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>
187
105
  </div>
106
+ </div>
188
107
 
189
- <!-- Demo Output -->
190
- <div id="demo-output" style="display: none;">
191
- <h3 class="section-title">Demo Output</h3>
192
- <div class="demo-output" id="demo-log"></div>
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>
193
115
  </div>
194
116
  </div>
195
-
196
- <script>
197
- const API = '';
198
-
199
- // Tab switching
200
- document.querySelectorAll('.mesh-tab').forEach(function(tab) {
201
- tab.addEventListener('click', function() {
202
- document.querySelectorAll('.mesh-tab').forEach(function(t) { t.classList.remove('active'); });
203
- document.querySelectorAll('.tab-content').forEach(function(c) { c.classList.remove('active'); });
204
- tab.classList.add('active');
205
- document.getElementById('tab-' + tab.dataset.tab).classList.add('active');
206
- });
207
- });
208
-
209
- async function loadDashboard() {
210
- try {
211
- const res = await fetch(API + '/api/mesh/dashboard');
212
- const data = await res.json();
213
-
214
- // Stats
215
- document.getElementById('stat-agents').textContent = data.mesh?.active_agents || 0;
216
- document.getElementById('stat-messages').textContent = data.mesh?.active_messages || 0;
217
- document.getElementById('stat-knowledge').textContent = data.mesh?.total_knowledge || 0;
218
- document.getElementById('stat-domains').textContent = data.mesh?.known_domains || 0;
219
-
220
- // Agents
221
- const agentsGrid = document.getElementById('agents-grid');
222
- if (data.agents && data.agents.length > 0) {
223
- agentsGrid.innerHTML = data.agents.map(function(a) {
224
- return '<div class="agent-card ' + a.agent_role + '">' +
225
- '<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px;">' +
226
- '<div class="agent-role">' + a.agent_role + '</div>' +
227
- '<span class="agent-status ' + a.status + '">' + a.status + '</span></div>' +
228
- '<div class="agent-name">' + (a.display_name || a.id.slice(0, 8)) + '</div>' +
229
- '<div class="agent-stats">' +
230
- '<div class="agent-stat"><strong>' + a.messages_sent + '</strong>Sent</div>' +
231
- '<div class="agent-stat"><strong>' + a.messages_received + '</strong>Received</div>' +
232
- '<div class="agent-stat"><strong>' + a.knowledge_count + '</strong>Knowledge</div>' +
233
- '<div class="agent-stat"><strong>' + timeAgo(a.last_heartbeat) + '</strong>Last Seen</div></div></div>';
234
- }).join('');
235
- } else {
236
- agentsGrid.innerHTML = '<div style="grid-column: 1/-1; text-align: center; padding: 40px; color: var(--text-muted);">No agents active. Run a demo or connect agents via SDK.</div>';
237
- }
238
-
239
- // Channels
240
- var channelsList = document.getElementById('channels-list');
241
- channelsList.innerHTML = data.channels.map(function(ch) {
242
- return '<div class="channel-card"><div><div class="channel-name">#' + ch.name + '</div>' +
243
- '<div class="channel-desc">' + (ch.description || '') + '</div></div>' +
244
- '<span class="channel-badge">' + ch.channel_type + '</span></div>';
245
- }).join('');
246
-
247
- // Load messages for discoveries channel
248
- loadMessages('discoveries');
249
-
250
- // Symphony templates
251
- var templatesGrid = document.getElementById('symphony-templates');
252
- templatesGrid.innerHTML = data.symphonyTemplates.map(function(t) {
253
- return '<div class="symphony-card"><div class="symphony-name">' + t.name.replace(/_/g, ' ') + '</div>' +
254
- '<div class="symphony-desc">' + (t.description || '') + '</div>' +
255
- '<div class="symphony-phases">' + t.phase_order.map(function(p) {
256
- return '<span class="phase-tag">' + p + '</span>';
257
- }).join('') + '</div></div>';
258
- }).join('');
259
-
260
- } catch (err) {
261
- console.error('Failed to load dashboard:', err);
262
- }
263
- }
264
-
265
- async function loadMessages(channel) {
266
- try {
267
- var res = await fetch(API + '/api/mesh/channels/' + channel + '/messages?limit=20');
268
- var messages = await res.json();
269
- var list = document.getElementById('messages-list');
270
-
271
- if (messages.length > 0) {
272
- list.innerHTML = messages.map(function(m) {
273
- return '<div class="message-item"><div class="message-header">' +
274
- '<div><span class="message-type ' + m.message_type + '">' + m.message_type + '</span> ' +
275
- '<span class="message-subject">' + escapeHtml(m.subject) + '</span></div>' +
276
- '<span class="message-time">' + timeAgo(m.created_at) + '</span></div>' +
277
- '<div class="message-sender">From: ' + (m.sender_name || m.sender_role || m.sender_id?.slice(0, 8)) + '</div></div>';
278
- }).join('');
279
- } else {
280
- list.innerHTML = '<div style="text-align: center; padding: 30px; color: var(--text-muted);">No messages yet</div>';
281
- }
282
- } catch (e) {
283
- console.error('Failed to load messages:', e);
284
- }
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>';
285
274
  }
286
-
287
- async function runDemo() {
288
- var output = document.getElementById('demo-output');
289
- var log = document.getElementById('demo-log');
290
- output.style.display = 'block';
291
- log.textContent = '';
292
-
293
- function addLog(msg) { log.textContent += msg + '\n'; log.scrollTop = log.scrollHeight; }
294
-
295
- addLog('=== Agent Mesh Symphony Demo ===\n');
296
-
297
- try {
298
- // 1. Register agents
299
- addLog('[1/6] Registering agents in mesh...');
300
- var researcher = await (await fetch(API + '/api/mesh/agents', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ siteId: 'demo', role: 'researcher', displayName: 'PriceBot', capabilities: ['scrape', 'compare'] }) })).json();
301
- var analyst = await (await fetch(API + '/api/mesh/agents', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ siteId: 'demo', role: 'analyst', displayName: 'ValueBot', capabilities: ['score', 'rank'] }) })).json();
302
- var negotiator = await (await fetch(API + '/api/mesh/agents', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ siteId: 'demo', role: 'negotiator', displayName: 'DealBot', capabilities: ['negotiate', 'bundle'] }) })).json();
303
- var guardian = await (await fetch(API + '/api/mesh/agents', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ siteId: 'demo', role: 'guardian', displayName: 'TrustBot', capabilities: ['verify', 'audit'] }) })).json();
304
- addLog(' Researcher: ' + researcher.id.slice(0, 8) + ' (PriceBot)');
305
- addLog(' Analyst: ' + analyst.id.slice(0, 8) + ' (ValueBot)');
306
- addLog(' Negotiator: ' + negotiator.id.slice(0, 8) + ' (DealBot)');
307
- addLog(' Guardian: ' + guardian.id.slice(0, 8) + ' (TrustBot)');
308
-
309
- // 2. Share knowledge
310
- addLog('\n[2/6] Sharing knowledge to mesh...');
311
- await (await fetch(API + '/api/mesh/knowledge', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ agentId: researcher.id, knowledgeType: 'price', domain: 'electronics', key: 'iphone-15-price', value: { price: 999, currency: 'USD', source: 'apple.com' }, confidence: 0.95 }) })).json();
312
- addLog(' Shared: iPhone 15 price data (confidence: 0.95)');
313
-
314
- await (await fetch(API + '/api/mesh/knowledge', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ agentId: researcher.id, knowledgeType: 'price', domain: 'electronics', key: 'competitor-price', value: { price: 949, currency: 'USD', source: 'bestbuy.com' }, confidence: 0.9 }) })).json();
315
- addLog(' Shared: Competitor price data (confidence: 0.90)');
316
-
317
- // 3. Broadcast alert
318
- addLog('\n[3/6] Broadcasting alert...');
319
- await (await fetch(API + '/api/mesh/alerts', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ senderId: guardian.id, subject: 'Price drop detected on competitor site', details: { domain: 'electronics', oldPrice: 999, newPrice: 949 }, priority: 2 }) })).json();
320
- addLog(' Alert: Price drop detected on competitor site');
321
-
322
- // 4. Run Symphony
323
- addLog('\n[4/6] Running Symphony Orchestration...');
324
- addLog(' Template: purchase_advisor');
325
- addLog(' Phases: discover analyze → negotiate → guard → decide');
326
- var symphony = await (await fetch(API + '/api/mesh/symphony/perform', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({
327
- siteId: 'demo',
328
- task: 'Find the best deal on iPhone 15',
329
- taskType: 'purchase',
330
- inputData: {
331
- siteData: { schema: { actions: { buy: { params: {} }, compare: { params: {} } } }, policies: { returns: '30 days', shipping: 'Free over $50' } },
332
- products: [
333
- { name: 'iPhone 15', price: 999, rating: 4.7, inStock: true, discount: 5 },
334
- { name: 'iPhone 15 Pro', price: 1199, rating: 4.8, inStock: true },
335
- { name: 'iPhone 14', price: 699, rating: 4.5, inStock: true, discount: 15 }
336
- ],
337
- preferences: { maxPrice: 1000, preferredCategory: 'electronics' }
338
- }
339
- }) })).json();
340
-
341
- addLog('\n Symphony Results:');
342
- addLog(' Status: ' + symphony.status);
343
- addLog(' Confidence: ' + (symphony.confidence * 100).toFixed(1) + '%');
344
- addLog(' Duration: ' + symphony.duration_ms + 'ms');
345
-
346
- if (symphony.phases) {
347
- Object.entries(symphony.phases).forEach(function(entry) {
348
- var phase = entry[0], result = entry[1];
349
- addLog('\n Phase: ' + phase);
350
- if (result.output?.reasoning) addLog(' → ' + result.output.reasoning);
351
- if (result.consensus?.reasoning) {
352
- result.consensus.reasoning.forEach(function(r) { addLog(' → ' + r); });
353
- }
354
- });
355
- }
356
-
357
- // 5. Learning
358
- addLog('\n[5/6] Training learning engine...');
359
- var decision = await (await fetch(API + '/api/mesh/learning/decisions', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ siteId: 'demo', agentId: researcher.id, domain: 'electronics', action: 'buy_cheapest', context: { category: 'phones' }, features: { price: 699, discount: 15, rating: 4.5 } }) })).json();
360
- addLog(' Decision recorded: ' + decision.decisionId?.slice(0, 8));
361
- addLog(' Predicted reward: ' + (decision.predictedReward * 100).toFixed(1) + '%');
362
-
363
- var fb = await (await fetch(API + '/api/mesh/learning/decisions/' + decision.decisionId + '/feedback', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ outcome: 'accepted', reward: 0.8 }) })).json();
364
- addLog(' Feedback: accepted (reward: 0.8)');
365
- addLog(' Updated confidence: ' + (fb.updatedConfidence * 100).toFixed(1) + '%');
366
-
367
- // 6. Get recommendation
368
- addLog('\n[6/6] Getting recommendation...');
369
- var rec = await (await fetch(API + '/api/mesh/learning/recommend', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ siteId: 'demo', agentId: researcher.id, domain: 'electronics', actions: ['buy_cheapest', 'buy_best_rated', 'wait_for_sale'], context: { category: 'phones', price: 999 } }) })).json();
370
- addLog(' Recommended: ' + rec.recommended);
371
- addLog(' Confidence: ' + (rec.confidence * 100).toFixed(1) + '%');
372
-
373
- addLog('\n=== Demo Complete ===');
374
- addLog('All processing was LOCAL — zero external API calls, zero tokens consumed.');
375
-
376
- } catch (err) {
377
- addLog('\nError: ' + err.message);
378
- }
379
-
380
- loadDashboard();
381
- }
382
-
383
- function timeAgo(dateStr) {
384
- if (!dateStr) return 'never';
385
- var diff = Date.now() - new Date(dateStr).getTime();
386
- if (diff < 60000) return 'just now';
387
- if (diff < 3600000) return Math.floor(diff / 60000) + 'm ago';
388
- if (diff < 86400000) return Math.floor(diff / 3600000) + 'h ago';
389
- return Math.floor(diff / 86400000) + 'd ago';
390
- }
391
-
392
- function escapeHtml(str) {
393
- var div = document.createElement('div');
394
- div.textContent = str || '';
395
- return div.innerHTML;
396
- }
397
-
398
- loadDashboard();
399
- </script>
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>
400
327
  </body>
401
328
  </html>