web-agent-bridge 2.2.0 → 2.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.
@@ -356,6 +356,99 @@
356
356
  }).then(function(r) { return r.json(); });
357
357
  };
358
358
 
359
+ // ── Agent Mesh Protocol ───────────────────────────────────────────
360
+
361
+ WABInstance.prototype.meshJoin = function(role, displayName, capabilities) {
362
+ if (!this.serverUrl) return Promise.resolve({ error: 'Mesh requires a server URL' });
363
+ var self = this;
364
+ return fetch(this.serverUrl + '/api/mesh/agents', {
365
+ method: 'POST',
366
+ headers: { 'Content-Type': 'application/json' },
367
+ body: JSON.stringify({ siteId: this.name, role: role, displayName: displayName, capabilities: capabilities })
368
+ }).then(function(r) { return r.json(); }).then(function(data) {
369
+ self._meshAgentId = data.id;
370
+ self._emit('mesh:joined', data);
371
+ return data;
372
+ });
373
+ };
374
+
375
+ WABInstance.prototype.meshPublish = function(channel, messageType, subject, payload, opts) {
376
+ if (!this.serverUrl || !this._meshAgentId) return Promise.resolve({ error: 'Must join mesh first' });
377
+ return fetch(this.serverUrl + '/api/mesh/channels/' + encodeURIComponent(channel) + '/messages', {
378
+ method: 'POST',
379
+ headers: { 'Content-Type': 'application/json' },
380
+ body: JSON.stringify(Object.assign({ senderId: this._meshAgentId, messageType: messageType, subject: subject, payload: payload }, opts || {}))
381
+ }).then(function(r) { return r.json(); });
382
+ };
383
+
384
+ WABInstance.prototype.meshReceive = function(channel, limit) {
385
+ if (!this.serverUrl || !this._meshAgentId) return Promise.resolve({ error: 'Must join mesh first' });
386
+ return fetch(this.serverUrl + '/api/mesh/agents/' + this._meshAgentId + '/messages/' + encodeURIComponent(channel) + '?limit=' + (limit || 20))
387
+ .then(function(r) { return r.json(); });
388
+ };
389
+
390
+ WABInstance.prototype.meshShareKnowledge = function(knowledgeType, domain, key, value, confidence) {
391
+ if (!this.serverUrl || !this._meshAgentId) return Promise.resolve({ error: 'Must join mesh first' });
392
+ return fetch(this.serverUrl + '/api/mesh/knowledge', {
393
+ method: 'POST',
394
+ headers: { 'Content-Type': 'application/json' },
395
+ body: JSON.stringify({ agentId: this._meshAgentId, knowledgeType: knowledgeType, domain: domain, key: key, value: value, confidence: confidence })
396
+ }).then(function(r) { return r.json(); });
397
+ };
398
+
399
+ WABInstance.prototype.meshAlert = function(subject, details, priority) {
400
+ if (!this.serverUrl || !this._meshAgentId) return Promise.resolve({ error: 'Must join mesh first' });
401
+ return fetch(this.serverUrl + '/api/mesh/alerts', {
402
+ method: 'POST',
403
+ headers: { 'Content-Type': 'application/json' },
404
+ body: JSON.stringify({ senderId: this._meshAgentId, subject: subject, details: details, priority: priority })
405
+ }).then(function(r) { return r.json(); });
406
+ };
407
+
408
+ // ── Agent Symphony Orchestrator ───────────────────────────────────
409
+
410
+ WABInstance.prototype.symphonyPerform = function(task, taskType, inputData) {
411
+ if (!this.serverUrl) return Promise.resolve({ error: 'Symphony requires a server URL' });
412
+ var self = this;
413
+ return fetch(this.serverUrl + '/api/mesh/symphony/perform', {
414
+ method: 'POST',
415
+ headers: { 'Content-Type': 'application/json' },
416
+ body: JSON.stringify({ siteId: this.name, task: task, taskType: taskType, inputData: inputData })
417
+ }).then(function(r) { return r.json(); }).then(function(data) {
418
+ self._emit('symphony:completed', data);
419
+ return data;
420
+ });
421
+ };
422
+
423
+ // ── Agent Learning Engine ─────────────────────────────────────────
424
+
425
+ WABInstance.prototype.learnRecord = function(domain, action, context, features) {
426
+ if (!this.serverUrl || !this._meshAgentId) return Promise.resolve({ error: 'Must join mesh first' });
427
+ return fetch(this.serverUrl + '/api/mesh/learning/decisions', {
428
+ method: 'POST',
429
+ headers: { 'Content-Type': 'application/json' },
430
+ body: JSON.stringify({ siteId: this.name, agentId: this._meshAgentId, domain: domain, action: action, context: context, features: features })
431
+ }).then(function(r) { return r.json(); });
432
+ };
433
+
434
+ WABInstance.prototype.learnFeedback = function(decisionId, outcome, reward) {
435
+ if (!this.serverUrl) return Promise.resolve({ error: 'Learning requires a server URL' });
436
+ return fetch(this.serverUrl + '/api/mesh/learning/decisions/' + decisionId + '/feedback', {
437
+ method: 'POST',
438
+ headers: { 'Content-Type': 'application/json' },
439
+ body: JSON.stringify({ outcome: outcome, reward: reward })
440
+ }).then(function(r) { return r.json(); });
441
+ };
442
+
443
+ WABInstance.prototype.learnRecommend = function(domain, actions, context) {
444
+ if (!this.serverUrl || !this._meshAgentId) return Promise.resolve({ error: 'Must join mesh first' });
445
+ return fetch(this.serverUrl + '/api/mesh/learning/recommend', {
446
+ method: 'POST',
447
+ headers: { 'Content-Type': 'application/json' },
448
+ body: JSON.stringify({ siteId: this.name, agentId: this._meshAgentId, domain: domain, actions: actions, context: context })
449
+ }).then(function(r) { return r.json(); });
450
+ };
451
+
359
452
  // ── Static API ────────────────────────────────────────────────────
