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
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mesh API Routes
|
|
3
|
+
* ════════════════════════════════════════════════════════════════════════
|
|
4
|
+
* Routes for: Agent Mesh Protocol, Agent Learning Engine,
|
|
5
|
+
* and Agent Symphony Orchestrator.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const express = require('express');
|
|
9
|
+
const router = express.Router();
|
|
10
|
+
|
|
11
|
+
const mesh = require('../services/agent-mesh');
|
|
12
|
+
const learning = require('../services/agent-learning');
|
|
13
|
+
const symphony = require('../services/agent-symphony');
|
|
14
|
+
|
|
15
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
16
|
+
// AGENT MESH PROTOCOL
|
|
17
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
18
|
+
|
|
19
|
+
// Register an agent in the mesh
|
|
20
|
+
router.post('/agents', (req, res) => {
|
|
21
|
+
const { siteId, role, displayName, capabilities } = req.body;
|
|
22
|
+
if (!role) return res.status(400).json({ error: 'role is required' });
|
|
23
|
+
const result = mesh.registerAgent(siteId || 'default', role, displayName, capabilities);
|
|
24
|
+
res.json(result);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
// Get all active agents
|
|
28
|
+
router.get('/agents', (req, res) => {
|
|
29
|
+
res.json(mesh.getActiveAgents());
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
// Get agents by role
|
|
33
|
+
router.get('/agents/role/:role', (req, res) => {
|
|
34
|
+
res.json(mesh.getAgentsByRole(req.params.role));
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
// Agent heartbeat
|
|
38
|
+
router.post('/agents/:agentId/heartbeat', (req, res) => {
|
|
39
|
+
mesh.heartbeat(req.params.agentId);
|
|
40
|
+
res.json({ ok: true });
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
// Set agent status
|
|
44
|
+
router.patch('/agents/:agentId/status', (req, res) => {
|
|
45
|
+
const { status } = req.body;
|
|
46
|
+
if (!status) return res.status(400).json({ error: 'status is required' });
|
|
47
|
+
mesh.setAgentStatus(req.params.agentId, status);
|
|
48
|
+
res.json({ ok: true });
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
// Get channels
|
|
52
|
+
router.get('/channels', (req, res) => {
|
|
53
|
+
res.json(mesh.getChannels());
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
// Publish message to channel
|
|
57
|
+
router.post('/channels/:channel/messages', (req, res) => {
|
|
58
|
+
const { senderId, messageType, subject, payload, priority, ttl, targetId } = req.body;
|
|
59
|
+
if (!senderId || !messageType || !subject) {
|
|
60
|
+
return res.status(400).json({ error: 'senderId, messageType, and subject are required' });
|
|
61
|
+
}
|
|
62
|
+
try {
|
|
63
|
+
const msg = mesh.publish(senderId, req.params.channel, messageType, subject, payload || {}, { priority, ttl, targetId });
|
|
64
|
+
res.json(msg);
|
|
65
|
+
} catch (err) {
|
|
66
|
+
res.status(400).json({ error: err.message });
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
// Get messages from channel
|
|
71
|
+
router.get('/channels/:channel/messages', (req, res) => {
|
|
72
|
+
const limit = Math.min(parseInt(req.query.limit) || 50, 200);
|
|
73
|
+
res.json(mesh.getMessages(req.params.channel, limit));
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
// Get unread messages for an agent
|
|
77
|
+
router.get('/agents/:agentId/messages/:channel', (req, res) => {
|
|
78
|
+
const limit = Math.min(parseInt(req.query.limit) || 20, 100);
|
|
79
|
+
res.json(mesh.getMessagesForAgent(req.params.agentId, req.params.channel, limit));
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
// Acknowledge a message
|
|
83
|
+
router.post('/agents/:agentId/messages/:messageId/ack', (req, res) => {
|
|
84
|
+
mesh.acknowledge(req.params.agentId, req.params.messageId);
|
|
85
|
+
res.json({ ok: true });
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
// Get unread count
|
|
89
|
+
router.get('/agents/:agentId/unread', (req, res) => {
|
|
90
|
+
res.json({ count: mesh.getUnreadCount(req.params.agentId) });
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
// Broadcast alert
|
|
94
|
+
router.post('/alerts', (req, res) => {
|
|
95
|
+
const { senderId, subject, details, priority } = req.body;
|
|
96
|
+
if (!senderId || !subject) return res.status(400).json({ error: 'senderId and subject are required' });
|
|
97
|
+
res.json(mesh.broadcastAlert(senderId, subject, details || {}, priority));
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
// Share tactic
|
|
101
|
+
router.post('/tactics', (req, res) => {
|
|
102
|
+
const { senderId, domain, tactic, confidence } = req.body;
|
|
103
|
+
if (!senderId || !domain || !tactic) {
|
|
104
|
+
return res.status(400).json({ error: 'senderId, domain, and tactic are required' });
|
|
105
|
+
}
|
|
106
|
+
res.json(mesh.shareTactic(senderId, domain, tactic, confidence));
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
// Request help from other agents
|
|
110
|
+
router.post('/help', (req, res) => {
|
|
111
|
+
const { senderId, subject, question, targetRole } = req.body;
|
|
112
|
+
if (!senderId || !subject || !question) {
|
|
113
|
+
return res.status(400).json({ error: 'senderId, subject, and question are required' });
|
|
114
|
+
}
|
|
115
|
+
res.json(mesh.requestHelp(senderId, subject, question, targetRole));
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
119
|
+
// KNOWLEDGE SHARING
|
|
120
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
121
|
+
|
|
122
|
+
// Share knowledge to the mesh
|
|
123
|
+
router.post('/knowledge', (req, res) => {
|
|
124
|
+
const { agentId, knowledgeType, domain, key, value, confidence } = req.body;
|
|
125
|
+
if (!agentId || !knowledgeType || !domain || !key || !value) {
|
|
126
|
+
return res.status(400).json({ error: 'agentId, knowledgeType, domain, key, and value are required' });
|
|
127
|
+
}
|
|
128
|
+
res.json(mesh.shareKnowledge(agentId, knowledgeType, domain, key, value, confidence));
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
// Query knowledge
|
|
132
|
+
router.get('/knowledge/:domain/:key', (req, res) => {
|
|
133
|
+
const result = mesh.queryKnowledge(req.params.domain, req.params.key);
|
|
134
|
+
if (!result) return res.status(404).json({ error: 'Knowledge not found' });
|
|
135
|
+
res.json(result);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
// Search knowledge by domain
|
|
139
|
+
router.get('/knowledge/:domain', (req, res) => {
|
|
140
|
+
const limit = Math.min(parseInt(req.query.limit) || 20, 100);
|
|
141
|
+
res.json(mesh.searchKnowledge(req.params.domain, limit));
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
// Verify knowledge
|
|
145
|
+
router.post('/knowledge/:knowledgeId/verify', (req, res) => {
|
|
146
|
+
const { verifierAgentId, confidence } = req.body;
|
|
147
|
+
if (!verifierAgentId) return res.status(400).json({ error: 'verifierAgentId is required' });
|
|
148
|
+
mesh.verifyKnowledge(req.params.knowledgeId, verifierAgentId, confidence || 1.0);
|
|
149
|
+
res.json({ ok: true });
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
153
|
+
// AGENT LEARNING ENGINE
|
|
154
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
155
|
+
|
|
156
|
+
// Record a decision
|
|
157
|
+
router.post('/learning/decisions', (req, res) => {
|
|
158
|
+
const { siteId, agentId, domain, action, context, features } = req.body;
|
|
159
|
+
if (!siteId || !agentId || !domain || !action) {
|
|
160
|
+
return res.status(400).json({ error: 'siteId, agentId, domain, and action are required' });
|
|
161
|
+
}
|
|
162
|
+
res.json(learning.recordDecision(siteId, agentId, domain, action, context, features));
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
// Provide feedback on a decision
|
|
166
|
+
router.post('/learning/decisions/:decisionId/feedback', (req, res) => {
|
|
167
|
+
const { outcome, reward } = req.body;
|
|
168
|
+
if (!outcome || reward === undefined) {
|
|
169
|
+
return res.status(400).json({ error: 'outcome and reward are required' });
|
|
170
|
+
}
|
|
171
|
+
try {
|
|
172
|
+
res.json(learning.feedback(req.params.decisionId, outcome, reward));
|
|
173
|
+
} catch (err) {
|
|
174
|
+
res.status(404).json({ error: err.message });
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
// Get recommendation
|
|
179
|
+
router.post('/learning/recommend', (req, res) => {
|
|
180
|
+
const { siteId, agentId, domain, actions, context } = req.body;
|
|
181
|
+
if (!siteId || !agentId || !domain || !actions || !Array.isArray(actions)) {
|
|
182
|
+
return res.status(400).json({ error: 'siteId, agentId, domain, and actions (array) are required' });
|
|
183
|
+
}
|
|
184
|
+
res.json(learning.recommend(siteId, agentId, domain, actions, context));
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
// Get preferences
|
|
188
|
+
router.get('/learning/preferences/:siteId/:agentId/:domain', (req, res) => {
|
|
189
|
+
res.json(learning.getPreferences(req.params.siteId, req.params.agentId, req.params.domain));
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
// Start learning session
|
|
193
|
+
router.post('/learning/sessions', (req, res) => {
|
|
194
|
+
const { siteId, agentId } = req.body;
|
|
195
|
+
if (!siteId || !agentId) return res.status(400).json({ error: 'siteId and agentId are required' });
|
|
196
|
+
res.json(learning.startSession(siteId, agentId));
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
// End learning session
|
|
200
|
+
router.post('/learning/sessions/:sessionId/end', (req, res) => {
|
|
201
|
+
const { decisionsMade, correctPredictions } = req.body;
|
|
202
|
+
res.json(learning.endSession(req.params.sessionId, decisionsMade || 0, correctPredictions || 0));
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
// Learning stats
|
|
206
|
+
router.get('/learning/stats/:siteId/:agentId', (req, res) => {
|
|
207
|
+
res.json(learning.getStats(req.params.siteId, req.params.agentId));
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
211
|
+
// AGENT SYMPHONY ORCHESTRATOR
|
|
212
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
213
|
+
|
|
214
|
+
// Perform a full symphony (end-to-end)
|
|
215
|
+
router.post('/symphony/perform', (req, res) => {
|
|
216
|
+
const { siteId, task, taskType, inputData, agentIds } = req.body;
|
|
217
|
+
if (!siteId || !task || !taskType) {
|
|
218
|
+
return res.status(400).json({ error: 'siteId, task, and taskType are required' });
|
|
219
|
+
}
|
|
220
|
+
try {
|
|
221
|
+
const result = symphony.perform(siteId, task, taskType, inputData, agentIds);
|
|
222
|
+
res.json(result);
|
|
223
|
+
} catch (err) {
|
|
224
|
+
res.status(400).json({ error: err.message });
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
// Compose a symphony (step-by-step)
|
|
229
|
+
router.post('/symphony/compose', (req, res) => {
|
|
230
|
+
const { siteId, task, taskType, agentIds } = req.body;
|
|
231
|
+
if (!siteId || !task || !taskType) {
|
|
232
|
+
return res.status(400).json({ error: 'siteId, task, and taskType are required' });
|
|
233
|
+
}
|
|
234
|
+
try {
|
|
235
|
+
res.json(symphony.compose(siteId, task, taskType, agentIds));
|
|
236
|
+
} catch (err) {
|
|
237
|
+
res.status(400).json({ error: err.message });
|
|
238
|
+
}
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
// Execute a single phase
|
|
242
|
+
router.post('/symphony/:compositionId/phase', (req, res) => {
|
|
243
|
+
const { phase, input } = req.body;
|
|
244
|
+
if (!phase) return res.status(400).json({ error: 'phase is required' });
|
|
245
|
+
try {
|
|
246
|
+
res.json(symphony.executePhase(req.params.compositionId, phase, input));
|
|
247
|
+
} catch (err) {
|
|
248
|
+
res.status(400).json({ error: err.message });
|
|
249
|
+
}
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
// Get composition details
|
|
253
|
+
router.get('/symphony/:compositionId', (req, res) => {
|
|
254
|
+
const result = symphony.getComposition(req.params.compositionId);
|
|
255
|
+
if (!result) return res.status(404).json({ error: 'Composition not found' });
|
|
256
|
+
res.json(result);
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
// Get compositions for site
|
|
260
|
+
router.get('/symphony/site/:siteId', (req, res) => {
|
|
261
|
+
const limit = Math.min(parseInt(req.query.limit) || 20, 100);
|
|
262
|
+
res.json(symphony.getCompositions(req.params.siteId, limit));
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
// Get templates
|
|
266
|
+
router.get('/symphony/templates/all', (req, res) => {
|
|
267
|
+
res.json(symphony.getTemplates());
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
// Symphony stats
|
|
271
|
+
router.get('/symphony/stats/:siteId', (req, res) => {
|
|
272
|
+
res.json(symphony.getStats(req.params.siteId));
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
276
|
+
// MESH STATISTICS
|
|
277
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
278
|
+
|
|
279
|
+
// Overall mesh stats
|
|
280
|
+
router.get('/stats', (req, res) => {
|
|
281
|
+
const meshStats = mesh.getStats();
|
|
282
|
+
res.json(meshStats);
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
// Dashboard aggregate data
|
|
286
|
+
router.get('/dashboard', (req, res) => {
|
|
287
|
+
const meshStats = mesh.getStats();
|
|
288
|
+
const channels = mesh.getChannels();
|
|
289
|
+
const agents = mesh.getActiveAgents();
|
|
290
|
+
const templates = symphony.getTemplates();
|
|
291
|
+
|
|
292
|
+
res.json({
|
|
293
|
+
mesh: meshStats,
|
|
294
|
+
channels,
|
|
295
|
+
agents,
|
|
296
|
+
symphonyTemplates: templates,
|
|
297
|
+
});
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
module.exports = router;
|