web-agent-bridge 2.0.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.ar.md +126 -0
- package/README.md +189 -1
- package/bin/agent-runner.js +465 -0
- package/bin/cli.js +58 -0
- package/package.json +4 -2
- package/public/index.html +86 -0
- package/public/script/wab.min.js +172 -1
- package/public/sovereign.html +660 -0
- package/sdk/package.json +1 -1
- package/server/index.js +2 -0
- package/server/routes/sovereign.js +307 -0
- package/server/services/negotiation.js +439 -0
- package/server/services/reputation.js +465 -0
- package/server/services/verification.js +481 -0
- package/templates/artisan-marketplace.yaml +104 -0
- package/templates/book-price-scout.yaml +98 -0
- package/templates/electronics-price-tracker.yaml +108 -0
- package/templates/flight-deal-hunter.yaml +113 -0
- package/templates/freelancer-direct.yaml +116 -0
- package/templates/grocery-price-compare.yaml +93 -0
- package/templates/hotel-direct-booking.yaml +113 -0
- package/templates/local-services.yaml +98 -0
- package/templates/olive-oil-tunisia.yaml +88 -0
- package/templates/organic-farm-fresh.yaml +101 -0
- package/templates/restaurant-direct.yaml +97 -0
package/public/script/wab.min.js
CHANGED
|
@@ -72,8 +72,17 @@
|
|
|
72
72
|
transport: ['javascript', 'http'],
|
|
73
73
|
ready: true,
|
|
74
74
|
discover: function() { return self.discover(); },
|
|
75
|
-
execute: function(name, params) { return self.execute(name, params); }
|
|
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); }
|
|
76
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); };
|
|
77
86
|
};
|
|
78
87
|
|
|
79
88
|
// Cross-frame communication for embedded demos
|
|
@@ -97,6 +106,12 @@
|
|
|
97
106
|
e.source.postMessage({ source: 'wab-store', type: 'execute-response', action: e.data.action, detail: result }, '*');
|
|
98
107
|
});
|
|
99
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
|
+
}
|
|
100
115
|
});
|
|
101
116
|
|
|
102
117
|
// Notify parent that store is ready
|
|
@@ -200,6 +215,147 @@
|
|
|
200
215
|
return this._auditLog.slice();
|
|
201
216
|
};
|
|
202
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
|
+
|
|
203
359
|
// ── Static API ────────────────────────────────────────────────────
|
|
204
360
|
|
|
205
361
|
var WAB = {
|
|
@@ -226,6 +382,21 @@
|
|
|
226
382
|
return Promise.resolve({ error: 'WAB not initialized. Call WAB.init() first.' });
|
|
227
383
|
},
|
|
228
384
|
|
|
385
|
+
negotiate: function(agentId, proposal) {
|
|
386
|
+
if (WAB._instance) return WAB._instance.negotiate(agentId, proposal);
|
|
387
|
+
return Promise.resolve({ error: 'WAB not initialized' });
|
|
388
|
+
},
|
|
389
|
+
|
|
390
|
+
getReputation: function(siteId) {
|
|
391
|
+
if (WAB._instance) return WAB._instance.getReputation(siteId);
|
|
392
|
+
return Promise.resolve({ error: 'WAB not initialized' });
|
|
393
|
+
},
|
|
394
|
+
|
|
395
|
+
verifyPrice: function(opts) {
|
|
396
|
+
if (WAB._instance) return WAB._instance.verifyPrice(opts);
|
|
397
|
+
return Promise.resolve({ error: 'WAB not initialized' });
|
|
398
|
+
},
|
|
399
|
+
|
|
229
400
|
_instance: null
|
|
230
401
|
};
|
|
231
402
|
|