web-agent-bridge 3.12.0 → 3.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Anthropic Claude tool-use example agent that uses the WAB system prompt
|
|
5
|
+
* and WABLiveTool to safely interact with a third-party site.
|
|
6
|
+
*
|
|
7
|
+
* Prerequisites:
|
|
8
|
+
* npm install @anthropic-ai/sdk web-agent-bridge
|
|
9
|
+
* export ANTHROPIC_API_KEY=sk-ant-...
|
|
10
|
+
*
|
|
11
|
+
* Run:
|
|
12
|
+
* node examples/anthropic-tools-agent.js "Place an order on shop.example.com"
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
const Anthropic = require('@anthropic-ai/sdk');
|
|
16
|
+
const { systemPrompt, WABLiveTool } = require('web-agent-bridge');
|
|
17
|
+
|
|
18
|
+
const client = new Anthropic();
|
|
19
|
+
const tool = new WABLiveTool({ agentName: 'wab-anthropic-demo/1' });
|
|
20
|
+
|
|
21
|
+
const CLAUDE_TOOL_SCHEMA = [{
|
|
22
|
+
name: tool.name,
|
|
23
|
+
description: tool.description,
|
|
24
|
+
input_schema: tool.schema
|
|
25
|
+
}];
|
|
26
|
+
|
|
27
|
+
async function run(userTask) {
|
|
28
|
+
const messages = [{ role: 'user', content: userTask }];
|
|
29
|
+
const system = systemPrompt({ agentName: 'wab-anthropic-demo', agentVersion: '1.0' });
|
|
30
|
+
|
|
31
|
+
for (let step = 0; step < 6; step++) {
|
|
32
|
+
const reply = await client.messages.create({
|
|
33
|
+
model: 'claude-3-5-sonnet-latest',
|
|
34
|
+
max_tokens: 1024,
|
|
35
|
+
system,
|
|
36
|
+
tools: CLAUDE_TOOL_SCHEMA,
|
|
37
|
+
messages
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
messages.push({ role: 'assistant', content: reply.content });
|
|
41
|
+
|
|
42
|
+
if (reply.stop_reason !== 'tool_use') {
|
|
43
|
+
const text = reply.content.filter(b => b.type === 'text').map(b => b.text).join('\n');
|
|
44
|
+
console.log('\n=== Final answer ===\n' + text);
|
|
45
|
+
return text;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const toolUses = reply.content.filter(b => b.type === 'tool_use');
|
|
49
|
+
const results = [];
|
|
50
|
+
for (const tu of toolUses) {
|
|
51
|
+
console.log(`→ tool call: ${tu.name} ${JSON.stringify(tu.input)}`);
|
|
52
|
+
const out = await (tool.invoke ? tool.invoke(tu.input) : tool._call(tu.input));
|
|
53
|
+
console.log('← tool result:', String(out).slice(0, 280));
|
|
54
|
+
results.push({ type: 'tool_result', tool_use_id: tu.id, content: typeof out === 'string' ? out : JSON.stringify(out) });
|
|
55
|
+
}
|
|
56
|
+
messages.push({ role: 'user', content: results });
|
|
57
|
+
}
|
|
58
|
+
console.warn('Max steps reached without a final answer.');
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (require.main === module) {
|
|
62
|
+
const task = process.argv.slice(2).join(' ') || 'Search shop.example.com for olive oil';
|
|
63
|
+
run(task).catch((e) => { console.error(e); process.exit(1); });
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
module.exports = { run };
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* OpenAI tool-calling example agent that uses the WAB system prompt
|
|
5
|
+
* and WABLiveTool to safely interact with a third-party site.
|
|
6
|
+
*
|
|
7
|
+
* Prerequisites:
|
|
8
|
+
* npm install openai web-agent-bridge
|
|
9
|
+
* export OPENAI_API_KEY=sk-...
|
|
10
|
+
*
|
|
11
|
+
* Run:
|
|
12
|
+
* node examples/openai-tools-agent.js "Search shop.example.com for olive oil"
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
const OpenAI = require('openai');
|
|
16
|
+
const { systemPrompt, WABLiveTool } = require('web-agent-bridge');
|
|
17
|
+
|
|
18
|
+
const client = new OpenAI();
|
|
19
|
+
const tool = new WABLiveTool({ agentName: 'wab-openai-demo/1' });
|
|
20
|
+
|
|
21
|
+
const OPENAI_TOOL_SCHEMA = [{
|
|
22
|
+
type: 'function',
|
|
23
|
+
function: {
|
|
24
|
+
name: tool.name,
|
|
25
|
+
description: tool.description,
|
|
26
|
+
parameters: tool.schema
|
|
27
|
+
}
|
|
28
|
+
}];
|
|
29
|
+
|
|
30
|
+
async function run(userTask) {
|
|
31
|
+
const messages = [
|
|
32
|
+
{ role: 'system', content: systemPrompt({ agentName: 'wab-openai-demo', agentVersion: '1.0' }) },
|
|
33
|
+
{ role: 'user', content: userTask }
|
|
34
|
+
];
|
|
35
|
+
|
|
36
|
+
for (let step = 0; step < 6; step++) {
|
|
37
|
+
const reply = await client.chat.completions.create({
|
|
38
|
+
model: 'gpt-4o-mini',
|
|
39
|
+
messages,
|
|
40
|
+
tools: OPENAI_TOOL_SCHEMA,
|
|
41
|
+
tool_choice: 'auto'
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
const msg = reply.choices[0].message;
|
|
45
|
+
messages.push(msg);
|
|
46
|
+
|
|
47
|
+
if (!msg.tool_calls || msg.tool_calls.length === 0) {
|
|
48
|
+
console.log('\n=== Final answer ===\n' + msg.content);
|
|
49
|
+
return msg.content;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
for (const tc of msg.tool_calls) {
|
|
53
|
+
let args = {};
|
|
54
|
+
try { args = JSON.parse(tc.function.arguments || '{}'); } catch (_) { /* ignore */ }
|
|
55
|
+
console.log(`→ tool call: ${tc.function.name} ${JSON.stringify(args)}`);
|
|
56
|
+
const out = await (tool.invoke ? tool.invoke(args) : tool._call(args));
|
|
57
|
+
console.log('← tool result:', String(out).slice(0, 280));
|
|
58
|
+
messages.push({ role: 'tool', tool_call_id: tc.id, content: typeof out === 'string' ? out : JSON.stringify(out) });
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
console.warn('Max steps reached without a final answer.');
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (require.main === module) {
|
|
65
|
+
const task = process.argv.slice(2).join(' ') || 'Search shop.example.com for olive oil';
|
|
66
|
+
run(task).catch((e) => { console.error(e); process.exit(1); });
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
module.exports = { run };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "web-agent-bridge",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.14.0",
|
|
4
4
|
"description": "Agent Transaction Bridge — the trust + transaction layer for agentic commerce. Signed intent contracts, idempotent transactions, Ed25519-verifiable receipts, explicit compensation. Plus the original WAB stack: sovereign browser, ShieldQR, SSL health, DNS discovery, agent mesh, and unified gateway for safe AI–website interaction.",
|
|
5
5
|
"author": "Web Agent Bridge <dev@webagentbridge.com>",
|
|
6
6
|
"main": "server/index.js",
|
package/server/index.js
CHANGED
|
@@ -323,6 +323,10 @@ app.use('/api/revocations', apiLimiter, require('./routes/revocations'));
|
|
|
323
323
|
// ── Agent-Driven Adoption v3.12.0 — canonical LLM agent system prompt ──
|
|
324
324
|
app.use('/api/agent', apiLimiter, require('./routes/agent-prompt'));
|
|
325
325
|
|
|
326
|
+
// ── Network Effect v3.14.0 — trusted-domains snapshot + revocations feeds ──
|
|
327
|
+
// (apiLimiter already applies via /api mount above; do not stack it here.)
|
|
328
|
+
app.use('/api', require('./routes/network'));
|
|
329
|
+
|
|
326
330
|
// ── WAB Commercial Foundations v3.8.0 (Partners · Trust Graph API · Governance SaaS · Enterprise Mesh) ──
|
|
327
331
|
app.use('/api/partners', apiLimiter, require('./routes/partners'));
|
|
328
332
|
app.use('/api/keys', apiLimiter, require('./routes/api-keys'));
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Network-effect public endpoints (v3.14.0).
|
|
5
|
+
*
|
|
6
|
+
* GET /api/trusted-domains.json — snapshot of currently-attested,
|
|
7
|
+
* non-revoked WAB sites. Cached 1 hour. Designed for agent bootstrap
|
|
8
|
+
* and third-party crawlers building "verified web" indexes.
|
|
9
|
+
* GET /api/trusted-domains.txt — same data, newline-separated domains.
|
|
10
|
+
* GET /api/revocations/feed.json — JSON Feed 1.1 of the transparency log.
|
|
11
|
+
* GET /api/revocations/feed.xml — Atom 1.0 of the transparency log.
|
|
12
|
+
*
|
|
13
|
+
* Mounted at /api in server/index.js.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
const express = require('express');
|
|
17
|
+
const router = express.Router();
|
|
18
|
+
const { db } = require('../models/db');
|
|
19
|
+
|
|
20
|
+
const SNAPSHOT_TTL_MS = 60 * 60 * 1000; // 1h
|
|
21
|
+
let _snapshotCache = { ts: 0, data: null };
|
|
22
|
+
|
|
23
|
+
function buildSnapshot() {
|
|
24
|
+
// Active sites that have no active blocking revocation.
|
|
25
|
+
let rows = [];
|
|
26
|
+
try {
|
|
27
|
+
rows = db.prepare(`
|
|
28
|
+
SELECT s.id, s.domain, s.name, s.description, s.tier, s.created_at
|
|
29
|
+
FROM sites s
|
|
30
|
+
WHERE s.active = 1
|
|
31
|
+
AND NOT EXISTS (
|
|
32
|
+
SELECT 1 FROM site_revocations r
|
|
33
|
+
WHERE r.site_id = s.id
|
|
34
|
+
AND r.status IN ('pending_appeal', 'appealed', 'final')
|
|
35
|
+
AND r.type IN ('suspended', 'revoked')
|
|
36
|
+
)
|
|
37
|
+
ORDER BY s.created_at ASC
|
|
38
|
+
`).all();
|
|
39
|
+
} catch (_) {
|
|
40
|
+
// site_revocations may not yet exist on a very first boot
|
|
41
|
+
rows = db.prepare(`
|
|
42
|
+
SELECT id, domain, name, description, tier, created_at
|
|
43
|
+
FROM sites WHERE active = 1 ORDER BY created_at ASC
|
|
44
|
+
`).all();
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return {
|
|
48
|
+
schema: 'wab-trusted-domains/v1',
|
|
49
|
+
generated_at: new Date().toISOString(),
|
|
50
|
+
total: rows.length,
|
|
51
|
+
domains: rows.map(r => ({
|
|
52
|
+
domain: r.domain,
|
|
53
|
+
name: r.name,
|
|
54
|
+
tier: r.tier || 'free',
|
|
55
|
+
registered_at: r.created_at,
|
|
56
|
+
discovery_url: 'https://' + r.domain + '/.well-known/wab.json',
|
|
57
|
+
badge_url: 'https://webagentbridge.com/api/discovery/badge/' + r.domain + '.svg'
|
|
58
|
+
}))
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function getSnapshot() {
|
|
63
|
+
const now = Date.now();
|
|
64
|
+
if (_snapshotCache.data && (now - _snapshotCache.ts) < SNAPSHOT_TTL_MS) {
|
|
65
|
+
return _snapshotCache.data;
|
|
66
|
+
}
|
|
67
|
+
const snap = buildSnapshot();
|
|
68
|
+
_snapshotCache = { ts: now, data: snap };
|
|
69
|
+
return snap;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
router.get('/trusted-domains.json', (req, res) => {
|
|
73
|
+
const snap = getSnapshot();
|
|
74
|
+
res.set('Cache-Control', 'public, max-age=3600, s-maxage=3600');
|
|
75
|
+
res.set('X-WAB-Snapshot-Schema', snap.schema);
|
|
76
|
+
res.json(snap);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
router.get('/trusted-domains.txt', (req, res) => {
|
|
80
|
+
const snap = getSnapshot();
|
|
81
|
+
res.set('Cache-Control', 'public, max-age=3600, s-maxage=3600');
|
|
82
|
+
res.type('text/plain; charset=utf-8');
|
|
83
|
+
res.send(snap.domains.map(d => d.domain).join('\n') + '\n');
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
// ── Revocation feeds ─────────────────────────────────────────────────
|
|
87
|
+
|
|
88
|
+
function listRecentRevocations(limit) {
|
|
89
|
+
try {
|
|
90
|
+
return db.prepare(`
|
|
91
|
+
SELECT id, domain, type, reason_code, reason_text,
|
|
92
|
+
decided_at, appeal_deadline, status, updated_at
|
|
93
|
+
FROM site_revocations
|
|
94
|
+
WHERE type IN ('suspended', 'revoked')
|
|
95
|
+
ORDER BY decided_at DESC
|
|
96
|
+
LIMIT ?
|
|
97
|
+
`).all(limit);
|
|
98
|
+
} catch (_) { return []; }
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function escapeXml(s) {
|
|
102
|
+
return String(s == null ? '' : s)
|
|
103
|
+
.replace(/&/g, '&')
|
|
104
|
+
.replace(/</g, '<')
|
|
105
|
+
.replace(/>/g, '>')
|
|
106
|
+
.replace(/"/g, '"')
|
|
107
|
+
.replace(/'/g, ''');
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
router.get('/transparency/feed.json', (req, res) => {
|
|
111
|
+
const limit = Math.min(parseInt(req.query.limit, 10) || 50, 200);
|
|
112
|
+
const items = listRecentRevocations(limit);
|
|
113
|
+
res.set('Cache-Control', 'public, max-age=300, s-maxage=300');
|
|
114
|
+
res.json({
|
|
115
|
+
version: 'https://jsonfeed.org/version/1.1',
|
|
116
|
+
title: 'Web Agent Bridge — Revocations Transparency Log',
|
|
117
|
+
home_page_url: 'https://webagentbridge.com/revocations.html',
|
|
118
|
+
feed_url: 'https://webagentbridge.com/api/transparency/feed.json',
|
|
119
|
+
description: 'Live feed of WAB site revocations and suspensions.',
|
|
120
|
+
language: 'en',
|
|
121
|
+
items: items.map(r => ({
|
|
122
|
+
id: r.id,
|
|
123
|
+
url: 'https://webagentbridge.com/revocations.html#' + r.id,
|
|
124
|
+
title: '[' + r.type.toUpperCase() + '] ' + r.domain + ' — ' + (r.reason_code || 'unknown'),
|
|
125
|
+
content_text: (r.reason_text || '') +
|
|
126
|
+
(r.appeal_deadline ? '\nAppeal deadline: ' + r.appeal_deadline : '') +
|
|
127
|
+
'\nStatus: ' + r.status,
|
|
128
|
+
date_published: r.decided_at,
|
|
129
|
+
date_modified: r.updated_at || r.decided_at,
|
|
130
|
+
tags: [r.type, r.reason_code, r.status].filter(Boolean),
|
|
131
|
+
_wab: {
|
|
132
|
+
domain: r.domain,
|
|
133
|
+
type: r.type,
|
|
134
|
+
reason_code: r.reason_code,
|
|
135
|
+
status: r.status,
|
|
136
|
+
appeal_deadline: r.appeal_deadline
|
|
137
|
+
}
|
|
138
|
+
}))
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
router.get('/transparency/feed.xml', (req, res) => {
|
|
143
|
+
const limit = Math.min(parseInt(req.query.limit, 10) || 50, 200);
|
|
144
|
+
const items = listRecentRevocations(limit);
|
|
145
|
+
const updated = items[0] && items[0].decided_at
|
|
146
|
+
? new Date(items[0].decided_at).toISOString()
|
|
147
|
+
: new Date().toISOString();
|
|
148
|
+
|
|
149
|
+
const entries = items.map(r => {
|
|
150
|
+
const url = 'https://webagentbridge.com/revocations.html#' + r.id;
|
|
151
|
+
const published = new Date(r.decided_at).toISOString();
|
|
152
|
+
const mod = new Date(r.updated_at || r.decided_at).toISOString();
|
|
153
|
+
const title = '[' + r.type.toUpperCase() + '] ' + r.domain + ' — ' + (r.reason_code || 'unknown');
|
|
154
|
+
const summary = (r.reason_text || '') +
|
|
155
|
+
(r.appeal_deadline ? ' Appeal deadline: ' + r.appeal_deadline + '.' : '') +
|
|
156
|
+
' Status: ' + r.status + '.';
|
|
157
|
+
return [
|
|
158
|
+
' <entry>',
|
|
159
|
+
' <id>tag:webagentbridge.com,2026:' + r.id + '</id>',
|
|
160
|
+
' <title>' + escapeXml(title) + '</title>',
|
|
161
|
+
' <link rel="alternate" href="' + url + '"/>',
|
|
162
|
+
' <published>' + published + '</published>',
|
|
163
|
+
' <updated>' + mod + '</updated>',
|
|
164
|
+
' <category term="' + escapeXml(r.type) + '"/>',
|
|
165
|
+
' <category term="' + escapeXml(r.reason_code || '') + '"/>',
|
|
166
|
+
' <summary>' + escapeXml(summary) + '</summary>',
|
|
167
|
+
' </entry>'
|
|
168
|
+
].join('\n');
|
|
169
|
+
}).join('\n');
|
|
170
|
+
|
|
171
|
+
const xml = '<?xml version="1.0" encoding="utf-8"?>\n' +
|
|
172
|
+
'<feed xmlns="http://www.w3.org/2005/Atom">\n' +
|
|
173
|
+
' <id>https://webagentbridge.com/api/transparency/feed.xml</id>\n' +
|
|
174
|
+
' <title>Web Agent Bridge — Revocations Transparency Log</title>\n' +
|
|
175
|
+
' <updated>' + updated + '</updated>\n' +
|
|
176
|
+
' <link rel="self" href="https://webagentbridge.com/api/transparency/feed.xml"/>\n' +
|
|
177
|
+
' <link rel="alternate" href="https://webagentbridge.com/revocations.html"/>\n' +
|
|
178
|
+
(entries ? entries + '\n' : '') +
|
|
179
|
+
'</feed>\n';
|
|
180
|
+
|
|
181
|
+
res.set('Cache-Control', 'public, max-age=300, s-maxage=300');
|
|
182
|
+
res.type('application/atom+xml; charset=utf-8');
|
|
183
|
+
res.send(xml);
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
module.exports = router;
|
|
187
|
+
module.exports._buildSnapshot = buildSnapshot;
|
|
188
|
+
module.exports._listRecentRevocations = listRecentRevocations;
|
|
189
|
+
module.exports.__resetCache = function () { _snapshotCache = { ts: 0, data: null }; };
|