360
453
 
361
454
  var WAB = {
@@ -397,6 +490,51 @@
397
490
  return Promise.resolve({ error: 'WAB not initialized' });
398
491
  },
399
492
 
493
+ meshJoin: function(role, displayName, capabilities) {
494
+ if (WAB._instance) return WAB._instance.meshJoin(role, displayName, capabilities);
495
+ return Promise.resolve({ error: 'WAB not initialized' });
496
+ },
497
+
498
+ meshPublish: function(channel, messageType, subject, payload, opts) {
499
+ if (WAB._instance) return WAB._instance.meshPublish(channel, messageType, subject, payload, opts);
500
+ return Promise.resolve({ error: 'WAB not initialized' });
501
+ },
502
+
503
+ meshReceive: function(channel, limit) {
504
+ if (WAB._instance) return WAB._instance.meshReceive(channel, limit);
505
+ return Promise.resolve({ error: 'WAB not initialized' });
506
+ },
507
+
508
+ meshShareKnowledge: function(knowledgeType, domain, key, value, confidence) {
509
+ if (WAB._instance) return WAB._instance.meshShareKnowledge(knowledgeType, domain, key, value, confidence);
510
+ return Promise.resolve({ error: 'WAB not initialized' });
511
+ },
512
+
513
+ meshAlert: function(subject, details, priority) {
514
+ if (WAB._instance) return WAB._instance.meshAlert(subject, details, priority);
515
+ return Promise.resolve({ error: 'WAB not initialized' });
516
+ },
517
+
518
+ symphonyPerform: function(task, taskType, inputData) {
519
+ if (WAB._instance) return WAB._instance.symphonyPerform(task, taskType, inputData);
520
+ return Promise.resolve({ error: 'WAB not initialized' });
521
+ },
522
+
523
+ learnRecord: function(domain, action, context, features) {
524
+ if (WAB._instance) return WAB._instance.learnRecord(domain, action, context, features);
525
+ return Promise.resolve({ error: 'WAB not initialized' });
526
+ },
527
+
528
+ learnFeedback: function(decisionId, outcome, reward) {
529
+ if (WAB._instance) return WAB._instance.learnFeedback(decisionId, outcome, reward);
530
+ return Promise.resolve({ error: 'WAB not initialized' });
531
+ },
532
+
533
+ learnRecommend: function(domain, actions, context) {
534
+ if (WAB._instance) return WAB._instance.learnRecommend(domain, actions, context);
535
+ return Promise.resolve({ error: 'WAB not initialized' });
536
+ },
537
+
400
538
  _instance: null
