web-agent-bridge 3.2.0 → 3.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/LICENSE +72 -72
- package/README.ar.md +1286 -1152
- package/README.md +1764 -1635
- package/bin/agent-runner.js +474 -474
- package/bin/cli.js +237 -138
- package/bin/wab.js +80 -80
- package/examples/bidi-agent.js +119 -119
- package/examples/cross-site-agent.js +91 -91
- package/examples/mcp-agent.js +94 -94
- package/examples/next-app-router/README.md +44 -44
- package/examples/puppeteer-agent.js +108 -108
- package/examples/saas-dashboard/README.md +55 -55
- package/examples/shopify-hydrogen/README.md +74 -74
- package/examples/vision-agent.js +171 -171
- package/examples/wordpress-elementor/README.md +77 -77
- package/package.json +16 -3
- package/public/.well-known/agent-tools.json +180 -180
- package/public/.well-known/ai-assets.json +59 -59
- package/public/.well-known/security.txt +8 -0
- package/public/agent-workspace.html +349 -349
- package/public/ai.html +198 -198
- package/public/api.html +413 -412
- package/public/browser.html +486 -486
- package/public/commander-dashboard.html +243 -243
- package/public/cookies.html +210 -210
- package/public/css/agent-workspace.css +1713 -1713
- package/public/css/premium.css +317 -317
- package/public/css/styles.css +1235 -1235
- package/public/dashboard.html +706 -706
- package/public/dns.html +507 -0
- package/public/docs.html +587 -587
- package/public/feed.xml +89 -89
- package/public/growth.html +463 -463
- package/public/index.html +1070 -982
- package/public/integrations.html +556 -0
- package/public/js/agent-workspace.js +1740 -1740
- package/public/js/auth-nav.js +31 -31
- package/public/js/auth-redirect.js +12 -12
- package/public/js/cookie-consent.js +56 -56
- package/public/js/wab-demo-page.js +721 -721
- package/public/js/ws-client.js +74 -74
- package/public/llms-full.txt +360 -360
- package/public/llms.txt +125 -125
- package/public/login.html +85 -85
- package/public/mesh-dashboard.html +328 -328
- package/public/openapi.json +580 -580
- package/public/phone-shield.html +281 -0
- package/public/premium-dashboard.html +2489 -2489
- package/public/premium.html +793 -793
- package/public/privacy.html +297 -297
- package/public/register.html +105 -105
- package/public/robots.txt +87 -87
- package/public/script/wab-consent.d.ts +36 -36
- package/public/script/wab-consent.js +104 -104
- package/public/script/wab-schema.js +131 -131
- package/public/script/wab.d.ts +108 -108
- package/public/script/wab.min.js +580 -580
- package/public/security.txt +8 -0
- package/public/terms.html +256 -256
- package/script/ai-agent-bridge.js +1754 -1754
- package/sdk/README.md +99 -99
- package/sdk/agent-mesh.js +449 -449
- package/sdk/commander.js +262 -262
- package/sdk/index.d.ts +464 -464
- package/sdk/index.js +12 -1
- package/sdk/multi-agent.js +318 -318
- package/sdk/package.json +1 -1
- package/sdk/safety-shield.js +219 -0
- package/sdk/schema-discovery.js +83 -83
- package/server/adapters/index.js +520 -520
- package/server/config/plans.js +367 -367
- package/server/config/secrets.js +102 -102
- package/server/control-plane/index.js +301 -301
- package/server/data-plane/index.js +354 -354
- package/server/index.js +531 -427
- package/server/llm/index.js +404 -404
- package/server/middleware/adminAuth.js +35 -35
- package/server/middleware/auth.js +50 -50
- package/server/middleware/featureGate.js +88 -88
- package/server/middleware/rateLimits.js +100 -100
- package/server/middleware/sensitiveAction.js +157 -0
- package/server/migrations/001_add_analytics_indexes.sql +7 -7
- package/server/migrations/002_premium_features.sql +418 -418
- package/server/migrations/003_ads_integer_cents.sql +33 -33
- package/server/migrations/004_agent_os.sql +158 -158
- package/server/migrations/005_marketplace_metering.sql +126 -126
- package/server/models/adapters/index.js +33 -33
- package/server/models/adapters/mysql.js +183 -183
- package/server/models/adapters/postgresql.js +172 -172
- package/server/models/adapters/sqlite.js +7 -7
- package/server/models/db.js +681 -681
- package/server/observability/failure-analysis.js +337 -337
- package/server/observability/index.js +394 -394
- package/server/protocol/capabilities.js +223 -223
- package/server/protocol/index.js +243 -243
- package/server/protocol/schema.js +584 -584
- package/server/registry/certification.js +271 -271
- package/server/registry/index.js +326 -326
- package/server/routes/admin-premium.js +671 -671
- package/server/routes/admin.js +261 -261
- package/server/routes/ads.js +130 -130
- package/server/routes/agent-workspace.js +540 -540
- package/server/routes/api.js +150 -150
- package/server/routes/auth.js +71 -71
- package/server/routes/billing.js +45 -45
- package/server/routes/commander.js +316 -316
- package/server/routes/demo-showcase.js +332 -332
- package/server/routes/demo-store.js +154 -0
- package/server/routes/discovery.js +417 -417
- package/server/routes/gateway.js +173 -157
- package/server/routes/license.js +251 -240
- package/server/routes/mesh.js +469 -469
- package/server/routes/noscript.js +543 -543
- package/server/routes/premium-v2.js +686 -686
- package/server/routes/premium.js +724 -724
- package/server/routes/runtime.js +2148 -2147
- package/server/routes/sovereign.js +465 -385
- package/server/routes/universal.js +200 -185
- package/server/routes/wab-api.js +850 -501
- package/server/runtime/container-worker.js +111 -111
- package/server/runtime/container.js +448 -448
- package/server/runtime/distributed-worker.js +362 -362
- package/server/runtime/event-bus.js +210 -210
- package/server/runtime/index.js +253 -253
- package/server/runtime/queue.js +599 -599
- package/server/runtime/replay.js +666 -666
- package/server/runtime/sandbox.js +266 -266
- package/server/runtime/scheduler.js +534 -534
- package/server/runtime/session-engine.js +293 -293
- package/server/runtime/state-manager.js +188 -188
- package/server/security/cross-site-redactor.js +196 -0
- package/server/security/dry-run.js +180 -0
- package/server/security/human-gate-rate-limit.js +147 -0
- package/server/security/human-gate-transports.js +178 -0
- package/server/security/human-gate.js +281 -0
- package/server/security/index.js +368 -368
- package/server/security/intent-engine.js +245 -0
- package/server/security/reward-guard.js +171 -0
- package/server/security/rollback-store.js +239 -0
- package/server/security/token-scope.js +404 -0
- package/server/security/url-policy.js +139 -0
- package/server/services/agent-chat.js +506 -506
- package/server/services/agent-learning.js +601 -575
- package/server/services/agent-memory.js +625 -625
- package/server/services/agent-mesh.js +555 -539
- package/server/services/agent-symphony.js +717 -717
- package/server/services/agent-tasks.js +1807 -1807
- package/server/services/api-key-engine.js +292 -261
- package/server/services/cluster.js +894 -894
- package/server/services/commander.js +738 -738
- package/server/services/edge-compute.js +440 -440
- package/server/services/email.js +204 -204
- package/server/services/hosted-runtime.js +205 -205
- package/server/services/lfd.js +635 -635
- package/server/services/local-ai.js +389 -389
- package/server/services/marketplace.js +270 -270
- package/server/services/metering.js +182 -182
- package/server/services/modules/affiliate-intelligence.js +93 -93
- package/server/services/modules/agent-firewall.js +90 -90
- package/server/services/modules/bounty.js +89 -89
- package/server/services/modules/collective-bargaining.js +92 -92
- package/server/services/modules/dark-pattern.js +66 -66
- package/server/services/modules/gov-intelligence.js +45 -45
- package/server/services/modules/neural.js +55 -55
- package/server/services/modules/notary.js +49 -49
- package/server/services/modules/price-time-machine.js +86 -86
- package/server/services/modules/protocol.js +104 -104
- package/server/services/negotiation.js +439 -439
- package/server/services/plugins.js +771 -771
- package/server/services/price-intelligence.js +566 -566
- package/server/services/price-shield.js +1137 -1137
- package/server/services/reputation.js +465 -465
- package/server/services/search-engine.js +357 -357
- package/server/services/security.js +513 -513
- package/server/services/self-healing.js +843 -843
- package/server/services/sovereign-shield.js +542 -0
- package/server/services/stripe.js +192 -192
- package/server/services/swarm.js +788 -788
- package/server/services/universal-scraper.js +662 -661
- package/server/services/verification.js +481 -481
- package/server/services/vision.js +1163 -1163
- package/server/utils/cache.js +125 -125
- package/server/utils/migrate.js +81 -81
- package/server/utils/safe-fetch.js +228 -0
- package/server/utils/secureFields.js +50 -50
- package/server/ws.js +161 -161
- package/templates/artisan-marketplace.yaml +104 -104
- package/templates/book-price-scout.yaml +98 -98
- package/templates/electronics-price-tracker.yaml +108 -108
- package/templates/flight-deal-hunter.yaml +113 -113
- package/templates/freelancer-direct.yaml +116 -116
- package/templates/grocery-price-compare.yaml +93 -93
- package/templates/hotel-direct-booking.yaml +113 -113
- package/templates/local-services.yaml +98 -98
- package/templates/olive-oil-tunisia.yaml +88 -88
- package/templates/organic-farm-fresh.yaml +101 -101
- package/templates/restaurant-direct.yaml +97 -97
- package/public/score.html +0 -263
- package/server/migrations/006_growth_suite.sql +0 -138
- package/server/routes/growth.js +0 -962
- package/server/services/fairness-engine.js +0 -409
- package/server/services/fairness.js +0 -420
package/public/script/wab.min.js
CHANGED
|
@@ -1,580 +1,580 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* WAB Client v2.0.0 — Add this script to make any website AI-agent-ready
|
|
3
|
-
* https://webagentbridge.com
|
|
4
|
-
*
|
|
5
|
-
* Usage (zero setup):
|
|
6
|
-
* <script src="https://webagentbridge.com/script/wab.min.js"></script>
|
|
7
|
-
* <script>
|
|
8
|
-
* WAB.init({
|
|
9
|
-
* name: "My Store",
|
|
10
|
-
* actions: {
|
|
11
|
-
* buy: { description: "Buy product", run: () => { ... } },
|
|
12
|
-
* getPrice: { description: "Get price", run: () => ({ price: "$49" }) }
|
|
13
|
-
* }
|
|
14
|
-
* });
|
|
15
|
-
* </script>
|
|
16
|
-
*
|
|
17
|
-
* That's it. Your site now supports:
|
|
18
|
-
* - In-browser agent access via window.__wab_protocol
|
|
19
|
-
* - Discovery via WAB.discover()
|
|
20
|
-
* - Execution via WAB.execute("actionName", params)
|
|
21
|
-
* - Cross-frame sync (postMessage) for embedded demos
|
|
22
|
-
* - Auto-connection to WAB server endpoints if available
|
|
23
|
-
*
|
|
24
|
-
* License: MIT
|
|
25
|
-
*/
|
|
26
|
-
(function(global) {
|
|
27
|
-
'use strict';
|
|
28
|
-
|
|
29
|
-
var VERSION = '2.2.0';
|
|
30
|
-
|
|
31
|
-
// ── WAB Instance ──────────────────────────────────────────────────
|
|
32
|
-
function WABInstance(config) {
|
|
33
|
-
this.name = config.name || document.title || 'WAB Site';
|
|
34
|
-
this.serverUrl = config.serverUrl || '';
|
|
35
|
-
this.actions = {};
|
|
36
|
-
this._auditLog = [];
|
|
37
|
-
this._listeners = {};
|
|
38
|
-
this._ready = false;
|
|
39
|
-
|
|
40
|
-
if (config.actions) {
|
|
41
|
-
for (var key in config.actions) {
|
|
42
|
-
if (!config.actions.hasOwnProperty(key)) continue;
|
|
43
|
-
var action = config.actions[key];
|
|
44
|
-
if (typeof action === 'function') {
|
|
45
|
-
this.actions[key] = { description: key, params: [], run: action };
|
|
46
|
-
} else {
|
|
47
|
-
this.actions[key] = {
|
|
48
|
-
description: action.description || key,
|
|
49
|
-
params: action.params || [],
|
|
50
|
-
run: action.run || action.handler || function() { return { error: 'no handler' }; }
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
this._exposeProtocol();
|
|
57
|
-
this._setupCrossFrame();
|
|
58
|
-
this._ready = true;
|
|
59
|
-
this._emit('ready', { name: this.name, actions: Object.keys(this.actions) });
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// Expose window.__wab_protocol for in-browser agents
|
|
63
|
-
WABInstance.prototype._exposeProtocol = function() {
|
|
64
|
-
var self = this;
|
|
65
|
-
global.__wab_protocol = {
|
|
66
|
-
version: VERSION,
|
|
67
|
-
protocol: '1.0',
|
|
68
|
-
name: this.name,
|
|
69
|
-
actions: Object.keys(this.actions).map(function(k) {
|
|
70
|
-
return { name: k, description: self.actions[k].description, params: self.actions[k].params };
|
|
71
|
-
}),
|
|
72
|
-
transport: ['javascript', 'http'],
|
|
73
|
-
ready: true,
|
|
74
|
-
discover: function() { return self.discover(); },
|
|
75
|
-
execute: function(name, params) { return self.execute(name, params); },
|
|
76
|
-
negotiate: function(agentId, proposal) { return self.negotiate(agentId, proposal); },
|
|
77
|
-
getReputation: function(siteId) { return self.getReputation(siteId); },
|
|
78
|
-
verifyPrice: function(productId, domPrice, visionPrice) { return self.verifyPrice(productId, domPrice, visionPrice); }
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
// Also expose on window.AICommands for broad compatibility
|
|
82
|
-
global.AICommands = global.AICommands || {};
|
|
83
|
-
global.AICommands.negotiate = function(agentId, proposal) { return self.negotiate(agentId, proposal); };
|
|
84
|
-
global.AICommands.getReputation = function(siteId) { return self.getReputation(siteId); };
|
|
85
|
-
global.AICommands.verifyPrice = function(productId, domP, visP) { return self.verifyPrice(productId, domP, visP); };
|
|
86
|
-
};
|
|
87
|
-
|
|
88
|
-
// Cross-frame communication for embedded demos
|
|
89
|
-
WABInstance.prototype._setupCrossFrame = function() {
|
|
90
|
-
var self = this;
|
|
91
|
-
|
|
92
|
-
global.addEventListener('message', function(e) {
|
|
93
|
-
if (!e.data || e.data.source === 'wab-store') return;
|
|
94
|
-
|
|
95
|
-
// Handle requests from parent (agent demo page)
|
|
96
|
-
if (e.data.source === 'wab-agent' && e.data.type === 'refresh') {
|
|
97
|
-
self._emit('refresh');
|
|
98
|
-
}
|
|
99
|
-
if (e.data.source === 'wab-agent' && e.data.type === 'discover') {
|
|
100
|
-
self.discover().then(function(doc) {
|
|
101
|
-
e.source.postMessage({ source: 'wab-store', type: 'discover-response', detail: doc }, '*');
|
|
102
|
-
});
|
|
103
|
-
}
|
|
104
|
-
if (e.data.source === 'wab-agent' && e.data.type === 'execute') {
|
|
105
|
-
self.execute(e.data.action, e.data.params).then(function(result) {
|
|
106
|
-
e.source.postMessage({ source: 'wab-store', type: 'execute-response', action: e.data.action, detail: result }, '*');
|
|
107
|
-
});
|
|
108
|
-
}
|
|
109
|
-
// Negotiation protocol via postMessage
|
|
110
|
-
if (e.data.source === 'wab-agent' && e.data.type === 'negotiate') {
|
|
111
|
-
self.negotiate(e.data.agentId, e.data.proposal).then(function(result) {
|
|
112
|
-
e.source.postMessage({ source: 'wab-store', type: 'negotiate-response', detail: result }, '*');
|
|
113
|
-
});
|
|
114
|
-
}
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
// Notify parent that store is ready
|
|
118
|
-
this._notifyParent('store-ready', { name: this.name, actions: Object.keys(this.actions) });
|
|
119
|
-
};
|
|
120
|
-
|
|
121
|
-
WABInstance.prototype._notifyParent = function(type, detail) {
|
|
122
|
-
try {
|
|
123
|
-
if (global.parent !== global) {
|
|
124
|
-
global.parent.postMessage({ source: 'wab-store', type: type, detail: detail }, '*');
|
|
125
|
-
}
|
|
126
|
-
} catch(e) {}
|
|
127
|
-
};
|
|
128
|
-
|
|
129
|
-
// Event system
|
|
130
|
-
WABInstance.prototype.on = function(event, fn) {
|
|
131
|
-
if (!this._listeners[event]) this._listeners[event] = [];
|
|
132
|
-
this._listeners[event].push(fn);
|
|
133
|
-
return this;
|
|
134
|
-
};
|
|
135
|
-
|
|
136
|
-
WABInstance.prototype._emit = function(event, data) {
|
|
137
|
-
var fns = this._listeners[event] || [];
|
|
138
|
-
for (var i = 0; i < fns.length; i++) {
|
|
139
|
-
try { fns[i](data); } catch(e) {}
|
|
140
|
-
}
|
|
141
|
-
};
|
|
142
|
-
|
|
143
|
-
// ── Core API ──────────────────────────────────────────────────────
|
|
144
|
-
|
|
145
|
-
WABInstance.prototype.discover = function() {
|
|
146
|
-
var self = this;
|
|
147
|
-
var actionList = [];
|
|
148
|
-
for (var key in this.actions) {
|
|
149
|
-
if (this.actions.hasOwnProperty(key)) {
|
|
150
|
-
actionList.push({ name: key, description: this.actions[key].description, params: this.actions[key].params });
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
// If server URL is configured, fetch from server for authoritative discovery
|
|
155
|
-
if (this.serverUrl) {
|
|
156
|
-
return fetch(this.serverUrl + '/.well-known/wab.json')
|
|
157
|
-
.then(function(r) { return r.json(); })
|
|
158
|
-
.catch(function() {
|
|
159
|
-
return { wab_version: VERSION, protocol: '1.0', name: self.name, actions: actionList, transport: ['javascript'] };
|
|
160
|
-
});
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
return Promise.resolve({
|
|
164
|
-
wab_version: VERSION,
|
|
165
|
-
protocol: '1.0',
|
|
166
|
-
name: this.name,
|
|
167
|
-
actions: actionList,
|
|
168
|
-
transport: ['javascript', 'http']
|
|
169
|
-
});
|
|
170
|
-
};
|
|
171
|
-
|
|
172
|
-
WABInstance.prototype.execute = function(actionName, params) {
|
|
173
|
-
var self = this;
|
|
174
|
-
var action = this.actions[actionName];
|
|
175
|
-
params = params || {};
|
|
176
|
-
|
|
177
|
-
// If server URL configured and action not local, proxy to server
|
|
178
|
-
if (!action && this.serverUrl) {
|
|
179
|
-
return fetch(this.serverUrl + '/wab/execute', {
|
|
180
|
-
method: 'POST',
|
|
181
|
-
headers: { 'Content-Type': 'application/json' },
|
|
182
|
-
body: JSON.stringify({ action: actionName, params: params })
|
|
183
|
-
}).then(function(r) { return r.json(); });
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
if (!action) {
|
|
187
|
-
return Promise.resolve({ success: false, error: 'Action not found: ' + actionName });
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
var start = Date.now();
|
|
191
|
-
try {
|
|
192
|
-
var result = action.run(params);
|
|
193
|
-
var handleResult = function(res) {
|
|
194
|
-
var duration = Date.now() - start;
|
|
195
|
-
self._auditLog.push({ action: actionName, params: params, result: res, duration_ms: duration, timestamp: new Date().toISOString() });
|
|
196
|
-
if (self._auditLog.length > 200) self._auditLog.shift();
|
|
197
|
-
self._notifyParent('action-executed', { action: actionName, result: res, duration_ms: duration });
|
|
198
|
-
self._emit('execute', { action: actionName, params: params, result: res, duration_ms: duration });
|
|
199
|
-
return res;
|
|
200
|
-
};
|
|
201
|
-
if (result && typeof result.then === 'function') {
|
|
202
|
-
return result.then(handleResult);
|
|
203
|
-
}
|
|
204
|
-
return Promise.resolve(handleResult(result));
|
|
205
|
-
} catch (e) {
|
|
206
|
-
return Promise.resolve({ success: false, error: e.message });
|
|
207
|
-
}
|
|
208
|
-
};
|
|
209
|
-
|
|
210
|
-
WABInstance.prototype.getActions = function() {
|
|
211
|
-
return Object.keys(this.actions);
|
|
212
|
-
};
|
|
213
|
-
|
|
214
|
-
WABInstance.prototype.getAuditLog = function() {
|
|
215
|
-
return this._auditLog.slice();
|
|
216
|
-
};
|
|
217
|
-
|
|
218
|
-
// ── Negotiation Support ─────────────────────────────────────────
|
|
219
|
-
// Sites can define negotiation rules; agents can open sessions and propose
|
|
220
|
-
|
|
221
|
-
WABInstance.prototype.setNegotiationRules = function(rules) {
|
|
222
|
-
this._negotiationRules = rules || {};
|
|
223
|
-
// Expose in protocol
|
|
224
|
-
if (global.__wab_protocol) {
|
|
225
|
-
global.__wab_protocol.negotiation = { available: true, rules: Object.keys(rules) };
|
|
226
|
-
}
|
|
227
|
-
return this;
|
|
228
|
-
};
|
|
229
|
-
|
|
230
|
-
WABInstance.prototype.negotiate = function(agentId, proposal) {
|
|
231
|
-
var self = this;
|
|
232
|
-
// Support in-browser negotiation rules (no server needed)
|
|
233
|
-
if (this._negotiationRules && !this.serverUrl) {
|
|
234
|
-
return this._handleLocalNegotiation(agentId, proposal);
|
|
235
|
-
}
|
|
236
|
-
if (!this.serverUrl) {
|
|
237
|
-
return Promise.resolve({ error: 'Negotiation requires a server URL or local rules' });
|
|
238
|
-
}
|
|
239
|
-
return fetch(this.serverUrl + '/api/sovereign/negotiation/sessions', {
|
|
240
|
-
method: 'POST',
|
|
241
|
-
headers: { 'Content-Type': 'application/json' },
|
|
242
|
-
body: JSON.stringify({
|
|
243
|
-
siteId: self.name,
|
|
244
|
-
agentId: agentId,
|
|
245
|
-
itemId: proposal.itemId || null,
|
|
246
|
-
itemName: proposal.itemName || 'item',
|
|
247
|
-
originalPrice: proposal.originalPrice || 0
|
|
248
|
-
})
|
|
249
|
-
}).then(function(r) { return r.json(); })
|
|
250
|
-
.then(function(session) {
|
|
251
|
-
if (!session.sessionId) return session;
|
|
252
|
-
return fetch(self.serverUrl + '/api/sovereign/negotiation/sessions/' + session.sessionId + '/propose', {
|
|
253
|
-
method: 'POST',
|
|
254
|
-
headers: { 'Content-Type': 'application/json' },
|
|
255
|
-
body: JSON.stringify({
|
|
256
|
-
strategy: proposal.strategy || 'instant_payment',
|
|
257
|
-
proposedDiscount: proposal.proposedDiscount || 5,
|
|
258
|
-
arguments: proposal.arguments || []
|
|
259
|
-
})
|
|
260
|
-
}).then(function(r) { return r.json(); });
|
|
261
|
-
});
|
|
262
|
-
};
|
|
263
|
-
|
|
264
|
-
// Local in-browser negotiation (no server required)
|
|
265
|
-
WABInstance.prototype._handleLocalNegotiation = function(agentId, proposal) {
|
|
266
|
-
var rules = this._negotiationRules;
|
|
267
|
-
var strategy = proposal.strategy || 'custom';
|
|
268
|
-
var rule = rules[strategy];
|
|
269
|
-
if (!rule) {
|
|
270
|
-
return Promise.resolve({
|
|
271
|
-
status: 'rejected',
|
|
272
|
-
reason: 'no_applicable_rule',
|
|
273
|
-
message: 'No rule matches strategy: ' + strategy
|
|
274
|
-
});
|
|
275
|
-
}
|
|
276
|
-
var requested = proposal.proposedDiscount || 0;
|
|
277
|
-
var maxAllowed = rule.maxDiscount || 20;
|
|
278
|
-
var offered = rule.discount || 0;
|
|
279
|
-
var actual = Math.min(requested, maxAllowed, offered);
|
|
280
|
-
var originalPrice = proposal.originalPrice || 0;
|
|
281
|
-
var finalPrice = Math.round(originalPrice * (1 - actual / 100) * 100) / 100;
|
|
282
|
-
|
|
283
|
-
if (requested <= offered) {
|
|
284
|
-
this._emit('negotiation', { status: 'accepted', agentId: agentId, discount: actual, finalPrice: finalPrice });
|
|
285
|
-
return Promise.resolve({
|
|
286
|
-
status: 'agreed',
|
|
287
|
-
discount: actual,
|
|
288
|
-
originalPrice: originalPrice,
|
|
289
|
-
finalPrice: finalPrice,
|
|
290
|
-
message: 'Deal accepted! ' + actual + '% off. Price: $' + finalPrice
|
|
291
|
-
});
|
|
292
|
-
}
|
|
293
|
-
// Counter-offer
|
|
294
|
-
this._emit('negotiation', { status: 'counter', agentId: agentId, counterDiscount: offered });
|
|
295
|
-
return Promise.resolve({
|
|
296
|
-
status: 'counter_offer',
|
|
297
|
-
counterDiscount: offered,
|
|
298
|
-
counterPrice: Math.round(originalPrice * (1 - offered / 100) * 100) / 100,
|
|
299
|
-
message: 'We can offer ' + offered + '% off for ' + strategy.replace(/_/g, ' ')
|
|
300
|
-
});
|
|
301
|
-
};
|
|
302
|
-
|
|
303
|
-
// ── Reputation Support ────────────────────────────────────────────
|
|
304
|
-
|
|
305
|
-
WABInstance.prototype.submitAttestation = function(agentId, targetSiteId, interactionType, outcome, extras) {
|
|
306
|
-
if (!this.serverUrl) {
|
|
307
|
-
return Promise.resolve({ error: 'Reputation requires a server URL' });
|
|
308
|
-
}
|
|
309
|
-
var body = {
|
|
310
|
-
siteId: targetSiteId,
|
|
311
|
-
agentId: agentId,
|
|
312
|
-
interactionType: interactionType || 'purchase',
|
|
313
|
-
outcome: outcome || 'success'
|
|
314
|
-
};
|
|
315
|
-
if (extras) {
|
|
316
|
-
if (extras.priceAccuracy != null) body.priceAccuracy = extras.priceAccuracy;
|
|
317
|
-
if (extras.responseTimeMs != null) body.responseTimeMs = extras.responseTimeMs;
|
|
318
|
-
if (extras.dataIntegrity != null) body.dataIntegrity = extras.dataIntegrity;
|
|
319
|
-
if (extras.visionVerified != null) body.visionVerified = extras.visionVerified;
|
|
320
|
-
if (extras.details) body.details = extras.details;
|
|
321
|
-
}
|
|
322
|
-
return fetch(this.serverUrl + '/api/sovereign/reputation/attestations', {
|
|
323
|
-
method: 'POST',
|
|
324
|
-
headers: { 'Content-Type': 'application/json' },
|
|
325
|
-
body: JSON.stringify(body)
|
|
326
|
-
}).then(function(r) { return r.json(); });
|
|
327
|
-
};
|
|
328
|
-
|
|
329
|
-
WABInstance.prototype.getReputation = function(siteId) {
|
|
330
|
-
if (!this.serverUrl) {
|
|
331
|
-
return Promise.resolve({ error: 'Reputation requires a server URL' });
|
|
332
|
-
}
|
|
333
|
-
return fetch(this.serverUrl + '/api/sovereign/reputation/sites/' + encodeURIComponent(siteId))
|
|
334
|
-
.then(function(r) { return r.json(); });
|
|
335
|
-
};
|
|
336
|
-
|
|
337
|
-
// ── Verification Support ──────────────────────────────────────────
|
|
338
|
-
|
|
339
|
-
WABInstance.prototype.verifyPrice = function(opts) {
|
|
340
|
-
if (!this.serverUrl) {
|
|
341
|
-
return Promise.resolve({ error: 'Verification requires a server URL' });
|
|
342
|
-
}
|
|
343
|
-
var body = {
|
|
344
|
-
siteId: opts.siteId || this.name,
|
|
345
|
-
agentId: opts.agentId || null,
|
|
346
|
-
url: opts.url || null,
|
|
347
|
-
domValue: opts.domValue || opts.domPrice,
|
|
348
|
-
visionValue: opts.visionValue || opts.visionPrice,
|
|
349
|
-
category: opts.category || null,
|
|
350
|
-
itemName: opts.itemName || null
|
|
351
|
-
};
|
|
352
|
-
return fetch(this.serverUrl + '/api/sovereign/verify/price', {
|
|
353
|
-
method: 'POST',
|
|
354
|
-
headers: { 'Content-Type': 'application/json' },
|
|
355
|
-
body: JSON.stringify(body)
|
|
356
|
-
}).then(function(r) { return r.json(); });
|
|
357
|
-
};
|
|
358
|
-
|
|
359
|
-
// ── Agent Mesh Protocol ───────────────────────────────────────────
|
|
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
|
-
|
|
368
|
-
WABInstance.prototype.meshJoin = function(role, displayName, capabilities) {
|
|
369
|
-
if (!this.serverUrl) return Promise.reject(new Error('Mesh requires a server URL'));
|
|
370
|
-
var self = this;
|
|
371
|
-
return _meshReq(this.serverUrl + '/api/mesh/agents', {
|
|
372
|
-
method: 'POST',
|
|
373
|
-
headers: { 'Content-Type': 'application/json' },
|
|
374
|
-
body: JSON.stringify({ siteId: this.name, role: role, displayName: displayName, capabilities: capabilities })
|
|
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;
|
|
382
|
-
});
|
|
383
|
-
};
|
|
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
|
-
|
|
393
|
-
WABInstance.prototype.meshPublish = function(channel, messageType, subject, payload, opts) {
|
|
394
|
-
if (!this.serverUrl || !this._meshAgentId) return Promise.reject(new Error('Must join mesh first'));
|
|
395
|
-
return _meshReq(this.serverUrl + '/api/mesh/messages', {
|
|
396
|
-
method: 'POST',
|
|
397
|
-
headers: { 'Content-Type': 'application/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; });
|
|
400
|
-
};
|
|
401
|
-
|
|
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');
|
|
416
|
-
};
|
|
417
|
-
|
|
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', {
|
|
422
|
-
method: 'POST',
|
|
423
|
-
headers: { 'Content-Type': 'application/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; });
|
|
438
|
-
};
|
|
439
|
-
|
|
440
|
-
WABInstance.prototype.meshAlert = function(subject, details, priority) {
|
|
441
|
-
if (!this.serverUrl || !this._meshAgentId) return Promise.reject(new Error('Must join mesh first'));
|
|
442
|
-
return _meshReq(this.serverUrl + '/api/mesh/alert', {
|
|
443
|
-
method: 'POST',
|
|
444
|
-
headers: { 'Content-Type': 'application/json' },
|
|
445
|
-
body: JSON.stringify({ senderId: this._meshAgentId, subject: subject, details: details, priority: priority })
|
|
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; });
|
|
471
|
-
};
|
|
472
|
-
|
|
473
|
-
// ── Agent Symphony Orchestrator ───────────────────────────────────
|
|
474
|
-
|
|
475
|
-
WABInstance.prototype.symphonyPerform = function(template, inputData, schema) {
|
|
476
|
-
if (!this.serverUrl) return Promise.reject(new Error('Symphony requires a server URL'));
|
|
477
|
-
var self = this;
|
|
478
|
-
return _meshReq(this.serverUrl + '/api/mesh/symphony/compose', {
|
|
479
|
-
method: 'POST',
|
|
480
|
-
headers: { 'Content-Type': 'application/json' },
|
|
481
|
-
body: JSON.stringify({ siteId: this.name, template: template, inputData: inputData, schema: schema })
|
|
482
|
-
}).then(function(data) {
|
|
483
|
-
self._emit('symphony:completed', data);
|
|
484
|
-
return data;
|
|
485
|
-
});
|
|
486
|
-
};
|
|
487
|
-
|
|
488
|
-
// ── Agent Learning Engine ─────────────────────────────────────────
|
|
489
|
-
|
|
490
|
-
WABInstance.prototype.learnRecord = function(domain, action, context, features) {
|
|
491
|
-
if (!this.serverUrl || !this._meshAgentId) return Promise.reject(new Error('Must join mesh first'));
|
|
492
|
-
return _meshReq(this.serverUrl + '/api/mesh/learning/decisions', {
|
|
493
|
-
method: 'POST',
|
|
494
|
-
headers: { 'Content-Type': 'application/json' },
|
|
495
|
-
body: JSON.stringify({ siteId: this.name, agentId: this._meshAgentId, domain: domain, action: action, context: context, features: features })
|
|
496
|
-
});
|
|
497
|
-
};
|
|
498
|
-
|
|
499
|
-
WABInstance.prototype.learnFeedback = function(decisionId, outcome, reward) {
|
|
500
|
-
if (!this.serverUrl) return Promise.reject(new Error('Learning requires a server URL'));
|
|
501
|
-
return _meshReq(this.serverUrl + '/api/mesh/learning/feedback', {
|
|
502
|
-
method: 'POST',
|
|
503
|
-
headers: { 'Content-Type': 'application/json' },
|
|
504
|
-
body: JSON.stringify({ decisionId: decisionId, outcome: outcome, reward: reward })
|
|
505
|
-
});
|
|
506
|
-
};
|
|
507
|
-
|
|
508
|
-
WABInstance.prototype.learnRecommend = function(domain, actions, context) {
|
|
509
|
-
if (!this.serverUrl || !this._meshAgentId) return Promise.reject(new Error('Must join mesh first'));
|
|
510
|
-
return _meshReq(this.serverUrl + '/api/mesh/learning/recommend', {
|
|
511
|
-
method: 'POST',
|
|
512
|
-
headers: { 'Content-Type': 'application/json' },
|
|
513
|
-
body: JSON.stringify({ siteId: this.name, agentId: this._meshAgentId, domain: domain, actions: actions, context: context })
|
|
514
|
-
});
|
|
515
|
-
};
|
|
516
|
-
|
|
517
|
-
// ── Static API ────────────────────────────────────────────────────
|
|
518
|
-
|
|
519
|
-
var WAB = {
|
|
520
|
-
version: VERSION,
|
|
521
|
-
|
|
522
|
-
init: function(config) {
|
|
523
|
-
var instance = new WABInstance(config || {});
|
|
524
|
-
WAB._instance = instance;
|
|
525
|
-
return instance;
|
|
526
|
-
},
|
|
527
|
-
|
|
528
|
-
connect: function(serverUrl) {
|
|
529
|
-
return new WABInstance({ name: 'WAB Client', serverUrl: serverUrl });
|
|
530
|
-
},
|
|
531
|
-
|
|
532
|
-
discover: function() {
|
|
533
|
-
if (WAB._instance) return WAB._instance.discover();
|
|
534
|
-
return Promise.reject(new Error('WAB not initialized. Call WAB.init() first.'));
|
|
535
|
-
},
|
|
536
|
-
|
|
537
|
-
execute: function(actionName, params) {
|
|
538
|
-
if (WAB._instance) return WAB._instance.execute(actionName, params);
|
|
539
|
-
return Promise.reject(new Error('WAB not initialized. Call WAB.init() first.'));
|
|
540
|
-
},
|
|
541
|
-
|
|
542
|
-
negotiate: function(agentId, proposal) {
|
|
543
|
-
if (WAB._instance) return WAB._instance.negotiate(agentId, proposal);
|
|
544
|
-
return Promise.reject(new Error('WAB not initialized'));
|
|
545
|
-
},
|
|
546
|
-
|
|
547
|
-
getReputation: function(siteId) {
|
|
548
|
-
if (WAB._instance) return WAB._instance.getReputation(siteId);
|
|
549
|
-
return Promise.reject(new Error('WAB not initialized'));
|
|
550
|
-
},
|
|
551
|
-
|
|
552
|
-
verifyPrice: function(opts) {
|
|
553
|
-
if (WAB._instance) return WAB._instance.verifyPrice(opts);
|
|
554
|
-
return Promise.reject(new Error('WAB not initialized'));
|
|
555
|
-
},
|
|
556
|
-
|
|
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')); },
|
|
574
|
-
|
|
575
|
-
_instance: null
|
|
576
|
-
};
|
|
577
|
-
|
|
578
|
-
global.WAB = WAB;
|
|
579
|
-
|
|
580
|
-
})(typeof window !== 'undefined' ? window : this);
|
|
1
|
+
/**
|
|
2
|
+
* WAB Client v2.0.0 — Add this script to make any website AI-agent-ready
|
|
3
|
+
* https://webagentbridge.com
|
|
4
|
+
*
|
|
5
|
+
* Usage (zero setup):
|
|
6
|
+
* <script src="https://webagentbridge.com/script/wab.min.js"></script>
|
|
7
|
+
* <script>
|
|
8
|
+
* WAB.init({
|
|
9
|
+
* name: "My Store",
|
|
10
|
+
* actions: {
|
|
11
|
+
* buy: { description: "Buy product", run: () => { ... } },
|
|
12
|
+
* getPrice: { description: "Get price", run: () => ({ price: "$49" }) }
|
|
13
|
+
* }
|
|
14
|
+
* });
|
|
15
|
+
* </script>
|
|
16
|
+
*
|
|
17
|
+
* That's it. Your site now supports:
|
|
18
|
+
* - In-browser agent access via window.__wab_protocol
|
|
19
|
+
* - Discovery via WAB.discover()
|
|
20
|
+
* - Execution via WAB.execute("actionName", params)
|
|
21
|
+
* - Cross-frame sync (postMessage) for embedded demos
|
|
22
|
+
* - Auto-connection to WAB server endpoints if available
|
|
23
|
+
*
|
|
24
|
+
* License: MIT
|
|
25
|
+
*/
|
|
26
|
+
(function(global) {
|
|
27
|
+
'use strict';
|
|
28
|
+
|
|
29
|
+
var VERSION = '2.2.0';
|
|
30
|
+
|
|
31
|
+
// ── WAB Instance ──────────────────────────────────────────────────
|
|
32
|
+
function WABInstance(config) {
|
|
33
|
+
this.name = config.name || document.title || 'WAB Site';
|
|
34
|
+
this.serverUrl = config.serverUrl || '';
|
|
35
|
+
this.actions = {};
|
|
36
|
+
this._auditLog = [];
|
|
37
|
+
this._listeners = {};
|
|
38
|
+
this._ready = false;
|
|
39
|
+
|
|
40
|
+
if (config.actions) {
|
|
41
|
+
for (var key in config.actions) {
|
|
42
|
+
if (!config.actions.hasOwnProperty(key)) continue;
|
|
43
|
+
var action = config.actions[key];
|
|
44
|
+
if (typeof action === 'function') {
|
|
45
|
+
this.actions[key] = { description: key, params: [], run: action };
|
|
46
|
+
} else {
|
|
47
|
+
this.actions[key] = {
|
|
48
|
+
description: action.description || key,
|
|
49
|
+
params: action.params || [],
|
|
50
|
+
run: action.run || action.handler || function() { return { error: 'no handler' }; }
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
this._exposeProtocol();
|
|
57
|
+
this._setupCrossFrame();
|
|
58
|
+
this._ready = true;
|
|
59
|
+
this._emit('ready', { name: this.name, actions: Object.keys(this.actions) });
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Expose window.__wab_protocol for in-browser agents
|
|
63
|
+
WABInstance.prototype._exposeProtocol = function() {
|
|
64
|
+
var self = this;
|
|
65
|
+
global.__wab_protocol = {
|
|
66
|
+
version: VERSION,
|
|
67
|
+
protocol: '1.0',
|
|
68
|
+
name: this.name,
|
|
69
|
+
actions: Object.keys(this.actions).map(function(k) {
|
|
70
|
+
return { name: k, description: self.actions[k].description, params: self.actions[k].params };
|
|
71
|
+
}),
|
|
72
|
+
transport: ['javascript', 'http'],
|
|
73
|
+
ready: true,
|
|
74
|
+
discover: function() { return self.discover(); },
|
|
75
|
+
execute: function(name, params) { return self.execute(name, params); },
|
|
76
|
+
negotiate: function(agentId, proposal) { return self.negotiate(agentId, proposal); },
|
|
77
|
+
getReputation: function(siteId) { return self.getReputation(siteId); },
|
|
78
|
+
verifyPrice: function(productId, domPrice, visionPrice) { return self.verifyPrice(productId, domPrice, visionPrice); }
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
// Also expose on window.AICommands for broad compatibility
|
|
82
|
+
global.AICommands = global.AICommands || {};
|
|
83
|
+
global.AICommands.negotiate = function(agentId, proposal) { return self.negotiate(agentId, proposal); };
|
|
84
|
+
global.AICommands.getReputation = function(siteId) { return self.getReputation(siteId); };
|
|
85
|
+
global.AICommands.verifyPrice = function(productId, domP, visP) { return self.verifyPrice(productId, domP, visP); };
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
// Cross-frame communication for embedded demos
|
|
89
|
+
WABInstance.prototype._setupCrossFrame = function() {
|
|
90
|
+
var self = this;
|
|
91
|
+
|
|
92
|
+
global.addEventListener('message', function(e) {
|
|
93
|
+
if (!e.data || e.data.source === 'wab-store') return;
|
|
94
|
+
|
|
95
|
+
// Handle requests from parent (agent demo page)
|
|
96
|
+
if (e.data.source === 'wab-agent' && e.data.type === 'refresh') {
|
|
97
|
+
self._emit('refresh');
|
|
98
|
+
}
|
|
99
|
+
if (e.data.source === 'wab-agent' && e.data.type === 'discover') {
|
|
100
|
+
self.discover().then(function(doc) {
|
|
101
|
+
e.source.postMessage({ source: 'wab-store', type: 'discover-response', detail: doc }, '*');
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
if (e.data.source === 'wab-agent' && e.data.type === 'execute') {
|
|
105
|
+
self.execute(e.data.action, e.data.params).then(function(result) {
|
|
106
|
+
e.source.postMessage({ source: 'wab-store', type: 'execute-response', action: e.data.action, detail: result }, '*');
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
// Negotiation protocol via postMessage
|
|
110
|
+
if (e.data.source === 'wab-agent' && e.data.type === 'negotiate') {
|
|
111
|
+
self.negotiate(e.data.agentId, e.data.proposal).then(function(result) {
|
|
112
|
+
e.source.postMessage({ source: 'wab-store', type: 'negotiate-response', detail: result }, '*');
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
// Notify parent that store is ready
|
|
118
|
+
this._notifyParent('store-ready', { name: this.name, actions: Object.keys(this.actions) });
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
WABInstance.prototype._notifyParent = function(type, detail) {
|
|
122
|
+
try {
|
|
123
|
+
if (global.parent !== global) {
|
|
124
|
+
global.parent.postMessage({ source: 'wab-store', type: type, detail: detail }, '*');
|
|
125
|
+
}
|
|
126
|
+
} catch(e) {}
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
// Event system
|
|
130
|
+
WABInstance.prototype.on = function(event, fn) {
|
|
131
|
+
if (!this._listeners[event]) this._listeners[event] = [];
|
|
132
|
+
this._listeners[event].push(fn);
|
|
133
|
+
return this;
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
WABInstance.prototype._emit = function(event, data) {
|
|
137
|
+
var fns = this._listeners[event] || [];
|
|
138
|
+
for (var i = 0; i < fns.length; i++) {
|
|
139
|
+
try { fns[i](data); } catch(e) {}
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
// ── Core API ──────────────────────────────────────────────────────
|
|
144
|
+
|
|
145
|
+
WABInstance.prototype.discover = function() {
|
|
146
|
+
var self = this;
|
|
147
|
+
var actionList = [];
|
|
148
|
+
for (var key in this.actions) {
|
|
149
|
+
if (this.actions.hasOwnProperty(key)) {
|
|
150
|
+
actionList.push({ name: key, description: this.actions[key].description, params: this.actions[key].params });
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// If server URL is configured, fetch from server for authoritative discovery
|
|
155
|
+
if (this.serverUrl) {
|
|
156
|
+
return fetch(this.serverUrl + '/.well-known/wab.json')
|
|
157
|
+
.then(function(r) { return r.json(); })
|
|
158
|
+
.catch(function() {
|
|
159
|
+
return { wab_version: VERSION, protocol: '1.0', name: self.name, actions: actionList, transport: ['javascript'] };
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return Promise.resolve({
|
|
164
|
+
wab_version: VERSION,
|
|
165
|
+
protocol: '1.0',
|
|
166
|
+
name: this.name,
|
|
167
|
+
actions: actionList,
|
|
168
|
+
transport: ['javascript', 'http']
|
|
169
|
+
});
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
WABInstance.prototype.execute = function(actionName, params) {
|
|
173
|
+
var self = this;
|
|
174
|
+
var action = this.actions[actionName];
|
|
175
|
+
params = params || {};
|
|
176
|
+
|
|
177
|
+
// If server URL configured and action not local, proxy to server
|
|
178
|
+
if (!action && this.serverUrl) {
|
|
179
|
+
return fetch(this.serverUrl + '/wab/execute', {
|
|
180
|
+
method: 'POST',
|
|
181
|
+
headers: { 'Content-Type': 'application/json' },
|
|
182
|
+
body: JSON.stringify({ action: actionName, params: params })
|
|
183
|
+
}).then(function(r) { return r.json(); });
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if (!action) {
|
|
187
|
+
return Promise.resolve({ success: false, error: 'Action not found: ' + actionName });
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
var start = Date.now();
|
|
191
|
+
try {
|
|
192
|
+
var result = action.run(params);
|
|
193
|
+
var handleResult = function(res) {
|
|
194
|
+
var duration = Date.now() - start;
|
|
195
|
+
self._auditLog.push({ action: actionName, params: params, result: res, duration_ms: duration, timestamp: new Date().toISOString() });
|
|
196
|
+
if (self._auditLog.length > 200) self._auditLog.shift();
|
|
197
|
+
self._notifyParent('action-executed', { action: actionName, result: res, duration_ms: duration });
|
|
198
|
+
self._emit('execute', { action: actionName, params: params, result: res, duration_ms: duration });
|
|
199
|
+
return res;
|
|
200
|
+
};
|
|
201
|
+
if (result && typeof result.then === 'function') {
|
|
202
|
+
return result.then(handleResult);
|
|
203
|
+
}
|
|
204
|
+
return Promise.resolve(handleResult(result));
|
|
205
|
+
} catch (e) {
|
|
206
|
+
return Promise.resolve({ success: false, error: e.message });
|
|
207
|
+
}
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
WABInstance.prototype.getActions = function() {
|
|
211
|
+
return Object.keys(this.actions);
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
WABInstance.prototype.getAuditLog = function() {
|
|
215
|
+
return this._auditLog.slice();
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
// ── Negotiation Support ─────────────────────────────────────────
|
|
219
|
+
// Sites can define negotiation rules; agents can open sessions and propose
|
|
220
|
+
|
|
221
|
+
WABInstance.prototype.setNegotiationRules = function(rules) {
|
|
222
|
+
this._negotiationRules = rules || {};
|
|
223
|
+
// Expose in protocol
|
|
224
|
+
if (global.__wab_protocol) {
|
|
225
|
+
global.__wab_protocol.negotiation = { available: true, rules: Object.keys(rules) };
|
|
226
|
+
}
|
|
227
|
+
return this;
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
WABInstance.prototype.negotiate = function(agentId, proposal) {
|
|
231
|
+
var self = this;
|
|
232
|
+
// Support in-browser negotiation rules (no server needed)
|
|
233
|
+
if (this._negotiationRules && !this.serverUrl) {
|
|
234
|
+
return this._handleLocalNegotiation(agentId, proposal);
|
|
235
|
+
}
|
|
236
|
+
if (!this.serverUrl) {
|
|
237
|
+
return Promise.resolve({ error: 'Negotiation requires a server URL or local rules' });
|
|
238
|
+
}
|
|
239
|
+
return fetch(this.serverUrl + '/api/sovereign/negotiation/sessions', {
|
|
240
|
+
method: 'POST',
|
|
241
|
+
headers: { 'Content-Type': 'application/json' },
|
|
242
|
+
body: JSON.stringify({
|
|
243
|
+
siteId: self.name,
|
|
244
|
+
agentId: agentId,
|
|
245
|
+
itemId: proposal.itemId || null,
|
|
246
|
+
itemName: proposal.itemName || 'item',
|
|
247
|
+
originalPrice: proposal.originalPrice || 0
|
|
248
|
+
})
|
|
249
|
+
}).then(function(r) { return r.json(); })
|
|
250
|
+
.then(function(session) {
|
|
251
|
+
if (!session.sessionId) return session;
|
|
252
|
+
return fetch(self.serverUrl + '/api/sovereign/negotiation/sessions/' + session.sessionId + '/propose', {
|
|
253
|
+
method: 'POST',
|
|
254
|
+
headers: { 'Content-Type': 'application/json' },
|
|
255
|
+
body: JSON.stringify({
|
|
256
|
+
strategy: proposal.strategy || 'instant_payment',
|
|
257
|
+
proposedDiscount: proposal.proposedDiscount || 5,
|
|
258
|
+
arguments: proposal.arguments || []
|
|
259
|
+
})
|
|
260
|
+
}).then(function(r) { return r.json(); });
|
|
261
|
+
});
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
// Local in-browser negotiation (no server required)
|
|
265
|
+
WABInstance.prototype._handleLocalNegotiation = function(agentId, proposal) {
|
|
266
|
+
var rules = this._negotiationRules;
|
|
267
|
+
var strategy = proposal.strategy || 'custom';
|
|
268
|
+
var rule = rules[strategy];
|
|
269
|
+
if (!rule) {
|
|
270
|
+
return Promise.resolve({
|
|
271
|
+
status: 'rejected',
|
|
272
|
+
reason: 'no_applicable_rule',
|
|
273
|
+
message: 'No rule matches strategy: ' + strategy
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
var requested = proposal.proposedDiscount || 0;
|
|
277
|
+
var maxAllowed = rule.maxDiscount || 20;
|
|
278
|
+
var offered = rule.discount || 0;
|
|
279
|
+
var actual = Math.min(requested, maxAllowed, offered);
|
|
280
|
+
var originalPrice = proposal.originalPrice || 0;
|
|
281
|
+
var finalPrice = Math.round(originalPrice * (1 - actual / 100) * 100) / 100;
|
|
282
|
+
|
|
283
|
+
if (requested <= offered) {
|
|
284
|
+
this._emit('negotiation', { status: 'accepted', agentId: agentId, discount: actual, finalPrice: finalPrice });
|
|
285
|
+
return Promise.resolve({
|
|
286
|
+
status: 'agreed',
|
|
287
|
+
discount: actual,
|
|
288
|
+
originalPrice: originalPrice,
|
|
289
|
+
finalPrice: finalPrice,
|
|
290
|
+
message: 'Deal accepted! ' + actual + '% off. Price: $' + finalPrice
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
// Counter-offer
|
|
294
|
+
this._emit('negotiation', { status: 'counter', agentId: agentId, counterDiscount: offered });
|
|
295
|
+
return Promise.resolve({
|
|
296
|
+
status: 'counter_offer',
|
|
297
|
+
counterDiscount: offered,
|
|
298
|
+
counterPrice: Math.round(originalPrice * (1 - offered / 100) * 100) / 100,
|
|
299
|
+
message: 'We can offer ' + offered + '% off for ' + strategy.replace(/_/g, ' ')
|
|
300
|
+
});
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
// ── Reputation Support ────────────────────────────────────────────
|
|
304
|
+
|
|
305
|
+
WABInstance.prototype.submitAttestation = function(agentId, targetSiteId, interactionType, outcome, extras) {
|
|
306
|
+
if (!this.serverUrl) {
|
|
307
|
+
return Promise.resolve({ error: 'Reputation requires a server URL' });
|
|
308
|
+
}
|
|
309
|
+
var body = {
|
|
310
|
+
siteId: targetSiteId,
|
|
311
|
+
agentId: agentId,
|
|
312
|
+
interactionType: interactionType || 'purchase',
|
|
313
|
+
outcome: outcome || 'success'
|
|
314
|
+
};
|
|
315
|
+
if (extras) {
|
|
316
|
+
if (extras.priceAccuracy != null) body.priceAccuracy = extras.priceAccuracy;
|
|
317
|
+
if (extras.responseTimeMs != null) body.responseTimeMs = extras.responseTimeMs;
|
|
318
|
+
if (extras.dataIntegrity != null) body.dataIntegrity = extras.dataIntegrity;
|
|
319
|
+
if (extras.visionVerified != null) body.visionVerified = extras.visionVerified;
|
|
320
|
+
if (extras.details) body.details = extras.details;
|
|
321
|
+
}
|
|
322
|
+
return fetch(this.serverUrl + '/api/sovereign/reputation/attestations', {
|
|
323
|
+
method: 'POST',
|
|
324
|
+
headers: { 'Content-Type': 'application/json' },
|
|
325
|
+
body: JSON.stringify(body)
|
|
326
|
+
}).then(function(r) { return r.json(); });
|
|
327
|
+
};
|
|
328
|
+
|
|
329
|
+
WABInstance.prototype.getReputation = function(siteId) {
|
|
330
|
+
if (!this.serverUrl) {
|
|
331
|
+
return Promise.resolve({ error: 'Reputation requires a server URL' });
|
|
332
|
+
}
|
|
333
|
+
return fetch(this.serverUrl + '/api/sovereign/reputation/sites/' + encodeURIComponent(siteId))
|
|
334
|
+
.then(function(r) { return r.json(); });
|
|
335
|
+
};
|
|
336
|
+
|
|
337
|
+
// ── Verification Support ──────────────────────────────────────────
|
|
338
|
+
|
|
339
|
+
WABInstance.prototype.verifyPrice = function(opts) {
|
|
340
|
+
if (!this.serverUrl) {
|
|
341
|
+
return Promise.resolve({ error: 'Verification requires a server URL' });
|
|
342
|
+
}
|
|
343
|
+
var body = {
|
|
344
|
+
siteId: opts.siteId || this.name,
|
|
345
|
+
agentId: opts.agentId || null,
|
|
346
|
+
url: opts.url || null,
|
|
347
|
+
domValue: opts.domValue || opts.domPrice,
|
|
348
|
+
visionValue: opts.visionValue || opts.visionPrice,
|
|
349
|
+
category: opts.category || null,
|
|
350
|
+
itemName: opts.itemName || null
|
|
351
|
+
};
|
|
352
|
+
return fetch(this.serverUrl + '/api/sovereign/verify/price', {
|
|
353
|
+
method: 'POST',
|
|
354
|
+
headers: { 'Content-Type': 'application/json' },
|
|
355
|
+
body: JSON.stringify(body)
|
|
356
|
+
}).then(function(r) { return r.json(); });
|
|
357
|
+
};
|
|
358
|
+
|
|
359
|
+
// ── Agent Mesh Protocol ───────────────────────────────────────────
|
|
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
|
+
|
|
368
|
+
WABInstance.prototype.meshJoin = function(role, displayName, capabilities) {
|
|
369
|
+
if (!this.serverUrl) return Promise.reject(new Error('Mesh requires a server URL'));
|
|
370
|
+
var self = this;
|
|
371
|
+
return _meshReq(this.serverUrl + '/api/mesh/agents', {
|
|
372
|
+
method: 'POST',
|
|
373
|
+
headers: { 'Content-Type': 'application/json' },
|
|
374
|
+
body: JSON.stringify({ siteId: this.name, role: role, displayName: displayName, capabilities: capabilities })
|
|
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;
|
|
382
|
+
});
|
|
383
|
+
};
|
|
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
|
+
|
|
393
|
+
WABInstance.prototype.meshPublish = function(channel, messageType, subject, payload, opts) {
|
|
394
|
+
if (!this.serverUrl || !this._meshAgentId) return Promise.reject(new Error('Must join mesh first'));
|
|
395
|
+
return _meshReq(this.serverUrl + '/api/mesh/messages', {
|
|
396
|
+
method: 'POST',
|
|
397
|
+
headers: { 'Content-Type': 'application/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; });
|
|
400
|
+
};
|
|
401
|
+
|
|
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');
|
|
416
|
+
};
|
|
417
|
+
|
|
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', {
|
|
422
|
+
method: 'POST',
|
|
423
|
+
headers: { 'Content-Type': 'application/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; });
|
|
438
|
+
};
|
|
439
|
+
|
|
440
|
+
WABInstance.prototype.meshAlert = function(subject, details, priority) {
|
|
441
|
+
if (!this.serverUrl || !this._meshAgentId) return Promise.reject(new Error('Must join mesh first'));
|
|
442
|
+
return _meshReq(this.serverUrl + '/api/mesh/alert', {
|
|
443
|
+
method: 'POST',
|
|
444
|
+
headers: { 'Content-Type': 'application/json' },
|
|
445
|
+
body: JSON.stringify({ senderId: this._meshAgentId, subject: subject, details: details, priority: priority })
|
|
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; });
|
|
471
|
+
};
|
|
472
|
+
|
|
473
|
+
// ── Agent Symphony Orchestrator ───────────────────────────────────
|
|
474
|
+
|
|
475
|
+
WABInstance.prototype.symphonyPerform = function(template, inputData, schema) {
|
|
476
|
+
if (!this.serverUrl) return Promise.reject(new Error('Symphony requires a server URL'));
|
|
477
|
+
var self = this;
|
|
478
|
+
return _meshReq(this.serverUrl + '/api/mesh/symphony/compose', {
|
|
479
|
+
method: 'POST',
|
|
480
|
+
headers: { 'Content-Type': 'application/json' },
|
|
481
|
+
body: JSON.stringify({ siteId: this.name, template: template, inputData: inputData, schema: schema })
|
|
482
|
+
}).then(function(data) {
|
|
483
|
+
self._emit('symphony:completed', data);
|
|
484
|
+
return data;
|
|
485
|
+
});
|
|
486
|
+
};
|
|
487
|
+
|
|
488
|
+
// ── Agent Learning Engine ─────────────────────────────────────────
|
|
489
|
+
|
|
490
|
+
WABInstance.prototype.learnRecord = function(domain, action, context, features) {
|
|
491
|
+
if (!this.serverUrl || !this._meshAgentId) return Promise.reject(new Error('Must join mesh first'));
|
|
492
|
+
return _meshReq(this.serverUrl + '/api/mesh/learning/decisions', {
|
|
493
|
+
method: 'POST',
|
|
494
|
+
headers: { 'Content-Type': 'application/json' },
|
|
495
|
+
body: JSON.stringify({ siteId: this.name, agentId: this._meshAgentId, domain: domain, action: action, context: context, features: features })
|
|
496
|
+
});
|
|
497
|
+
};
|
|
498
|
+
|
|
499
|
+
WABInstance.prototype.learnFeedback = function(decisionId, outcome, reward) {
|
|
500
|
+
if (!this.serverUrl) return Promise.reject(new Error('Learning requires a server URL'));
|
|
501
|
+
return _meshReq(this.serverUrl + '/api/mesh/learning/feedback', {
|
|
502
|
+
method: 'POST',
|
|
503
|
+
headers: { 'Content-Type': 'application/json' },
|
|
504
|
+
body: JSON.stringify({ decisionId: decisionId, outcome: outcome, reward: reward })
|
|
505
|
+
});
|
|
506
|
+
};
|
|
507
|
+
|
|
508
|
+
WABInstance.prototype.learnRecommend = function(domain, actions, context) {
|
|
509
|
+
if (!this.serverUrl || !this._meshAgentId) return Promise.reject(new Error('Must join mesh first'));
|
|
510
|
+
return _meshReq(this.serverUrl + '/api/mesh/learning/recommend', {
|
|
511
|
+
method: 'POST',
|
|
512
|
+
headers: { 'Content-Type': 'application/json' },
|
|
513
|
+
body: JSON.stringify({ siteId: this.name, agentId: this._meshAgentId, domain: domain, actions: actions, context: context })
|
|
514
|
+
});
|
|
515
|
+
};
|
|
516
|
+
|
|
517
|
+
// ── Static API ────────────────────────────────────────────────────
|
|
518
|
+
|
|
519
|
+
var WAB = {
|
|
520
|
+
version: VERSION,
|
|
521
|
+
|
|
522
|
+
init: function(config) {
|
|
523
|
+
var instance = new WABInstance(config || {});
|
|
524
|
+
WAB._instance = instance;
|
|
525
|
+
return instance;
|
|
526
|
+
},
|
|
527
|
+
|
|
528
|
+
connect: function(serverUrl) {
|
|
529
|
+
return new WABInstance({ name: 'WAB Client', serverUrl: serverUrl });
|
|
530
|
+
},
|
|
531
|
+
|
|
532
|
+
discover: function() {
|
|
533
|
+
if (WAB._instance) return WAB._instance.discover();
|
|
534
|
+
return Promise.reject(new Error('WAB not initialized. Call WAB.init() first.'));
|
|
535
|
+
},
|
|
536
|
+
|
|
537
|
+
execute: function(actionName, params) {
|
|
538
|
+
if (WAB._instance) return WAB._instance.execute(actionName, params);
|
|
539
|
+
return Promise.reject(new Error('WAB not initialized. Call WAB.init() first.'));
|
|
540
|
+
},
|
|
541
|
+
|
|
542
|
+
negotiate: function(agentId, proposal) {
|
|
543
|
+
if (WAB._instance) return WAB._instance.negotiate(agentId, proposal);
|
|
544
|
+
return Promise.reject(new Error('WAB not initialized'));
|
|
545
|
+
},
|
|
546
|
+
|
|
547
|
+
getReputation: function(siteId) {
|
|
548
|
+
if (WAB._instance) return WAB._instance.getReputation(siteId);
|
|
549
|
+
return Promise.reject(new Error('WAB not initialized'));
|
|
550
|
+
},
|
|
551
|
+
|
|
552
|
+
verifyPrice: function(opts) {
|
|
553
|
+
if (WAB._instance) return WAB._instance.verifyPrice(opts);
|
|
554
|
+
return Promise.reject(new Error('WAB not initialized'));
|
|
555
|
+
},
|
|
556
|
+
|
|
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')); },
|
|
574
|
+
|
|
575
|
+
_instance: null
|
|
576
|
+
};
|
|
577
|
+
|
|
578
|
+
global.WAB = WAB;
|
|
579
|
+
|
|
580
|
+
})(typeof window !== 'undefined' ? window : this);
|