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.
- package/package.json +12 -4
- package/public/commander-dashboard.html +243 -0
- package/public/css/premium.css +317 -317
- package/public/demo.html +259 -259
- package/public/index.html +644 -644
- package/public/mesh-dashboard.html +309 -382
- package/public/premium-dashboard.html +2487 -2487
- package/public/premium.html +791 -791
- package/public/script/wab.min.js +124 -87
- package/script/ai-agent-bridge.js +154 -84
- package/sdk/agent-mesh.js +287 -171
- package/sdk/commander.js +262 -0
- package/sdk/index.js +260 -260
- package/server/index.js +8 -1
- package/server/migrations/002_premium_features.sql +418 -418
- package/server/models/db.js +24 -5
- package/server/routes/admin-premium.js +671 -671
- package/server/routes/commander.js +316 -0
- package/server/routes/mesh.js +370 -201
- package/server/routes/premium-v2.js +686 -686
- package/server/routes/premium.js +724 -724
- package/server/services/agent-learning.js +230 -77
- package/server/services/agent-memory.js +625 -625
- package/server/services/agent-mesh.js +260 -67
- package/server/services/agent-symphony.js +548 -518
- package/server/services/commander.js +738 -0
- package/server/services/edge-compute.js +440 -0
- package/server/services/local-ai.js +389 -0
- package/server/services/plugins.js +747 -747
- package/server/services/self-healing.js +843 -843
- package/server/services/swarm.js +788 -788
- package/server/services/vision.js +871 -871
- package/public/admin/dashboard.html +0 -848
- package/public/admin/login.html +0 -84
- package/public/video/tutorial.mp4 +0 -0
package/public/script/wab.min.js
CHANGED
|
@@ -358,63 +358,128 @@
|
|
|
358
358
|
|
|
359
359
|
// ── Agent Mesh Protocol ───────────────────────────────────────────
|
|
360
360
|
|
|
361
|
+
function _meshReq(url, opts) {
|
|
362
|
+
return fetch(url, opts).then(function(r) {
|
|
363
|
+
if (!r.ok) return r.json().catch(function() { return {}; }).then(function(e) { return Promise.reject(new Error(e.error || r.statusText)); });
|
|
364
|
+
return r.json();
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
|
|
361
368
|
WABInstance.prototype.meshJoin = function(role, displayName, capabilities) {
|
|
362
|
-
if (!this.serverUrl) return Promise.
|
|
369
|
+
if (!this.serverUrl) return Promise.reject(new Error('Mesh requires a server URL'));
|
|
363
370
|
var self = this;
|
|
364
|
-
return
|
|
371
|
+
return _meshReq(this.serverUrl + '/api/mesh/agents', {
|
|
365
372
|
method: 'POST',
|
|
366
373
|
headers: { 'Content-Type': 'application/json' },
|
|
367
374
|
body: JSON.stringify({ siteId: this.name, role: role, displayName: displayName, capabilities: capabilities })
|
|
368
|
-
}).then(function(
|
|
369
|
-
self._meshAgentId = data.id;
|
|
370
|
-
self.
|
|
371
|
-
|
|
375
|
+
}).then(function(data) {
|
|
376
|
+
self._meshAgentId = data.agent.id;
|
|
377
|
+
self._meshHeartbeat = setInterval(function() {
|
|
378
|
+
_meshReq(self.serverUrl + '/api/mesh/agents/' + self._meshAgentId + '/heartbeat', { method: 'POST' }).catch(function() {});
|
|
379
|
+
}, 30000);
|
|
380
|
+
self._emit('mesh:joined', data.agent);
|
|
381
|
+
return data.agent;
|
|
372
382
|
});
|
|
373
383
|
};
|
|
374
384
|
|
|
385
|
+
WABInstance.prototype.meshLeave = function() {
|
|
386
|
+
if (this._meshHeartbeat) { clearInterval(this._meshHeartbeat); this._meshHeartbeat = null; }
|
|
387
|
+
if (!this.serverUrl || !this._meshAgentId) return Promise.resolve();
|
|
388
|
+
var self = this;
|
|
389
|
+
return _meshReq(this.serverUrl + '/api/mesh/agents/' + this._meshAgentId, { method: 'DELETE' })
|
|
390
|
+
.catch(function() {}).then(function() { self._meshAgentId = null; });
|
|
391
|
+
};
|
|
392
|
+
|
|
375
393
|
WABInstance.prototype.meshPublish = function(channel, messageType, subject, payload, opts) {
|
|
376
|
-
if (!this.serverUrl || !this._meshAgentId) return Promise.
|
|
377
|
-
return
|
|
394
|
+
if (!this.serverUrl || !this._meshAgentId) return Promise.reject(new Error('Must join mesh first'));
|
|
395
|
+
return _meshReq(this.serverUrl + '/api/mesh/messages', {
|
|
378
396
|
method: 'POST',
|
|
379
397
|
headers: { 'Content-Type': 'application/json' },
|
|
380
|
-
body: JSON.stringify(
|
|
381
|
-
}).then(function(
|
|
398
|
+
body: JSON.stringify({ channelName: channel || 'general', senderId: this._meshAgentId, targetId: (opts || {}).targetId, type: messageType, subject: subject, payload: payload, priority: (opts || {}).priority, ttl: (opts || {}).ttl })
|
|
399
|
+
}).then(function(d) { return d.message; });
|
|
382
400
|
};
|
|
383
401
|
|
|
384
|
-
WABInstance.prototype.meshReceive = function(
|
|
385
|
-
if (!this.serverUrl || !this._meshAgentId) return Promise.
|
|
386
|
-
return
|
|
387
|
-
.then(function(
|
|
402
|
+
WABInstance.prototype.meshReceive = function(limit) {
|
|
403
|
+
if (!this.serverUrl || !this._meshAgentId) return Promise.reject(new Error('Must join mesh first'));
|
|
404
|
+
return _meshReq(this.serverUrl + '/api/mesh/messages?agentId=' + this._meshAgentId + '&limit=' + (limit || 50))
|
|
405
|
+
.then(function(d) { return d.messages; });
|
|
406
|
+
};
|
|
407
|
+
|
|
408
|
+
WABInstance.prototype.meshAcknowledge = function(messageId) {
|
|
409
|
+
if (!this.serverUrl) return Promise.reject(new Error('Requires server URL'));
|
|
410
|
+
return _meshReq(this.serverUrl + '/api/mesh/messages/' + encodeURIComponent(messageId) + '/acknowledge', { method: 'POST' });
|
|
411
|
+
};
|
|
412
|
+
|
|
413
|
+
WABInstance.prototype.meshUnread = function() {
|
|
414
|
+
if (!this.serverUrl || !this._meshAgentId) return Promise.reject(new Error('Must join mesh first'));
|
|
415
|
+
return _meshReq(this.serverUrl + '/api/mesh/agents/' + this._meshAgentId + '/unread');
|
|
388
416
|
};
|
|
389
417
|
|
|
390
|
-
WABInstance.prototype.meshShareKnowledge = function(
|
|
391
|
-
if (!this.serverUrl || !this._meshAgentId) return Promise.
|
|
392
|
-
|
|
418
|
+
WABInstance.prototype.meshShareKnowledge = function(type, key, value, opts) {
|
|
419
|
+
if (!this.serverUrl || !this._meshAgentId) return Promise.reject(new Error('Must join mesh first'));
|
|
420
|
+
opts = opts || {};
|
|
421
|
+
return _meshReq(this.serverUrl + '/api/mesh/knowledge', {
|
|
393
422
|
method: 'POST',
|
|
394
423
|
headers: { 'Content-Type': 'application/json' },
|
|
395
|
-
body: JSON.stringify({ agentId: this._meshAgentId,
|
|
396
|
-
}).then(function(
|
|
424
|
+
body: JSON.stringify({ agentId: this._meshAgentId, type: type, domain: opts.domain, key: key, value: value, confidence: opts.confidence, source: opts.source })
|
|
425
|
+
}).then(function(d) { return d.knowledge; });
|
|
426
|
+
};
|
|
427
|
+
|
|
428
|
+
WABInstance.prototype.meshQueryKnowledge = function(params) {
|
|
429
|
+
if (!this.serverUrl) return Promise.reject(new Error('Requires server URL'));
|
|
430
|
+
var qs = Object.keys(params || {}).map(function(k) { return k + '=' + encodeURIComponent(params[k]); }).join('&');
|
|
431
|
+
return _meshReq(this.serverUrl + '/api/mesh/knowledge?' + qs).then(function(d) { return d.knowledge; });
|
|
432
|
+
};
|
|
433
|
+
|
|
434
|
+
WABInstance.prototype.meshSearchKnowledge = function(query, limit) {
|
|
435
|
+
if (!this.serverUrl) return Promise.reject(new Error('Requires server URL'));
|
|
436
|
+
return _meshReq(this.serverUrl + '/api/mesh/knowledge/search/' + encodeURIComponent(query) + '?limit=' + (limit || 20))
|
|
437
|
+
.then(function(d) { return d.knowledge; });
|
|
397
438
|
};
|
|
398
439
|
|
|
399
440
|
WABInstance.prototype.meshAlert = function(subject, details, priority) {
|
|
400
|
-
if (!this.serverUrl || !this._meshAgentId) return Promise.
|
|
401
|
-
return
|
|
441
|
+
if (!this.serverUrl || !this._meshAgentId) return Promise.reject(new Error('Must join mesh first'));
|
|
442
|
+
return _meshReq(this.serverUrl + '/api/mesh/alert', {
|
|
402
443
|
method: 'POST',
|
|
403
444
|
headers: { 'Content-Type': 'application/json' },
|
|
404
445
|
body: JSON.stringify({ senderId: this._meshAgentId, subject: subject, details: details, priority: priority })
|
|
405
|
-
}).then(function(
|
|
446
|
+
}).then(function(d) { return d.message; });
|
|
447
|
+
};
|
|
448
|
+
|
|
449
|
+
WABInstance.prototype.meshCreateVote = function(subject, options, deadlineSeconds) {
|
|
450
|
+
if (!this.serverUrl || !this._meshAgentId) return Promise.reject(new Error('Must join mesh first'));
|
|
451
|
+
return _meshReq(this.serverUrl + '/api/mesh/votes', {
|
|
452
|
+
method: 'POST',
|
|
453
|
+
headers: { 'Content-Type': 'application/json' },
|
|
454
|
+
body: JSON.stringify({ senderId: this._meshAgentId, subject: subject, options: options, deadlineSeconds: deadlineSeconds })
|
|
455
|
+
}).then(function(d) { return d.vote; });
|
|
456
|
+
};
|
|
457
|
+
|
|
458
|
+
WABInstance.prototype.meshCastVote = function(voteMessageId, choice, weight, reason) {
|
|
459
|
+
if (!this.serverUrl || !this._meshAgentId) return Promise.reject(new Error('Must join mesh first'));
|
|
460
|
+
return _meshReq(this.serverUrl + '/api/mesh/votes/' + encodeURIComponent(voteMessageId) + '/cast', {
|
|
461
|
+
method: 'POST',
|
|
462
|
+
headers: { 'Content-Type': 'application/json' },
|
|
463
|
+
body: JSON.stringify({ voterId: this._meshAgentId, choice: choice, weight: weight, reason: reason })
|
|
464
|
+
}).then(function(d) { return d.result; });
|
|
465
|
+
};
|
|
466
|
+
|
|
467
|
+
WABInstance.prototype.meshTallyVote = function(voteMessageId) {
|
|
468
|
+
if (!this.serverUrl) return Promise.reject(new Error('Requires server URL'));
|
|
469
|
+
return _meshReq(this.serverUrl + '/api/mesh/votes/' + encodeURIComponent(voteMessageId) + '/tally')
|
|
470
|
+
.then(function(d) { return d.tally; });
|
|
406
471
|
};
|
|
407
472
|
|
|
408
473
|
// ── Agent Symphony Orchestrator ───────────────────────────────────
|
|
409
474
|
|
|
410
|
-
WABInstance.prototype.symphonyPerform = function(
|
|
411
|
-
if (!this.serverUrl) return Promise.
|
|
475
|
+
WABInstance.prototype.symphonyPerform = function(template, inputData, schema) {
|
|
476
|
+
if (!this.serverUrl) return Promise.reject(new Error('Symphony requires a server URL'));
|
|
412
477
|
var self = this;
|
|
413
|
-
return
|
|
478
|
+
return _meshReq(this.serverUrl + '/api/mesh/symphony/compose', {
|
|
414
479
|
method: 'POST',
|
|
415
480
|
headers: { 'Content-Type': 'application/json' },
|
|
416
|
-
body: JSON.stringify({ siteId: this.name,
|
|
417
|
-
}).then(function(
|
|
481
|
+
body: JSON.stringify({ siteId: this.name, template: template, inputData: inputData, schema: schema })
|
|
482
|
+
}).then(function(data) {
|
|
418
483
|
self._emit('symphony:completed', data);
|
|
419
484
|
return data;
|
|
420
485
|
});
|
|
@@ -423,30 +488,30 @@
|
|
|
423
488
|
// ── Agent Learning Engine ─────────────────────────────────────────
|
|
424
489
|
|
|
425
490
|
WABInstance.prototype.learnRecord = function(domain, action, context, features) {
|
|
426
|
-
if (!this.serverUrl || !this._meshAgentId) return Promise.
|
|
427
|
-
return
|
|
491
|
+
if (!this.serverUrl || !this._meshAgentId) return Promise.reject(new Error('Must join mesh first'));
|
|
492
|
+
return _meshReq(this.serverUrl + '/api/mesh/learning/decisions', {
|
|
428
493
|
method: 'POST',
|
|
429
494
|
headers: { 'Content-Type': 'application/json' },
|
|
430
495
|
body: JSON.stringify({ siteId: this.name, agentId: this._meshAgentId, domain: domain, action: action, context: context, features: features })
|
|
431
|
-
})
|
|
496
|
+
});
|
|
432
497
|
};
|
|
433
498
|
|
|
434
499
|
WABInstance.prototype.learnFeedback = function(decisionId, outcome, reward) {
|
|
435
|
-
if (!this.serverUrl) return Promise.
|
|
436
|
-
return
|
|
500
|
+
if (!this.serverUrl) return Promise.reject(new Error('Learning requires a server URL'));
|
|
501
|
+
return _meshReq(this.serverUrl + '/api/mesh/learning/feedback', {
|
|
437
502
|
method: 'POST',
|
|
438
503
|
headers: { 'Content-Type': 'application/json' },
|
|
439
|
-
body: JSON.stringify({ outcome: outcome, reward: reward })
|
|
440
|
-
})
|
|
504
|
+
body: JSON.stringify({ decisionId: decisionId, outcome: outcome, reward: reward })
|
|
505
|
+
});
|
|
441
506
|
};
|
|
442
507
|
|
|
443
508
|
WABInstance.prototype.learnRecommend = function(domain, actions, context) {
|
|
444
|
-
if (!this.serverUrl || !this._meshAgentId) return Promise.
|
|
445
|
-
return
|
|
509
|
+
if (!this.serverUrl || !this._meshAgentId) return Promise.reject(new Error('Must join mesh first'));
|
|
510
|
+
return _meshReq(this.serverUrl + '/api/mesh/learning/recommend', {
|
|
446
511
|
method: 'POST',
|
|
447
512
|
headers: { 'Content-Type': 'application/json' },
|
|
448
513
|
body: JSON.stringify({ siteId: this.name, agentId: this._meshAgentId, domain: domain, actions: actions, context: context })
|
|
449
|
-
})
|
|
514
|
+
});
|
|
450
515
|
};
|
|
451
516
|
|
|
452
517
|
// ── Static API ────────────────────────────────────────────────────
|
|
@@ -460,80 +525,52 @@
|
|
|
460
525
|
return instance;
|
|
461
526
|
},
|
|
462
527
|
|
|
463
|
-
// Connect to a remote WAB server (for client pages that don't define actions)
|
|
464
528
|
connect: function(serverUrl) {
|
|
465
529
|
return new WABInstance({ name: 'WAB Client', serverUrl: serverUrl });
|
|
466
530
|
},
|
|
467
531
|
|
|
468
532
|
discover: function() {
|
|
469
533
|
if (WAB._instance) return WAB._instance.discover();
|
|
470
|
-
return Promise.
|
|
534
|
+
return Promise.reject(new Error('WAB not initialized. Call WAB.init() first.'));
|
|
471
535
|
},
|
|
472
536
|
|
|
473
537
|
execute: function(actionName, params) {
|
|
474
538
|
if (WAB._instance) return WAB._instance.execute(actionName, params);
|
|
475
|
-
return Promise.
|
|
539
|
+
return Promise.reject(new Error('WAB not initialized. Call WAB.init() first.'));
|
|
476
540
|
},
|
|
477
541
|
|
|
478
542
|
negotiate: function(agentId, proposal) {
|
|
479
543
|
if (WAB._instance) return WAB._instance.negotiate(agentId, proposal);
|
|
480
|
-
return Promise.
|
|
544
|
+
return Promise.reject(new Error('WAB not initialized'));
|
|
481
545
|
},
|
|
482
546
|
|
|
483
547
|
getReputation: function(siteId) {
|
|
484
548
|
if (WAB._instance) return WAB._instance.getReputation(siteId);
|
|
485
|
-
return Promise.
|
|
549
|
+
return Promise.reject(new Error('WAB not initialized'));
|
|
486
550
|
},
|
|
487
551
|
|
|
488
552
|
verifyPrice: function(opts) {
|
|
489
553
|
if (WAB._instance) return WAB._instance.verifyPrice(opts);
|
|
490
|
-
return Promise.
|
|
491
|
-
},
|
|
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' });
|
|
554
|
+
return Promise.reject(new Error('WAB not initialized'));
|
|
501
555
|
},
|
|
502
556
|
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
},
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
},
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
},
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
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
|
-
},
|
|
557
|
+
meshJoin: function(r, d, c) { return WAB._instance ? WAB._instance.meshJoin(r, d, c) : Promise.reject(new Error('WAB not initialized')); },
|
|
558
|
+
meshLeave: function() { return WAB._instance ? WAB._instance.meshLeave() : Promise.reject(new Error('WAB not initialized')); },
|
|
559
|
+
meshPublish: function(ch, mt, s, p, o) { return WAB._instance ? WAB._instance.meshPublish(ch, mt, s, p, o) : Promise.reject(new Error('WAB not initialized')); },
|
|
560
|
+
meshReceive: function(l) { return WAB._instance ? WAB._instance.meshReceive(l) : Promise.reject(new Error('WAB not initialized')); },
|
|
561
|
+
meshAcknowledge: function(id) { return WAB._instance ? WAB._instance.meshAcknowledge(id) : Promise.reject(new Error('WAB not initialized')); },
|
|
562
|
+
meshUnread: function() { return WAB._instance ? WAB._instance.meshUnread() : Promise.reject(new Error('WAB not initialized')); },
|
|
563
|
+
meshShareKnowledge: function(t, k, v, o) { return WAB._instance ? WAB._instance.meshShareKnowledge(t, k, v, o) : Promise.reject(new Error('WAB not initialized')); },
|
|
564
|
+
meshQueryKnowledge: function(p) { return WAB._instance ? WAB._instance.meshQueryKnowledge(p) : Promise.reject(new Error('WAB not initialized')); },
|
|
565
|
+
meshSearchKnowledge: function(q, l) { return WAB._instance ? WAB._instance.meshSearchKnowledge(q, l) : Promise.reject(new Error('WAB not initialized')); },
|
|
566
|
+
meshAlert: function(s, d, p) { return WAB._instance ? WAB._instance.meshAlert(s, d, p) : Promise.reject(new Error('WAB not initialized')); },
|
|
567
|
+
meshCreateVote: function(s, o, d) { return WAB._instance ? WAB._instance.meshCreateVote(s, o, d) : Promise.reject(new Error('WAB not initialized')); },
|
|
568
|
+
meshCastVote: function(id, c, w, r) { return WAB._instance ? WAB._instance.meshCastVote(id, c, w, r) : Promise.reject(new Error('WAB not initialized')); },
|
|
569
|
+
meshTallyVote: function(id) { return WAB._instance ? WAB._instance.meshTallyVote(id) : Promise.reject(new Error('WAB not initialized')); },
|
|
570
|
+
symphonyPerform: function(t, i, s) { return WAB._instance ? WAB._instance.symphonyPerform(t, i, s) : Promise.reject(new Error('WAB not initialized')); },
|
|
571
|
+
learnRecord: function(d, a, c, f) { return WAB._instance ? WAB._instance.learnRecord(d, a, c, f) : Promise.reject(new Error('WAB not initialized')); },
|
|
572
|
+
learnFeedback: function(id, o, r) { return WAB._instance ? WAB._instance.learnFeedback(id, o, r) : Promise.reject(new Error('WAB not initialized')); },
|
|
573
|
+
learnRecommend: function(d, a, c) { return WAB._instance ? WAB._instance.learnRecommend(d, a, c) : Promise.reject(new Error('WAB not initialized')); },
|
|
537
574
|
|
|
538
575
|
_instance: null
|
|
539
576
|
};
|
|
@@ -1428,132 +1428,202 @@
|
|
|
1428
1428
|
|
|
1429
1429
|
// ── Agent Mesh Protocol (Client-Side) ───────────────────────────────
|
|
1430
1430
|
|
|
1431
|
-
|
|
1432
|
-
* Join the agent mesh — register this agent and return its mesh identity.
|
|
1433
|
-
*/
|
|
1434
|
-
async meshJoin(role, displayName, capabilities) {
|
|
1431
|
+
async _meshPost(path, body) {
|
|
1435
1432
|
const base = this._resolveApiBase();
|
|
1436
|
-
const res = await fetch(`${base}/api/mesh
|
|
1433
|
+
const res = await fetch(`${base}/api/mesh${path}`, {
|
|
1437
1434
|
method: 'POST',
|
|
1438
1435
|
headers: { 'Content-Type': 'application/json' },
|
|
1439
|
-
body: JSON.stringify(
|
|
1436
|
+
body: body ? JSON.stringify(body) : undefined
|
|
1440
1437
|
});
|
|
1441
|
-
const
|
|
1442
|
-
|
|
1443
|
-
this.events.emit('mesh:joined', data);
|
|
1444
|
-
return data;
|
|
1438
|
+
if (!res.ok) { const e = await res.json().catch(() => ({})); throw new Error(e.error || res.statusText); }
|
|
1439
|
+
return res.json();
|
|
1445
1440
|
}
|
|
1446
1441
|
|
|
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');
|
|
1442
|
+
async _meshGet(path) {
|
|
1452
1443
|
const base = this._resolveApiBase();
|
|
1453
|
-
const res = await fetch(`${base}/api/mesh
|
|
1454
|
-
|
|
1455
|
-
headers: { 'Content-Type': 'application/json' },
|
|
1456
|
-
body: JSON.stringify({ senderId: this._meshAgentId, messageType, subject, payload, ...opts })
|
|
1457
|
-
});
|
|
1444
|
+
const res = await fetch(`${base}/api/mesh${path}`);
|
|
1445
|
+
if (!res.ok) { const e = await res.json().catch(() => ({})); throw new Error(e.error || res.statusText); }
|
|
1458
1446
|
return res.json();
|
|
1459
1447
|
}
|
|
1460
1448
|
|
|
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');
|
|
1449
|
+
async _meshDelete(path) {
|
|
1466
1450
|
const base = this._resolveApiBase();
|
|
1467
|
-
const res = await fetch(`${base}/api/mesh
|
|
1451
|
+
const res = await fetch(`${base}/api/mesh${path}`, { method: 'DELETE' });
|
|
1452
|
+
if (!res.ok) { const e = await res.json().catch(() => ({})); throw new Error(e.error || res.statusText); }
|
|
1468
1453
|
return res.json();
|
|
1469
1454
|
}
|
|
1470
1455
|
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1456
|
+
async meshJoin(role, displayName, capabilities) {
|
|
1457
|
+
const data = await this._meshPost('/agents', { siteId: this.config.siteId, role, displayName, capabilities });
|
|
1458
|
+
this._meshAgentId = data.agent.id;
|
|
1459
|
+
this._meshHeartbeat = setInterval(() => {
|
|
1460
|
+
this._meshPost(`/agents/${this._meshAgentId}/heartbeat`).catch(() => {});
|
|
1461
|
+
}, 30000);
|
|
1462
|
+
this.events.emit('mesh:joined', data.agent);
|
|
1463
|
+
return data.agent;
|
|
1464
|
+
}
|
|
1465
|
+
|
|
1466
|
+
async meshLeave() {
|
|
1467
|
+
if (this._meshHeartbeat) { clearInterval(this._meshHeartbeat); this._meshHeartbeat = null; }
|
|
1468
|
+
if (this._meshAgentId) {
|
|
1469
|
+
await this._meshDelete(`/agents/${this._meshAgentId}`).catch(() => {});
|
|
1470
|
+
this._meshAgentId = null;
|
|
1471
|
+
}
|
|
1472
|
+
}
|
|
1473
|
+
|
|
1474
|
+
async meshPublish(channel, messageType, subject, payload, opts) {
|
|
1475
1475
|
if (!this._meshAgentId) throw new Error('Must call meshJoin() first');
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1476
|
+
return (await this._meshPost('/messages', {
|
|
1477
|
+
channelName: channel || 'general', senderId: this._meshAgentId,
|
|
1478
|
+
targetId: opts?.targetId, type: messageType, subject, payload,
|
|
1479
|
+
priority: opts?.priority, ttl: opts?.ttl
|
|
1480
|
+
})).message;
|
|
1481
|
+
}
|
|
1482
|
+
|
|
1483
|
+
async meshReceive(limit) {
|
|
1484
|
+
if (!this._meshAgentId) throw new Error('Must call meshJoin() first');
|
|
1485
|
+
return (await this._meshGet(`/messages?agentId=${this._meshAgentId}&limit=${limit || 50}`)).messages;
|
|
1486
|
+
}
|
|
1487
|
+
|
|
1488
|
+
async meshAcknowledge(messageId) {
|
|
1489
|
+
return this._meshPost(`/messages/${encodeURIComponent(messageId)}/acknowledge`);
|
|
1490
|
+
}
|
|
1491
|
+
|
|
1492
|
+
async meshUnread() {
|
|
1493
|
+
if (!this._meshAgentId) throw new Error('Must call meshJoin() first');
|
|
1494
|
+
return this._meshGet(`/agents/${this._meshAgentId}/unread`);
|
|
1495
|
+
}
|
|
1496
|
+
|
|
1497
|
+
async meshShareKnowledge(type, key, value, opts) {
|
|
1498
|
+
if (!this._meshAgentId) throw new Error('Must call meshJoin() first');
|
|
1499
|
+
return (await this._meshPost('/knowledge', {
|
|
1500
|
+
agentId: this._meshAgentId, type, domain: opts?.domain,
|
|
1501
|
+
key, value, confidence: opts?.confidence, source: opts?.source
|
|
1502
|
+
})).knowledge;
|
|
1503
|
+
}
|
|
1504
|
+
|
|
1505
|
+
async meshQueryKnowledge(params) {
|
|
1506
|
+
const qs = new URLSearchParams(params || {}).toString();
|
|
1507
|
+
return (await this._meshGet(`/knowledge?${qs}`)).knowledge;
|
|
1508
|
+
}
|
|
1509
|
+
|
|
1510
|
+
async meshSearchKnowledge(query, limit) {
|
|
1511
|
+
return (await this._meshGet(`/knowledge/search/${encodeURIComponent(query)}?limit=${limit || 20}`)).knowledge;
|
|
1483
1512
|
}
|
|
1484
1513
|
|
|
1485
|
-
/**
|
|
1486
|
-
* Broadcast an alert to all mesh agents.
|
|
1487
|
-
*/
|
|
1488
1514
|
async meshAlert(subject, details, priority) {
|
|
1489
1515
|
if (!this._meshAgentId) throw new Error('Must call meshJoin() first');
|
|
1490
|
-
|
|
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();
|
|
1516
|
+
return (await this._meshPost('/alert', { senderId: this._meshAgentId, subject, details, priority })).message;
|
|
1497
1517
|
}
|
|
1498
1518
|
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1519
|
+
async meshCreateVote(subject, options, deadlineSeconds) {
|
|
1520
|
+
if (!this._meshAgentId) throw new Error('Must call meshJoin() first');
|
|
1521
|
+
return (await this._meshPost('/votes', { senderId: this._meshAgentId, subject, options, deadlineSeconds })).vote;
|
|
1522
|
+
}
|
|
1523
|
+
|
|
1524
|
+
async meshCastVote(voteMessageId, choice, weight, reason) {
|
|
1525
|
+
if (!this._meshAgentId) throw new Error('Must call meshJoin() first');
|
|
1526
|
+
return (await this._meshPost(`/votes/${encodeURIComponent(voteMessageId)}/cast`, {
|
|
1527
|
+
voterId: this._meshAgentId, choice, weight, reason
|
|
1528
|
+
})).result;
|
|
1529
|
+
}
|
|
1530
|
+
|
|
1531
|
+
async meshTallyVote(voteMessageId) {
|
|
1532
|
+
return (await this._meshGet(`/votes/${encodeURIComponent(voteMessageId)}/tally`)).tally;
|
|
1533
|
+
}
|
|
1534
|
+
|
|
1535
|
+
async symphonyPerform(template, inputData, schema) {
|
|
1536
|
+
const data = await this._meshPost('/symphony/compose', {
|
|
1537
|
+
siteId: this.config.siteId, template, inputData, schema
|
|
1508
1538
|
});
|
|
1509
|
-
const data = await res.json();
|
|
1510
1539
|
this.events.emit('symphony:completed', data);
|
|
1511
1540
|
return data;
|
|
1512
1541
|
}
|
|
1513
1542
|
|
|
1514
|
-
/**
|
|
1515
|
-
* Record a decision for the learning engine and get prediction.
|
|
1516
|
-
*/
|
|
1517
1543
|
async learnRecord(domain, action, context, features) {
|
|
1518
1544
|
if (!this._meshAgentId) throw new Error('Must call meshJoin() first');
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
method: 'POST',
|
|
1522
|
-
headers: { 'Content-Type': 'application/json' },
|
|
1523
|
-
body: JSON.stringify({ siteId: this.config.siteId, agentId: this._meshAgentId, domain, action, context, features })
|
|
1545
|
+
return this._meshPost('/learning/decisions', {
|
|
1546
|
+
siteId: this.config.siteId, agentId: this._meshAgentId, domain, action, context, features
|
|
1524
1547
|
});
|
|
1525
|
-
return res.json();
|
|
1526
1548
|
}
|
|
1527
1549
|
|
|
1528
|
-
/**
|
|
1529
|
-
* Provide feedback on a decision — the learning signal.
|
|
1530
|
-
*/
|
|
1531
1550
|
async learnFeedback(decisionId, outcome, reward) {
|
|
1532
|
-
|
|
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();
|
|
1551
|
+
return this._meshPost('/learning/feedback', { decisionId, outcome, reward });
|
|
1539
1552
|
}
|
|
1540
1553
|
|
|
1541
|
-
/**
|
|
1542
|
-
* Get recommendation from learning engine.
|
|
1543
|
-
*/
|
|
1544
1554
|
async learnRecommend(domain, actions, context) {
|
|
1545
1555
|
if (!this._meshAgentId) throw new Error('Must call meshJoin() first');
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1556
|
+
return this._meshPost('/learning/recommend', {
|
|
1557
|
+
siteId: this.config.siteId, agentId: this._meshAgentId, domain, actions, context
|
|
1558
|
+
});
|
|
1559
|
+
}
|
|
1560
|
+
|
|
1561
|
+
// ── Commander Agent Protocol ────────────────────────────────────────
|
|
1562
|
+
|
|
1563
|
+
async _cmdPost(path, body) {
|
|
1564
|
+
const base = this.config.serverUrl || '';
|
|
1565
|
+
const res = await fetch(`${base}/api/commander${path}`, {
|
|
1566
|
+
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
|
1567
|
+
body: JSON.stringify(body)
|
|
1551
1568
|
});
|
|
1569
|
+
if (!res.ok) throw new Error(`Commander POST ${path} failed: ${res.status}`);
|
|
1552
1570
|
return res.json();
|
|
1553
1571
|
}
|
|
1554
1572
|
|
|
1573
|
+
async _cmdGet(path) {
|
|
1574
|
+
const base = this.config.serverUrl || '';
|
|
1575
|
+
const res = await fetch(`${base}/api/commander${path}`);
|
|
1576
|
+
if (!res.ok) throw new Error(`Commander GET ${path} failed: ${res.status}`);
|
|
1577
|
+
return res.json();
|
|
1578
|
+
}
|
|
1579
|
+
|
|
1580
|
+
/** Launch a mission — decompose a goal and execute it. */
|
|
1581
|
+
async commanderLaunch(goal, options) {
|
|
1582
|
+
const data = await this._cmdPost('/missions/launch', {
|
|
1583
|
+
siteId: this.config.siteId, goal,
|
|
1584
|
+
title: options?.title || goal.substring(0, 80),
|
|
1585
|
+
strategy: options?.strategy,
|
|
1586
|
+
priority: options?.priority, context: options?.context
|
|
1587
|
+
});
|
|
1588
|
+
this.events.emit('commander:mission', data.mission);
|
|
1589
|
+
return data.mission;
|
|
1590
|
+
}
|
|
1591
|
+
|
|
1592
|
+
/** Get commander + edge + local AI stats. */
|
|
1593
|
+
async commanderStats() {
|
|
1594
|
+
return this._cmdGet(`/stats?siteId=${encodeURIComponent(this.config.siteId || 'default')}`);
|
|
1595
|
+
}
|
|
1596
|
+
|
|
1597
|
+
/** Register an edge computing node. */
|
|
1598
|
+
async edgeRegisterNode(hostname, hardware, capabilities) {
|
|
1599
|
+
return this._cmdPost('/edge/nodes', {
|
|
1600
|
+
siteId: this.config.siteId, hostname, hardware, capabilities
|
|
1601
|
+
});
|
|
1602
|
+
}
|
|
1603
|
+
|
|
1604
|
+
/** Submit a task to the edge computing queue. */
|
|
1605
|
+
async edgeSubmitTask(taskType, payload, options) {
|
|
1606
|
+
return this._cmdPost('/edge/tasks', { taskType, payload, ...options });
|
|
1607
|
+
}
|
|
1608
|
+
|
|
1609
|
+
/** Discover local AI models (Ollama, llama.cpp, etc.). */
|
|
1610
|
+
async localAIDiscover(customEndpoints) {
|
|
1611
|
+
return this._cmdPost('/local-ai/discover', {
|
|
1612
|
+
siteId: this.config.siteId, customEndpoints
|
|
1613
|
+
});
|
|
1614
|
+
}
|
|
1615
|
+
|
|
1616
|
+
/** Run inference on a local AI model. */
|
|
1617
|
+
async localAIInfer(prompt, options) {
|
|
1618
|
+
return this._cmdPost('/local-ai/infer', {
|
|
1619
|
+
siteId: this.config.siteId, prompt, ...options
|
|
1620
|
+
});
|
|
1621
|
+
}
|
|
1622
|
+
|
|
1555
1623
|
destroy() {
|
|
1556
1624
|
this.events.emit('destroy');
|
|
1625
|
+
if (this._meshHeartbeat) { clearInterval(this._meshHeartbeat); this._meshHeartbeat = null; }
|
|
1626
|
+
this._meshAgentId = null;
|
|
1557
1627
|
if (this._mutationObserver) {
|
|
1558
1628
|
this._mutationObserver.disconnect();
|
|
1559
1629
|
this._mutationObserver = null;
|