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.
@@ -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;