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.
- package/README.ar.md +7 -0
- package/README.md +7 -0
- package/package.json +1 -1
- package/public/index.html +52 -0
- package/public/llms.txt +1 -0
- package/public/mesh-dashboard.html +401 -0
- package/public/script/wab.min.js +138 -0
- package/script/ai-agent-bridge.js +126 -0
- package/sdk/agent-mesh.js +333 -0
- package/sdk/index.js +2 -1
- package/sdk/package.json +1 -1
- package/server/index.js +5 -0
- package/server/routes/mesh.js +300 -0
- package/server/services/agent-learning.js +422 -0
- package/server/services/agent-mesh.js +346 -0
- package/server/services/agent-symphony.js +681 -0
package/public/script/wab.min.js
CHANGED
|
@@ -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
package/sdk/package.json
CHANGED
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
|
});
|