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.
Files changed (35) hide show
  1. package/package.json +12 -4
  2. package/public/commander-dashboard.html +243 -0
  3. package/public/css/premium.css +317 -317
  4. package/public/demo.html +259 -259
  5. package/public/index.html +644 -644
  6. package/public/mesh-dashboard.html +309 -382
  7. package/public/premium-dashboard.html +2487 -2487
  8. package/public/premium.html +791 -791
  9. package/public/script/wab.min.js +124 -87
  10. package/script/ai-agent-bridge.js +154 -84
  11. package/sdk/agent-mesh.js +287 -171
  12. package/sdk/commander.js +262 -0
  13. package/sdk/index.js +260 -260
  14. package/server/index.js +8 -1
  15. package/server/migrations/002_premium_features.sql +418 -418
  16. package/server/models/db.js +24 -5
  17. package/server/routes/admin-premium.js +671 -671
  18. package/server/routes/commander.js +316 -0
  19. package/server/routes/mesh.js +370 -201
  20. package/server/routes/premium-v2.js +686 -686
  21. package/server/routes/premium.js +724 -724
  22. package/server/services/agent-learning.js +230 -77
  23. package/server/services/agent-memory.js +625 -625
  24. package/server/services/agent-mesh.js +260 -67
  25. package/server/services/agent-symphony.js +548 -518
  26. package/server/services/commander.js +738 -0
  27. package/server/services/edge-compute.js +440 -0
  28. package/server/services/local-ai.js +389 -0
  29. package/server/services/plugins.js +747 -747
  30. package/server/services/self-healing.js +843 -843
  31. package/server/services/swarm.js +788 -788
  32. package/server/services/vision.js +871 -871
  33. package/public/admin/dashboard.html +0 -848
  34. package/public/admin/login.html +0 -84
  35. package/public/video/tutorial.mp4 +0 -0
@@ -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.resolve({ error: 'Mesh requires a server URL' });
369
+ if (!this.serverUrl) return Promise.reject(new Error('Mesh requires a server URL'));
363
370
  var self = this;
364
- return fetch(this.serverUrl + '/api/mesh/agents', {
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(r) { return r.json(); }).then(function(data) {
369
- self._meshAgentId = data.id;
370
- self._emit('mesh:joined', data);
371
- return data;
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.resolve({ error: 'Must join mesh first' });
377
- return fetch(this.serverUrl + '/api/mesh/channels/' + encodeURIComponent(channel) + '/messages', {
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(Object.assign({ senderId: this._meshAgentId, messageType: messageType, subject: subject, payload: payload }, opts || {}))
381
- }).then(function(r) { return r.json(); });
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(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(); });
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(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', {
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, knowledgeType: knowledgeType, domain: domain, key: key, value: value, confidence: confidence })
396
- }).then(function(r) { return r.json(); });
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.resolve({ error: 'Must join mesh first' });
401
- return fetch(this.serverUrl + '/api/mesh/alerts', {
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(r) { return r.json(); });
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(task, taskType, inputData) {
411
- if (!this.serverUrl) return Promise.resolve({ error: 'Symphony requires a server URL' });
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 fetch(this.serverUrl + '/api/mesh/symphony/perform', {
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, task: task, taskType: taskType, inputData: inputData })
417
- }).then(function(r) { return r.json(); }).then(function(data) {
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.resolve({ error: 'Must join mesh first' });
427
- return fetch(this.serverUrl + '/api/mesh/learning/decisions', {
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
- }).then(function(r) { return r.json(); });
496
+ });
432
497
  };
433
498
 
434
499
  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', {
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
- }).then(function(r) { return r.json(); });
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.resolve({ error: 'Must join mesh first' });
445
- return fetch(this.serverUrl + '/api/mesh/learning/recommend', {
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
- }).then(function(r) { return r.json(); });
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.resolve({ error: 'WAB not initialized. Call WAB.init() first.' });
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.resolve({ error: 'WAB not initialized. Call WAB.init() first.' });
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.resolve({ error: 'WAB not initialized' });
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.resolve({ error: 'WAB not initialized' });
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.resolve({ error: 'WAB not initialized' });
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
- 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
- },
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/agents`, {
1433
+ const res = await fetch(`${base}/api/mesh${path}`, {
1437
1434
  method: 'POST',
1438
1435
  headers: { 'Content-Type': 'application/json' },
1439
- body: JSON.stringify({ siteId: this.config.siteId, role, displayName, capabilities })
1436
+ body: body ? JSON.stringify(body) : undefined
1440
1437
  });
1441
- const data = await res.json();
1442
- this._meshAgentId = data.id;
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/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
- });
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/agents/${this._meshAgentId}/messages/${encodeURIComponent(channel)}?limit=${limit || 20}`);
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
- * Share knowledge to the mesh.
1473
- */
1474
- async meshShareKnowledge(knowledgeType, domain, key, value, confidence) {
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
- 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();
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
- 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();
1516
+ return (await this._meshPost('/alert', { senderId: this._meshAgentId, subject, details, priority })).message;
1497
1517
  }
1498
1518
 
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 })
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
- 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 })
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
- 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();
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
- 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 })
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;