401
539
  };
402
540
 
@@ -1426,6 +1426,132 @@
1426
1426
  this.logger.log('refresh', {});
1427
1427
  }
1428
1428
 
1429
+ // ── Agent Mesh Protocol (Client-Side) ───────────────────────────────
1430
+
1431
+ /**
1432
+ * Join the agent mesh — register this agent and return its mesh identity.
1433
+ */
1434
+ async meshJoin(role, displayName, capabilities) {
1435
+ const base = this._resolveApiBase();
1436
+ const res = await fetch(`${base}/api/mesh/agents`, {
1437
+ method: 'POST',
1438
+ headers: { 'Content-Type': 'application/json' },
1439
+ body: JSON.stringify({ siteId: this.config.siteId, role, displayName, capabilities })
1440
+ });
1441
+ const data = await res.json();
1442
+ this._meshAgentId = data.id;
1443
+ this.events.emit('mesh:joined', data);
1444
+ return data;
1445
+ }
1446
+
1447
+ /**
1448
+ * Publish a message to a mesh channel.
1449
+ */
1450
+ async meshPublish(channel, messageType, subject, payload, opts) {
1451
+ if (!this._meshAgentId) throw new Error('Must call meshJoin() first');
1452
+ const base = this._resolveApiBase();
1453
+ const res = await fetch(`${base}/api/mesh/channels/${encodeURIComponent(channel)}/messages`, {
1454
+ method: 'POST',
1455
+ headers: { 'Content-Type': 'application/json' },
1456
+ body: JSON.stringify({ senderId: this._meshAgentId, messageType, subject, payload, ...opts })
1457
+ });
1458
+ return res.json();
1459
+ }
1460
+
1461
+ /**
1462
+ * Get unread mesh messages for this agent on a channel.
1463
+ */
1464
+ async meshReceive(channel, limit) {
1465
+ if (!this._meshAgentId) throw new Error('Must call meshJoin() first');
1466
+ const base = this._resolveApiBase();
1467
+ const res = await fetch(`${base}/api/mesh/agents/${this._meshAgentId}/messages/${encodeURIComponent(channel)}?limit=${limit || 20}`);
1468
+ return res.json();
1469
+ }
1470
+
1471
+ /**
1472
+ * Share knowledge to the mesh.
1473
+ */
1474
+ async meshShareKnowledge(knowledgeType, domain, key, value, confidence) {
1475
+ if (!this._meshAgentId) throw new Error('Must call meshJoin() first');
1476
+ const base = this._resolveApiBase();
1477
+ const res = await fetch(`${base}/api/mesh/knowledge`, {
1478
+ method: 'POST',
1479
+ headers: { 'Content-Type': 'application/json' },
1480
+ body: JSON.stringify({ agentId: this._meshAgentId, knowledgeType, domain, key, value, confidence })
1481
+ });
1482
+ return res.json();
1483
+ }
1484
+
1485
+ /**
1486
+ * Broadcast an alert to all mesh agents.
1487
+ */
1488
+ async meshAlert(subject, details, priority) {
1489
+ if (!this._meshAgentId) throw new Error('Must call meshJoin() first');
1490
+ const base = this._resolveApiBase();
1491
+ const res = await fetch(`${base}/api/mesh/alerts`, {
1492
+ method: 'POST',
1493
+ headers: { 'Content-Type': 'application/json' },
1494
+ body: JSON.stringify({ senderId: this._meshAgentId, subject, details, priority })
1495
+ });
1496
+ return res.json();
1497
+ }
1498
+
1499
+ /**
1500
+ * Run a full symphony — orchestrate specialized agents for a task.
1501
+ */
1502
+ async symphonyPerform(task, taskType, inputData) {
1503
+ const base = this._resolveApiBase();
1504
+ const res = await fetch(`${base}/api/mesh/symphony/perform`, {
1505
+ method: 'POST',
1506
+ headers: { 'Content-Type': 'application/json' },
1507
+ body: JSON.stringify({ siteId: this.config.siteId, task, taskType, inputData })
1508
+ });
1509
+ const data = await res.json();
1510
+ this.events.emit('symphony:completed', data);
1511
+ return data;
1512
+ }
1513
+
1514
+ /**
1515
+ * Record a decision for the learning engine and get prediction.
1516
+ */
1517
+ async learnRecord(domain, action, context, features) {
1518
+ if (!this._meshAgentId) throw new Error('Must call meshJoin() first');
1519
+ const base = this._resolveApiBase();
1520
+ const res = await fetch(`${base}/api/mesh/learning/decisions`, {
1521
+ method: 'POST',
1522
+ headers: { 'Content-Type': 'application/json' },
1523
+ body: JSON.stringify({ siteId: this.config.siteId, agentId: this._meshAgentId, domain, action, context, features })
1524
+ });
1525
+ return res.json();
1526
+ }
1527
+
1528
+ /**
1529
+ * Provide feedback on a decision — the learning signal.
1530
+ */
1531
+ async learnFeedback(decisionId, outcome, reward) {
1532
+ const base = this._resolveApiBase();
1533
+ const res = await fetch(`${base}/api/mesh/learning/decisions/${decisionId}/feedback`, {
1534
+ method: 'POST',
1535
+ headers: { 'Content-Type': 'application/json' },
1536
+ body: JSON.stringify({ outcome, reward })
1537
+ });
1538
+ return res.json();
1539
+ }
1540
+
1541
+ /**
1542
+ * Get recommendation from learning engine.
1543
+ */
1544
+ async learnRecommend(domain, actions, context) {
1545
+ if (!this._meshAgentId) throw new Error('Must call meshJoin() first');
1546
+ const base = this._resolveApiBase();
1547
+ const res = await fetch(`${base}/api/mesh/learning/recommend`, {
1548
+ method: 'POST',
1549
+ headers: { 'Content-Type': 'application/json' },
1550
+ body: JSON.stringify({ siteId: this.config.siteId, agentId: this._meshAgentId, domain, actions, context })
1551
+ });
1552
+ return res.json();
1553
+ }
1554
+
1429
1555
  destroy() {
1430
1556
  this.events.emit('destroy');
1431
1557
  if (this._mutationObserver) {
@@ -0,0 +1,333 @@
1
+ /**
2
+ * WAB Agent Mesh SDK
3
+ *
4
+ * Provides a high-level client for interacting with the Private Agent Mesh:
5
+ * - Join the mesh and communicate with other agents
6
+ * - Share and query collective knowledge
7
+ * - Run symphony orchestrations (Researcher → Analyst → Negotiator → Guardian)
8
+ * - Learn from user decisions via local reinforcement learning
9
+ *
10
+ * Usage:
11
+ * const { WABAgentMesh } = require('web-agent-bridge-sdk/agent-mesh');
12
+ * const mesh = new WABAgentMesh('https://your-wab-server.com');
13
+ *
14
+ * const agent = await mesh.join('researcher', 'PriceBot');
15
+ * await mesh.shareKnowledge('price', 'amazon.com', 'iphone-15', { price: 999, currency: 'USD' });
16
+ * const result = await mesh.symphony('Find the best deal on iPhone 15', 'purchase', { products: [...] });
17
+ */
18
+
19
+ class WABAgentMesh {
20
+ /**
21
+ * @param {string} serverUrl — WAB server base URL
22
+ * @param {object} [options]
23
+ * @param {string} [options.siteId] — Site identifier
24
+ * @param {number} [options.heartbeatInterval=30000] — Heartbeat interval in ms
25
+ */
26
+ constructor(serverUrl, options = {}) {
27
+ this.serverUrl = serverUrl.replace(/\/$/, '');
28
+ this.siteId = options.siteId || 'default';
29
+ this.heartbeatInterval = options.heartbeatInterval || 30000;
30
+ this.agentId = null;
31
+ this.role = null;
32
+ this._heartbeatTimer = null;
33
+ }
34
+
35
+ // ─── Mesh Management ──────────────────────────────────────────────
36
+
37
+ /**
38
+ * Join the agent mesh with a role.
39
+ * @param {string} role — Agent role (researcher, negotiator, analyst, guardian, etc.)
40
+ * @param {string} [displayName] — Human-readable name
41
+ * @param {string[]} [capabilities] — List of capabilities
42
+ * @returns {Promise<{id: string, role: string, displayName: string}>}
43
+ */
44
+ async join(role, displayName, capabilities = []) {
45
+ const data = await this._post('/api/mesh/agents', {
46
+ siteId: this.siteId, role, displayName, capabilities
47
+ });
48
+ this.agentId = data.id;
49
+ this.role = role;
50
+
51
+ // Start heartbeat
52
+ this._heartbeatTimer = setInterval(() => {
53
+ this._post(`/api/mesh/agents/${this.agentId}/heartbeat`, {}).catch(() => {});
54
+ }, this.heartbeatInterval);
55
+ if (this._heartbeatTimer.unref) this._heartbeatTimer.unref();
56
+
57
+ return data;
58
+ }
59
+
60
+ /**
61
+ * Leave the mesh.
62
+ */
63
+ async leave() {
64
+ if (this._heartbeatTimer) {
65
+ clearInterval(this._heartbeatTimer);
66
+ this._heartbeatTimer = null;
67
+ }
68
+ if (this.agentId) {
69
+ await this._patch(`/api/mesh/agents/${this.agentId}/status`, { status: 'offline' });
70
+ this.agentId = null;
71
+ }
72
+ }
73
+
74
+ /**
75
+ * Get all active agents in the mesh.
76
+ */
77
+ async getAgents() {
78
+ return this._get('/api/mesh/agents');
79
+ }
80
+
81
+ /**
82
+ * Get agents by role.
83
+ */
84
+ async getAgentsByRole(role) {
85
+ return this._get(`/api/mesh/agents/role/${encodeURIComponent(role)}`);
86
+ }
87
+
88
+ // ─── Messaging ────────────────────────────────────────────────────
89
+
90
+ /**
91
+ * Publish a message to a channel.
92
+ */
93
+ async publish(channel, messageType, subject, payload = {}, options = {}) {
94
+ this._requireAgent();
95
+ return this._post(`/api/mesh/channels/${encodeURIComponent(channel)}/messages`, {
96
+ senderId: this.agentId, messageType, subject, payload,
97
+ priority: options.priority, ttl: options.ttl, targetId: options.targetId
98
+ });
99
+ }
100
+
101
+ /**
102
+ * Get unread messages for this agent on a channel.
103
+ */
104
+ async receive(channel, limit = 20) {
105
+ this._requireAgent();
106
+ return this._get(`/api/mesh/agents/${this.agentId}/messages/${encodeURIComponent(channel)}?limit=${limit}`);
107
+ }
108
+
109
+ /**
110
+ * Acknowledge a message.
111
+ */
112
+ async acknowledge(messageId) {
113
+ this._requireAgent();
114
+ return this._post(`/api/mesh/agents/${this.agentId}/messages/${messageId}/ack`, {});
115
+ }
116
+
117
+ /**
118
+ * Get unread count.
119
+ */
120
+ async getUnreadCount() {
121
+ this._requireAgent();
122
+ return this._get(`/api/mesh/agents/${this.agentId}/unread`);
123
+ }
124
+
125
+ /**
126
+ * Broadcast an alert to all mesh agents.
127
+ */
128
+ async alert(subject, details = {}, priority = 2) {
129
+ this._requireAgent();
130
+ return this._post('/api/mesh/alerts', { senderId: this.agentId, subject, details, priority });
131
+ }
132
+
133
+ /**
134
+ * Share a tactic with the mesh.
135
+ */
136
+ async shareTactic(domain, tactic, confidence = 1.0) {
137
+ this._requireAgent();
138
+ return this._post('/api/mesh/tactics', {
139
+ senderId: this.agentId, domain, tactic, confidence
140
+ });
141
+ }
142
+
143
+ /**
144
+ * Request help from other agents.
145
+ */
146
+ async requestHelp(subject, question, targetRole = null) {
147
+ this._requireAgent();
148
+ return this._post('/api/mesh/help', {
149
+ senderId: this.agentId, subject, question, targetRole
150
+ });
151
+ }
152
+
153
+ // ─── Knowledge ────────────────────────────────────────────────────
154
+
155
+ /**
156
+ * Share knowledge to the mesh.
157
+ */
158
+ async shareKnowledge(knowledgeType, domain, key, value, confidence = 1.0) {
159
+ this._requireAgent();
160
+ return this._post('/api/mesh/knowledge', {
161
+ agentId: this.agentId, knowledgeType, domain, key, value, confidence
162
+ });
163
+ }
164
+
165
+ /**
166
+ * Query knowledge by domain and key.
167
+ */
168
+ async queryKnowledge(domain, key) {
169
+ return this._get(`/api/mesh/knowledge/${encodeURIComponent(domain)}/${encodeURIComponent(key)}`);
170
+ }
171
+
172
+ /**
173
+ * Search knowledge by domain.
174
+ */
175
+ async searchKnowledge(domain, limit = 20) {
176
+ return this._get(`/api/mesh/knowledge/${encodeURIComponent(domain)}?limit=${limit}`);
177
+ }
178
+
179
+ // ─── Symphony Orchestrator ────────────────────────────────────────
180
+
181
+ /**
182
+ * Run a full symphony — coordinate researcher, analyst, negotiator, guardian.
183
+ * @param {string} task — Task description
184
+ * @param {string} taskType — purchase, price_comparison, negotiation, exploration, verification
185
+ * @param {object} [inputData] — Data to pass to symphony phases
186
+ * @param {object} [agentIds] — Map role → agentId for specific agents
187
+ */
188
+ async symphony(task, taskType, inputData = {}, agentIds = {}) {
189
+ return this._post('/api/mesh/symphony/perform', {
190
+ siteId: this.siteId, task, taskType, inputData, agentIds
191
+ });
192
+ }
193
+
194
+ /**
195
+ * Compose a symphony step-by-step.
196
+ */
197
+ async symphonyCompose(task, taskType, agentIds = {}) {
198
+ return this._post('/api/mesh/symphony/compose', {
199
+ siteId: this.siteId, task, taskType, agentIds
200
+ });
201
+ }
202
+
203
+ /**
204
+ * Execute a single symphony phase.
205
+ */
206
+ async symphonyPhase(compositionId, phase, input = {}) {
207
+ return this._post(`/api/mesh/symphony/${compositionId}/phase`, { phase, input });
208
+ }
209
+
210
+ /**
211
+ * Get composition details.
212
+ */
213
+ async getComposition(compositionId) {
214
+ return this._get(`/api/mesh/symphony/${compositionId}`);
215
+ }
216
+
217
+ /**
218
+ * Get available symphony templates.
219
+ */
220
+ async getTemplates() {
221
+ return this._get('/api/mesh/symphony/templates/all');
222
+ }
223
+
224
+ // ─── Learning Engine ──────────────────────────────────────────────
225
+
226
+ /**
227
+ * Record a decision for the learning engine.
228
+ */
229
+ async recordDecision(domain, action, context = {}, features = {}) {
230
+ this._requireAgent();
231
+ return this._post('/api/mesh/learning/decisions', {
232
+ siteId: this.siteId, agentId: this.agentId, domain, action, context, features
233
+ });
234
+ }
235
+
236
+ /**
237
+ * Provide feedback on a decision.
238
+ * @param {string} decisionId
239
+ * @param {string} outcome — accepted, rejected, modified
240
+ * @param {number} reward — 0.0 to 1.0
241
+ */
242
+ async feedback(decisionId, outcome, reward) {
243
+ return this._post(`/api/mesh/learning/decisions/${decisionId}/feedback`, { outcome, reward });
244
+ }
245
+
246
+ /**
247
+ * Get recommendation based on learned preferences.
248
+ * @param {string} domain
249
+ * @param {string[]} actions — Available actions to choose from
250
+ * @param {object} [context] — Current context
251
+ */
252
+ async recommend(domain, actions, context = {}) {
253
+ this._requireAgent();
254
+ return this._post('/api/mesh/learning/recommend', {
255
+ siteId: this.siteId, agentId: this.agentId, domain, actions, context
256
+ });
257
+ }
258
+
259
+ /**
260
+ * Get learned preferences for a domain.
261
+ */
262
+ async getPreferences(domain) {
263
+ this._requireAgent();
264
+ return this._get(`/api/mesh/learning/preferences/${encodeURIComponent(this.siteId)}/${this.agentId}/${encodeURIComponent(domain)}`);
265
+ }
266
+
267
+ /**
268
+ * Get learning stats.
269
+ */
270
+ async getLearningStats() {
271
+ this._requireAgent();
272
+ return this._get(`/api/mesh/learning/stats/${encodeURIComponent(this.siteId)}/${this.agentId}`);
273
+ }
274
+
275
+ // ─── Stats ────────────────────────────────────────────────────────
276
+
277
+ /**
278
+ * Get mesh statistics.
279
+ */
280
+ async getStats() {
281
+ return this._get('/api/mesh/stats');
282
+ }
283
+
284
+ /**
285
+ * Get full dashboard data (agents, channels, templates, stats).
286
+ */
287
+ async getDashboard() {
288
+ return this._get('/api/mesh/dashboard');
289
+ }
290
+
291
+ // ─── Internal ─────────────────────────────────────────────────────
292
+
293
+ _requireAgent() {
294
+ if (!this.agentId) throw new Error('Must call join() first');
295
+ }
296
+
297
+ async _get(path) {
298
+ const res = await fetch(`${this.serverUrl}${path}`);
299
+ if (!res.ok) {
300
+ const err = await res.json().catch(() => ({ error: res.statusText }));
301
+ throw new Error(err.error || `HTTP ${res.status}`);
302
+ }
303
+ return res.json();
304
+ }
305
+
306
+ async _post(path, body) {
307
+ const res = await fetch(`${this.serverUrl}${path}`, {
308
+ method: 'POST',
309
+ headers: { 'Content-Type': 'application/json' },
310
+ body: JSON.stringify(body)
311
+ });
312
+ if (!res.ok) {
313
+ const err = await res.json().catch(() => ({ error: res.statusText }));
314
+ throw new Error(err.error || `HTTP ${res.status}`);
315
+ }
316
+ return res.json();
317
+ }
318
+
319
+ async _patch(path, body) {
320
+ const res = await fetch(`${this.serverUrl}${path}`, {
321
+ method: 'PATCH',
322
+ headers: { 'Content-Type': 'application/json' },
323
+ body: JSON.stringify(body)
324
+ });
325
+ if (!res.ok) {
326
+ const err = await res.json().catch(() => ({ error: res.statusText }));
327
+ throw new Error(err.error || `HTTP ${res.status}`);
328
+ }
329
+ return res.json();
330
+ }
331
+ }
332
+
333
+ module.exports = { WABAgentMesh };
package/sdk/index.js CHANGED
@@ -255,5 +255,6 @@ class WABAgent {
255
255
  }
256
256
 
257
257
  const { WABMultiAgent } = require('./multi-agent');
258
+ const { WABAgentMesh } = require('./agent-mesh');
258
259
 
259
- module.exports = { WABAgent, WABMultiAgent };
260
+ module.exports = { WABAgent, WABMultiAgent, WABAgentMesh };
package/sdk/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "web-agent-bridge-sdk",
3
- "version": "2.2.0",
3
+ "version": "2.3.0",
4
4
  "description": "SDK for building AI agents that interact with Web Agent Bridge (WAB)",
5
5
  "main": "index.js",
6
6
  "license": "MIT",
package/server/index.js CHANGED
@@ -19,6 +19,7 @@ const licenseRoutes = require('./routes/license');
19
19
  const adminRoutes = require('./routes/admin');
20
20
  const billingRoutes = require('./routes/billing');
21
21
  const sovereignRoutes = require('./routes/sovereign');
22
+ const meshRoutes = require('./routes/mesh');
22
23
  const { handleWebhookRequest } = require('./services/stripe');
23
24
 
24
25
  const app = express();
@@ -113,10 +114,14 @@ app.use('/api/license', licenseLimiter, licenseRoutes);
113
114
  app.use('/api/admin', apiLimiter, adminRoutes);
114
115
  app.use('/api/billing', apiLimiter, billingRoutes);
115
116
  app.use('/api/sovereign', apiLimiter, sovereignRoutes);
117
+ app.use('/api/mesh', apiLimiter, meshRoutes);
116
118
 
117
119
  app.get('/dashboard', (req, res) => {
118
120
  res.sendFile(path.join(__dirname, '..', 'public', 'dashboard.html'));
119
121
  });
122
+ app.get('/mesh-dashboard', (req, res) => {
123
+ res.sendFile(path.join(__dirname, '..', 'public', 'mesh-dashboard.html'));
124
+ });
120
125
  app.get('/docs', (req, res) => {
121
126
  res.sendFile(path.join(__dirname, '..', 'public', 'docs.html'));
122
127
  });