clawpowers 1.1.2 → 1.1.3
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.md +44 -2
- package/bin/clawpowers.js +27 -11
- package/docs/quickstart-first-transaction.md +204 -0
- package/package.json +1 -1
- package/runtime/demo/README.md +78 -0
- package/runtime/payments/pipeline.js +459 -0
- package/skill.json +26 -5
- package/skills/agent-bounties/SKILL.md +553 -0
- package/skills/prospecting/SKILL.md +141 -0
- package/skills/using-clawpowers/SKILL.md +6 -1
|
@@ -0,0 +1,553 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: agent-bounties
|
|
3
|
+
description: Agent-to-agent task bounties with mutual-stake escrow. Post tasks with rewards, accept and stake collateral, execute, verify, and release payment — all on-chain. Integrates MutualStakeEscrow from agentwallet-sdk and verification-before-completion for automated acceptance checks.
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
requires:
|
|
6
|
+
tools: [bash, node, curl]
|
|
7
|
+
env: [AGENT_PRIVATE_KEY, AGENT_WALLET_ADDRESS]
|
|
8
|
+
runtime: true
|
|
9
|
+
metrics:
|
|
10
|
+
tracks: [bounties_posted, bounties_accepted, bounties_completed, bounties_disputed, escrow_value_usd]
|
|
11
|
+
improves: [task_specification_clarity, acceptance_criteria_precision, economic_efficiency]
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# Agent Bounties + Escrow
|
|
15
|
+
|
|
16
|
+
## When to Use
|
|
17
|
+
|
|
18
|
+
Apply this skill when:
|
|
19
|
+
|
|
20
|
+
- **Hiring a specialist agent** — you need a task done that requires a different
|
|
21
|
+
skill set (security audit, code optimization, data enrichment)
|
|
22
|
+
- **Incentivizing quality** — you want the counterparty to have skin in the game
|
|
23
|
+
before accepting work
|
|
24
|
+
- **Coordinating across autonomous agents** — multi-agent pipelines where
|
|
25
|
+
intermediate results must be verified before proceeding
|
|
26
|
+
- **Preventing race conditions** — multiple agents competing for the same task;
|
|
27
|
+
first-accept-and-stake locks the work item
|
|
28
|
+
- **Building a verifiable service record** — bounty completion history feeds
|
|
29
|
+
the ERC-8004 reputation registry
|
|
30
|
+
|
|
31
|
+
**Skip when:**
|
|
32
|
+
|
|
33
|
+
- Task cost < $0.10 (gas overhead exceeds value — use a synchronous call instead)
|
|
34
|
+
- Acceptance criteria cannot be automated (purely subjective creative work)
|
|
35
|
+
- Both sides are the same agent process (no adversarial benefit from escrow)
|
|
36
|
+
- Deadline < 60 seconds from now (on-chain settlement lag)
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## Core Methodology
|
|
41
|
+
|
|
42
|
+
### Lifecycle Overview
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
Agent A (Buyer) On-Chain Escrow Agent B (Seller)
|
|
46
|
+
───────────────── ──────────────── ─────────────────
|
|
47
|
+
1. postBounty() ──► StakeVaultFactory.createEscrow()
|
|
48
|
+
(define task, reward, vaultAddress returned
|
|
49
|
+
deadline, criteria)
|
|
50
|
+
|
|
51
|
+
2. Broadcast bounty JSONL ──► ~/.clawpowers/state/bounties/
|
|
52
|
+
|
|
53
|
+
◄── 3. discoverBounties()
|
|
54
|
+
(scan JSONL, pick task)
|
|
55
|
+
|
|
56
|
+
──► 4. acceptBounty()
|
|
57
|
+
StakeVault.fund()
|
|
58
|
+
StakeVault.accept()
|
|
59
|
+
(stakes seller collateral)
|
|
60
|
+
|
|
61
|
+
──► 5. execute()
|
|
62
|
+
(uses ClawPowers skills,
|
|
63
|
+
submits deliverables)
|
|
64
|
+
|
|
65
|
+
6. verify() ◄── verification-before-completion ◄── 6. fulfill(proof)
|
|
66
|
+
(automated checks) skill runs acceptance tests
|
|
67
|
+
|
|
68
|
+
╔══ PASS ══╗ ╔══ FAIL ══╗
|
|
69
|
+
▼ ▼ ▼ ▼
|
|
70
|
+
7a. StakeVault Auto-release 7b. Buyer disputes /
|
|
71
|
+
.verify() reward → B StakeVault.challenge()
|
|
72
|
+
stake returned Arbiter resolves
|
|
73
|
+
to A and B or deadline expires
|
|
74
|
+
→ reclaimExpired()
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Phase 1: Post a Bounty
|
|
78
|
+
|
|
79
|
+
Agent A defines the task, reward, deadline, and machine-checkable acceptance criteria.
|
|
80
|
+
|
|
81
|
+
**Step 1a — Write the bounty to disk**
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
BOUNTY_ID="bounty-$(date +%s)-$(head -c 4 /dev/urandom | xxd -p)"
|
|
85
|
+
BOUNTIES_DIR="$HOME/.clawpowers/state/bounties"
|
|
86
|
+
mkdir -p "$BOUNTIES_DIR"
|
|
87
|
+
|
|
88
|
+
cat >> "$BOUNTIES_DIR/open.jsonl" << EOF
|
|
89
|
+
{"id":"$BOUNTY_ID","posted_at":"$(date -u +%Y-%m-%dT%H:%M:%SZ)","status":"open","skill_required":"security-audit","title":"Audit authentication module for OWASP Top 10","description":"Run a complete security audit of ./src/auth/ against OWASP Top 10 checklist. Deliverable: JSON report at /tmp/audit-report.json","acceptance_criteria":{"type":"json_schema","path":"/tmp/audit-report.json","required_fields":["issues","severity_counts","owasp_coverage_pct"],"min_coverage":80},"reward_usdc":"0.50","buyer_stake_usdc":"0.25","seller_stake_usdc":"0.25","deadline_hours":2,"chain":"base","vault_address":null}
|
|
90
|
+
EOF
|
|
91
|
+
|
|
92
|
+
echo "Bounty posted: $BOUNTY_ID"
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
**Step 1b — Deploy the escrow vault (JavaScript)**
|
|
96
|
+
|
|
97
|
+
```javascript
|
|
98
|
+
import { walletFromEnv } from 'agentwallet-sdk';
|
|
99
|
+
import { MutualStakeEscrow } from 'agentwallet-sdk';
|
|
100
|
+
import { parseUnits } from 'viem';
|
|
101
|
+
|
|
102
|
+
const wallet = walletFromEnv();
|
|
103
|
+
const escrow = new MutualStakeEscrow(wallet);
|
|
104
|
+
|
|
105
|
+
// Post bounty: buyer funds payment + own stake
|
|
106
|
+
const { vaultAddress, txHash } = await escrow.createEscrow({
|
|
107
|
+
seller: '0xSELLER_AGENT_ADDRESS', // Agent B's wallet address
|
|
108
|
+
token: '0x036CbD53842c5426634e7929541eC2318f3dCF7e', // USDC on base-sepolia
|
|
109
|
+
paymentAmount: parseUnits('0.50', 6), // reward for completing the task
|
|
110
|
+
buyerStake: parseUnits('0.25', 6), // buyer's skin-in-the-game
|
|
111
|
+
sellerStake: parseUnits('0.25', 6), // seller must match this to accept
|
|
112
|
+
deadline: Math.floor(Date.now() / 1000) + 7200, // 2 hours from now
|
|
113
|
+
challengeWindow: 300, // 5-minute dispute window
|
|
114
|
+
verifier: 'optimistic', // use optimistic verification
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
console.log(`Vault deployed: ${vaultAddress} (tx: ${txHash})`);
|
|
118
|
+
|
|
119
|
+
// Update the JSONL record with the vault address
|
|
120
|
+
// (use bash: sed -i or jq to patch the line matching bounty ID)
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
**Step 1c — Broadcast (optional)**
|
|
124
|
+
|
|
125
|
+
For multi-agent systems, share the open.jsonl via a known shared path or
|
|
126
|
+
publish to the ERC-8004 identity registry's service endpoint.
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
### Phase 2: Accept & Stake
|
|
131
|
+
|
|
132
|
+
Agent B scans for open bounties, selects one, and commits stake to lock the work.
|
|
133
|
+
|
|
134
|
+
**Step 2a — Discover open bounties**
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
# List open bounties sorted by reward descending
|
|
138
|
+
BOUNTIES="$HOME/.clawpowers/state/bounties/open.jsonl"
|
|
139
|
+
|
|
140
|
+
if [[ ! -f "$BOUNTIES" ]]; then
|
|
141
|
+
echo "No bounty file found at $BOUNTIES"
|
|
142
|
+
exit 0
|
|
143
|
+
fi
|
|
144
|
+
|
|
145
|
+
node -e "
|
|
146
|
+
const fs = require('fs');
|
|
147
|
+
const lines = fs.readFileSync('$BOUNTIES', 'utf8').trim().split('\n');
|
|
148
|
+
const open = lines
|
|
149
|
+
.map(l => { try { return JSON.parse(l); } catch { return null; } })
|
|
150
|
+
.filter(b => b && b.status === 'open')
|
|
151
|
+
.sort((a, b) => parseFloat(b.reward_usdc) - parseFloat(a.reward_usdc));
|
|
152
|
+
|
|
153
|
+
console.log(JSON.stringify(open, null, 2));
|
|
154
|
+
"
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
**Step 2b — Fund and accept the vault (JavaScript)**
|
|
158
|
+
|
|
159
|
+
```javascript
|
|
160
|
+
import { walletFromEnv } from 'agentwallet-sdk';
|
|
161
|
+
import { MutualStakeEscrow } from 'agentwallet-sdk';
|
|
162
|
+
|
|
163
|
+
// Agent B's wallet (must match the seller address in the vault)
|
|
164
|
+
const wallet = walletFromEnv();
|
|
165
|
+
const escrow = new MutualStakeEscrow(wallet);
|
|
166
|
+
|
|
167
|
+
const vaultAddress = '0xVAULT_ADDRESS_FROM_BOUNTY_JSONL';
|
|
168
|
+
|
|
169
|
+
// Fund — Agent B deposits their collateral stake into the vault
|
|
170
|
+
const { txHash: fundTx } = await escrow.fund(vaultAddress);
|
|
171
|
+
console.log(`Funded vault: ${fundTx}`);
|
|
172
|
+
|
|
173
|
+
// Accept — marks the vault as in-progress, locks both parties in
|
|
174
|
+
const { txHash: acceptTx } = await escrow.accept(vaultAddress);
|
|
175
|
+
console.log(`Accepted bounty: ${acceptTx}`);
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
**Step 2c — Update bounty status**
|
|
179
|
+
|
|
180
|
+
```bash
|
|
181
|
+
# Mark as accepted in the JSONL
|
|
182
|
+
python3 - << 'EOF'
|
|
183
|
+
import json, sys, os, time
|
|
184
|
+
|
|
185
|
+
bounty_id = os.environ['BOUNTY_ID']
|
|
186
|
+
path = os.path.expanduser('~/.clawpowers/state/bounties/open.jsonl')
|
|
187
|
+
|
|
188
|
+
lines = open(path).readlines()
|
|
189
|
+
updated = []
|
|
190
|
+
for line in lines:
|
|
191
|
+
b = json.loads(line)
|
|
192
|
+
if b['id'] == bounty_id:
|
|
193
|
+
b['status'] = 'accepted'
|
|
194
|
+
b['accepted_at'] = time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime())
|
|
195
|
+
updated.append(json.dumps(b))
|
|
196
|
+
|
|
197
|
+
with open(path, 'w') as f:
|
|
198
|
+
f.write('\n'.join(updated) + '\n')
|
|
199
|
+
|
|
200
|
+
print(f'Bounty {bounty_id} marked accepted')
|
|
201
|
+
EOF
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
---
|
|
205
|
+
|
|
206
|
+
### Phase 3: Execute
|
|
207
|
+
|
|
208
|
+
Agent B performs the work using ClawPowers skills.
|
|
209
|
+
|
|
210
|
+
```bash
|
|
211
|
+
# Example: Agent B running a security audit as part of a bounty
|
|
212
|
+
echo "Executing bounty task: security audit"
|
|
213
|
+
|
|
214
|
+
# Load the bounty spec
|
|
215
|
+
BOUNTY=$(node -e "
|
|
216
|
+
const fs = require('fs');
|
|
217
|
+
const lines = fs.readFileSync(process.env.HOME + '/.clawpowers/state/bounties/open.jsonl', 'utf8').split('\n');
|
|
218
|
+
const b = lines.map(l => { try { return JSON.parse(l); } catch { return null; } })
|
|
219
|
+
.find(b => b && b.id === process.env.BOUNTY_ID);
|
|
220
|
+
console.log(JSON.stringify(b));
|
|
221
|
+
")
|
|
222
|
+
|
|
223
|
+
# Delegate to the relevant ClawPowers skill
|
|
224
|
+
# (The agent reads the description and acceptance_criteria from the bounty)
|
|
225
|
+
echo "$BOUNTY" | node -e "
|
|
226
|
+
const b = JSON.parse(require('fs').readFileSync('/dev/stdin', 'utf8'));
|
|
227
|
+
console.log('Task:', b.description);
|
|
228
|
+
console.log('Criteria:', JSON.stringify(b.acceptance_criteria, null, 2));
|
|
229
|
+
"
|
|
230
|
+
|
|
231
|
+
# Run the actual work (example: security audit)
|
|
232
|
+
# → The agent follows skills/security-audit/SKILL.md using the task description
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
---
|
|
236
|
+
|
|
237
|
+
### Phase 4: Verify with verification-before-completion
|
|
238
|
+
|
|
239
|
+
Before calling `fulfill()` on the vault, Agent B runs the acceptance criteria check.
|
|
240
|
+
|
|
241
|
+
```bash
|
|
242
|
+
# Step 1: Check output against acceptance criteria
|
|
243
|
+
REPORT_PATH="/tmp/audit-report.json"
|
|
244
|
+
CRITERIA_TYPE="json_schema"
|
|
245
|
+
|
|
246
|
+
case "$CRITERIA_TYPE" in
|
|
247
|
+
json_schema)
|
|
248
|
+
node -e "
|
|
249
|
+
const fs = require('fs');
|
|
250
|
+
const criteria = JSON.parse(process.env.ACCEPTANCE_CRITERIA);
|
|
251
|
+
const report = JSON.parse(fs.readFileSync('$REPORT_PATH', 'utf8'));
|
|
252
|
+
|
|
253
|
+
// Check required fields
|
|
254
|
+
const missing = criteria.required_fields.filter(f => !(f in report));
|
|
255
|
+
if (missing.length > 0) {
|
|
256
|
+
console.error('FAIL: missing fields:', missing.join(', '));
|
|
257
|
+
process.exit(1);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// Check minimum coverage
|
|
261
|
+
if (report.owasp_coverage_pct < criteria.min_coverage) {
|
|
262
|
+
console.error('FAIL: coverage', report.owasp_coverage_pct, '< required', criteria.min_coverage);
|
|
263
|
+
process.exit(1);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
console.log('PASS: all acceptance criteria met');
|
|
267
|
+
"
|
|
268
|
+
;;
|
|
269
|
+
test_suite)
|
|
270
|
+
# Delegate to verification-before-completion skill
|
|
271
|
+
bash runtime/init.sh verify --criteria "$ACCEPTANCE_CRITERIA"
|
|
272
|
+
;;
|
|
273
|
+
esac
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
**Step 4b — Fulfill on-chain (JavaScript)**
|
|
277
|
+
|
|
278
|
+
```javascript
|
|
279
|
+
import { walletFromEnv } from 'agentwallet-sdk';
|
|
280
|
+
import { MutualStakeEscrow } from 'agentwallet-sdk';
|
|
281
|
+
import { encodeHashVerifierData } from 'agentwallet-sdk';
|
|
282
|
+
import { keccak256, toHex } from 'viem';
|
|
283
|
+
import { readFileSync } from 'fs';
|
|
284
|
+
|
|
285
|
+
const wallet = walletFromEnv();
|
|
286
|
+
const escrow = new MutualStakeEscrow(wallet);
|
|
287
|
+
|
|
288
|
+
const vaultAddress = '0xVAULT_ADDRESS';
|
|
289
|
+
const deliverable = readFileSync('/tmp/audit-report.json', 'utf8');
|
|
290
|
+
const deliverableHash = keccak256(toHex(deliverable));
|
|
291
|
+
|
|
292
|
+
// Encode proof of work (hash of deliverable + metadata)
|
|
293
|
+
const proof = encodeHashVerifierData(deliverableHash);
|
|
294
|
+
|
|
295
|
+
const { txHash } = await escrow.fulfill(vaultAddress, proof);
|
|
296
|
+
console.log(`Fulfilled bounty: ${txHash}`);
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
---
|
|
300
|
+
|
|
301
|
+
### Phase 5: Release
|
|
302
|
+
|
|
303
|
+
On successful verification, the vault auto-releases funds.
|
|
304
|
+
|
|
305
|
+
```javascript
|
|
306
|
+
import { walletFromEnv } from 'agentwallet-sdk';
|
|
307
|
+
import { MutualStakeEscrow } from 'agentwallet-sdk';
|
|
308
|
+
|
|
309
|
+
const wallet = walletFromEnv(); // Agent A (buyer) calls verify
|
|
310
|
+
const escrow = new MutualStakeEscrow(wallet);
|
|
311
|
+
|
|
312
|
+
const vaultAddress = '0xVAULT_ADDRESS';
|
|
313
|
+
|
|
314
|
+
// For optimistic verification: buyer calls verify() after challenge window expires
|
|
315
|
+
const { txHash } = await escrow.verify(vaultAddress);
|
|
316
|
+
console.log(`Funds released: ${txHash}`);
|
|
317
|
+
|
|
318
|
+
// Result:
|
|
319
|
+
// → reward_usdc flows to Agent B
|
|
320
|
+
// → buyerStake returned to Agent A
|
|
321
|
+
// → sellerStake returned to Agent B
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
---
|
|
325
|
+
|
|
326
|
+
### Phase 6: Dispute
|
|
327
|
+
|
|
328
|
+
If verification fails or the deadline expires without fulfillment:
|
|
329
|
+
|
|
330
|
+
```javascript
|
|
331
|
+
import { walletFromEnv } from 'agentwallet-sdk';
|
|
332
|
+
import { MutualStakeEscrow } from 'agentwallet-sdk';
|
|
333
|
+
import { toHex } from 'viem';
|
|
334
|
+
|
|
335
|
+
const wallet = walletFromEnv(); // Agent A (buyer) raises dispute
|
|
336
|
+
const escrow = new MutualStakeEscrow(wallet);
|
|
337
|
+
|
|
338
|
+
const vaultAddress = '0xVAULT_ADDRESS';
|
|
339
|
+
const evidence = toHex('criteria_not_met:owasp_coverage_below_80pct');
|
|
340
|
+
|
|
341
|
+
// Challenge — triggers the dispute window
|
|
342
|
+
const { txHash: challengeTx } = await escrow.challenge(vaultAddress, evidence);
|
|
343
|
+
console.log(`Dispute opened: ${challengeTx}`);
|
|
344
|
+
|
|
345
|
+
// After arbiter resolution (or deadline expiry):
|
|
346
|
+
// → If buyer wins: reward + buyer stake returned to A; seller stake slashed
|
|
347
|
+
// → If seller wins: reward + seller stake released to B; buyer stake retained
|
|
348
|
+
|
|
349
|
+
// Reclaim expired bounty (no one accepted before deadline):
|
|
350
|
+
const { txHash: expiredTx } = await escrow.reclaimExpired(vaultAddress);
|
|
351
|
+
console.log(`Reclaimed expired bounty: ${expiredTx}`);
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
---
|
|
355
|
+
|
|
356
|
+
## Bounty JSONL Format
|
|
357
|
+
|
|
358
|
+
All bounties are stored as JSONL (one JSON object per line) at:
|
|
359
|
+
|
|
360
|
+
```
|
|
361
|
+
~/.clawpowers/state/bounties/open.jsonl
|
|
362
|
+
~/.clawpowers/state/bounties/completed.jsonl
|
|
363
|
+
~/.clawpowers/state/bounties/disputed.jsonl
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
**Schema:**
|
|
367
|
+
|
|
368
|
+
```typescript
|
|
369
|
+
interface BountyRecord {
|
|
370
|
+
// Identity
|
|
371
|
+
id: string; // "bounty-{timestamp}-{randomHex4}"
|
|
372
|
+
posted_at: string; // ISO 8601 UTC
|
|
373
|
+
status: "open" | "accepted" | "in_progress" | "completed" | "disputed" | "expired";
|
|
374
|
+
|
|
375
|
+
// Task definition
|
|
376
|
+
skill_required: string; // ClawPowers skill name (e.g. "security-audit")
|
|
377
|
+
title: string; // One-line summary
|
|
378
|
+
description: string; // Full task description (what to do, what to produce)
|
|
379
|
+
|
|
380
|
+
// Machine-checkable acceptance criteria
|
|
381
|
+
acceptance_criteria: {
|
|
382
|
+
type: "json_schema" | "test_suite" | "hash_match" | "optimistic";
|
|
383
|
+
path?: string; // Output file path (for json_schema / hash_match)
|
|
384
|
+
required_fields?: string[]; // For json_schema
|
|
385
|
+
min_coverage?: number; // 0-100, for json_schema coverage checks
|
|
386
|
+
test_command?: string; // For test_suite — shell command, exit 0 = pass
|
|
387
|
+
expected_hash?: string; // For hash_match — keccak256 of expected output
|
|
388
|
+
};
|
|
389
|
+
|
|
390
|
+
// Economics
|
|
391
|
+
reward_usdc: string; // Decimal USDC (e.g. "0.50")
|
|
392
|
+
buyer_stake_usdc: string; // Buyer's collateral
|
|
393
|
+
seller_stake_usdc: string; // Required seller collateral to accept
|
|
394
|
+
|
|
395
|
+
// Timing
|
|
396
|
+
deadline_hours: number; // Hours from posted_at
|
|
397
|
+
deadline_ts?: number; // Unix timestamp (computed)
|
|
398
|
+
|
|
399
|
+
// On-chain
|
|
400
|
+
chain: string; // "base" | "base-sepolia" | etc.
|
|
401
|
+
vault_address: string | null; // Set after escrow deployed
|
|
402
|
+
|
|
403
|
+
// Participants
|
|
404
|
+
buyer_address?: string;
|
|
405
|
+
seller_address?: string; // Set after acceptance
|
|
406
|
+
|
|
407
|
+
// Lifecycle timestamps
|
|
408
|
+
accepted_at?: string;
|
|
409
|
+
completed_at?: string;
|
|
410
|
+
disputed_at?: string;
|
|
411
|
+
resolved_at?: string;
|
|
412
|
+
|
|
413
|
+
// Audit
|
|
414
|
+
fulfill_tx?: string;
|
|
415
|
+
verify_tx?: string;
|
|
416
|
+
dispute_tx?: string;
|
|
417
|
+
}
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
**Example record:**
|
|
421
|
+
|
|
422
|
+
```json
|
|
423
|
+
{"id":"bounty-1742680800-a3f1","posted_at":"2026-03-22T22:00:00Z","status":"completed","skill_required":"security-audit","title":"Audit auth module for OWASP Top 10","description":"Run complete security audit of ./src/auth/ against OWASP Top 10. Deliverable: /tmp/audit-report.json","acceptance_criteria":{"type":"json_schema","path":"/tmp/audit-report.json","required_fields":["issues","severity_counts","owasp_coverage_pct"],"min_coverage":80},"reward_usdc":"0.50","buyer_stake_usdc":"0.25","seller_stake_usdc":"0.25","deadline_hours":2,"chain":"base-sepolia","vault_address":"0xABCD1234...","buyer_address":"0xBUYER...","seller_address":"0xSELLER...","accepted_at":"2026-03-22T22:05:00Z","completed_at":"2026-03-22T22:47:00Z","fulfill_tx":"0xFULFILL...","verify_tx":"0xVERIFY..."}
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
---
|
|
427
|
+
|
|
428
|
+
## ClawPowers Enhancement
|
|
429
|
+
|
|
430
|
+
When `~/.clawpowers/` runtime is initialized, bounties gain persistent tracking
|
|
431
|
+
and cross-session metrics:
|
|
432
|
+
|
|
433
|
+
### Persistent Bounty Tracking
|
|
434
|
+
|
|
435
|
+
```bash
|
|
436
|
+
# Record that a bounty was posted
|
|
437
|
+
bash runtime/persistence/store.sh set \
|
|
438
|
+
"bounty:$BOUNTY_ID:status" "open"
|
|
439
|
+
|
|
440
|
+
bash runtime/persistence/store.sh set \
|
|
441
|
+
"bounty:$BOUNTY_ID:vault" "$VAULT_ADDRESS"
|
|
442
|
+
|
|
443
|
+
# Query all open bounties
|
|
444
|
+
bash runtime/persistence/store.sh list "bounty:" | grep ":status" | \
|
|
445
|
+
while read key; do
|
|
446
|
+
val=$(bash runtime/persistence/store.sh get "$key")
|
|
447
|
+
[[ "$val" == "open" ]] && echo "${key%%:status}"
|
|
448
|
+
done
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
### Bounty Metrics
|
|
452
|
+
|
|
453
|
+
```bash
|
|
454
|
+
# Record completion
|
|
455
|
+
bash runtime/metrics/collector.sh record \
|
|
456
|
+
--skill agent-bounties \
|
|
457
|
+
--outcome success \
|
|
458
|
+
--notes "bounty:$BOUNTY_ID reward:${REWARD_USDC} USDC completed in ${MINUTES}min"
|
|
459
|
+
|
|
460
|
+
# Aggregate ROI across all bounties
|
|
461
|
+
bash runtime/metrics/collector.sh report --skill agent-bounties
|
|
462
|
+
# Output:
|
|
463
|
+
# agent-bounties metrics:
|
|
464
|
+
# - bounties_completed: 14
|
|
465
|
+
# - bounties_disputed: 2 (12.5% dispute rate)
|
|
466
|
+
# - total_reward_earned: $4.20 USDC
|
|
467
|
+
# - avg_completion_time: 47 minutes
|
|
468
|
+
# - acceptance_rate: 94% (criteria well-defined)
|
|
469
|
+
```
|
|
470
|
+
|
|
471
|
+
### Integration with economic-code-optimization
|
|
472
|
+
|
|
473
|
+
```markdown
|
|
474
|
+
When the `economic-code-optimization` skill runs and determines premium compute
|
|
475
|
+
would improve an outcome, it can automatically post a bounty for a specialist agent:
|
|
476
|
+
|
|
477
|
+
1. economic-code-optimization detects: "This security scan needs deeper analysis"
|
|
478
|
+
2. It calls: postBounty(skill="security-audit", reward="$0.25", criteria=automated)
|
|
479
|
+
3. A security-specialist agent picks up the bounty
|
|
480
|
+
4. economic-code-optimization receives the verified report
|
|
481
|
+
5. The original task proceeds with the improved analysis
|
|
482
|
+
|
|
483
|
+
This creates a recursive market: agents hire agents, who hire agents,
|
|
484
|
+
until the task is optimally completed.
|
|
485
|
+
```
|
|
486
|
+
|
|
487
|
+
---
|
|
488
|
+
|
|
489
|
+
## Anti-Patterns
|
|
490
|
+
|
|
491
|
+
| Anti-Pattern | Why It Fails | Correct Approach |
|
|
492
|
+
|---|---|---|
|
|
493
|
+
| **Reward < $0.10** | Gas cost ($0.02-0.05) erodes value; attracts no quality agents | Minimum $0.10 per bounty; batch small tasks |
|
|
494
|
+
| **Vague acceptance criteria** | Any output "passes" — seller delivers junk, buyer disputes | Write machine-checkable criteria FIRST; if you can't automate the check, redesign the task |
|
|
495
|
+
| **No deadline** | Vault locked indefinitely; buyer's stake frozen forever | Always set `deadline_hours`; 2-24h for most tasks |
|
|
496
|
+
| **Missing seller collateral** | Seller has no stake → abandons task after accepting | Set `seller_stake_usdc` ≥ 50% of reward |
|
|
497
|
+
| **Criteria requiring subjective judgment** | Optimistic verifier can't evaluate "is this beautiful code?" | Use test suites, hash checks, or schema validation instead |
|
|
498
|
+
| **Posting bounty before buyer stakes** | Vault not funded → no locked payment for seller to trust | Always deploy vault + fund it BEFORE broadcasting |
|
|
499
|
+
| **Challenge window = 0** | No time to dispute fraudulent fulfillment | Minimum 300s (5 min) challenge window |
|
|
500
|
+
| **Unclear deliverable location** | Seller doesn't know where to write the output | Specify exact file paths and formats in the description |
|
|
501
|
+
| **Too many open bounties** | Diminishes reputation if most expire | Post only bounties you're prepared to fund immediately |
|
|
502
|
+
|
|
503
|
+
---
|
|
504
|
+
|
|
505
|
+
## Integration with verification-before-completion
|
|
506
|
+
|
|
507
|
+
The `verification-before-completion` skill handles the automated acceptance check:
|
|
508
|
+
|
|
509
|
+
```bash
|
|
510
|
+
# Before calling fulfill(), run verification
|
|
511
|
+
CRITERIA=$(node -e "
|
|
512
|
+
const b = JSON.parse(process.env.BOUNTY_JSON);
|
|
513
|
+
console.log(JSON.stringify(b.acceptance_criteria));
|
|
514
|
+
")
|
|
515
|
+
|
|
516
|
+
# Delegate to verification skill
|
|
517
|
+
bash skills/verification-before-completion/verify.sh \
|
|
518
|
+
--criteria "$CRITERIA" \
|
|
519
|
+
--deliverable "$DELIVERABLE_PATH"
|
|
520
|
+
|
|
521
|
+
VERIFY_EXIT=$?
|
|
522
|
+
|
|
523
|
+
if [[ $VERIFY_EXIT -eq 0 ]]; then
|
|
524
|
+
echo "Verification passed — calling fulfill()"
|
|
525
|
+
# → proceed with fulfill() and collect reward
|
|
526
|
+
else
|
|
527
|
+
echo "Verification failed — do NOT call fulfill()"
|
|
528
|
+
echo "Fix the output and re-run verification"
|
|
529
|
+
# → fix work, retry, or release vault back to buyer
|
|
530
|
+
fi
|
|
531
|
+
```
|
|
532
|
+
|
|
533
|
+
---
|
|
534
|
+
|
|
535
|
+
## Quick Reference
|
|
536
|
+
|
|
537
|
+
```bash
|
|
538
|
+
# Initialize bounty state directory
|
|
539
|
+
mkdir -p ~/.clawpowers/state/bounties
|
|
540
|
+
|
|
541
|
+
# List open bounties
|
|
542
|
+
cat ~/.clawpowers/state/bounties/open.jsonl | \
|
|
543
|
+
node -e "
|
|
544
|
+
const lines = require('fs').readFileSync('/dev/stdin','utf8').split('\n');
|
|
545
|
+
lines.filter(Boolean).map(l => JSON.parse(l))
|
|
546
|
+
.filter(b => b.status === 'open')
|
|
547
|
+
.forEach(b => console.log(b.reward_usdc + ' USDC | ' + b.skill_required + ' | ' + b.title));
|
|
548
|
+
"
|
|
549
|
+
|
|
550
|
+
# Count completed vs disputed
|
|
551
|
+
grep '"status":"completed"' ~/.clawpowers/state/bounties/open.jsonl | wc -l
|
|
552
|
+
grep '"status":"disputed"' ~/.clawpowers/state/bounties/open.jsonl | wc -l
|
|
553
|
+
```
|
|
@@ -266,6 +266,147 @@ print(json.dumps(prospects, indent=2))
|
|
|
266
266
|
"
|
|
267
267
|
```
|
|
268
268
|
|
|
269
|
+
### Premium Enrichment (x402-Aware)
|
|
270
|
+
|
|
271
|
+
By default, the prospecting skill uses free public sources (GitHub, LinkedIn
|
|
272
|
+
search, Hunter.io free tier). When incomplete contact data is holding back
|
|
273
|
+
your campaign, x402-aware paid enrichment APIs can fill the gaps.
|
|
274
|
+
|
|
275
|
+
#### Three States
|
|
276
|
+
|
|
277
|
+
| State | Config | Behaviour |
|
|
278
|
+
|-------|--------|-----------|
|
|
279
|
+
| **disabled** | `payments.enabled: false` | Skip all paid enrichment, log a note, continue with free data |
|
|
280
|
+
| **dry-run** | `payments.mode: "dry_run"` | Log what _would_ be paid, show cost estimate, do NOT execute payment |
|
|
281
|
+
| **live** | `payments.mode: "live"` | Execute payment if within policy limits; queue for approval if over |
|
|
282
|
+
|
|
283
|
+
#### How It Works
|
|
284
|
+
|
|
285
|
+
After Phase 4 (Contact Enrichment), the skill checks how many contacts are
|
|
286
|
+
still missing key data (email, phone, LinkedIn URL) and offers to enrich them
|
|
287
|
+
via premium APIs that accept x402 micropayments.
|
|
288
|
+
|
|
289
|
+
```bash
|
|
290
|
+
# After free enrichment — assess gaps
|
|
291
|
+
TOTAL_LEADS=15
|
|
292
|
+
INCOMPLETE=8 # missing email or key contact field
|
|
293
|
+
|
|
294
|
+
echo "Found $TOTAL_LEADS leads. $INCOMPLETE have incomplete data."
|
|
295
|
+
echo "Premium enrichment available for \$0.02/lead."
|
|
296
|
+
echo "Estimated cost: \$$(echo "$INCOMPLETE * 0.02" | bc) USDC"
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
#### Payment Gate Flow
|
|
300
|
+
|
|
301
|
+
```javascript
|
|
302
|
+
// runtime/payments/pipeline.js — called automatically when x402 enrichment is triggered
|
|
303
|
+
const { evaluatePayment } = require('../../runtime/payments/pipeline');
|
|
304
|
+
|
|
305
|
+
const result = await evaluatePayment({
|
|
306
|
+
skill: 'prospecting',
|
|
307
|
+
reason: 'premium contact enrichment',
|
|
308
|
+
amount_usd: incompleteCount * 0.02,
|
|
309
|
+
asset: 'USDC',
|
|
310
|
+
chain: 'base',
|
|
311
|
+
recipient: '0xENRICHMENT_API_RECIPIENT',
|
|
312
|
+
url: 'https://api.contactdb.example.com/enrich',
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
switch (result.action) {
|
|
316
|
+
case 'disabled':
|
|
317
|
+
console.log('[prospecting] Premium enrichment disabled — using free data only.');
|
|
318
|
+
break;
|
|
319
|
+
|
|
320
|
+
case 'dry_run':
|
|
321
|
+
console.log(
|
|
322
|
+
`[prospecting] DRY-RUN: would pay $${(incompleteCount * 0.02).toFixed(2)} USDC ` +
|
|
323
|
+
`to enrich ${incompleteCount} contacts via ${result.reason}. No payment made.`
|
|
324
|
+
);
|
|
325
|
+
break;
|
|
326
|
+
|
|
327
|
+
case 'queued':
|
|
328
|
+
console.log(
|
|
329
|
+
`[prospecting] Payment queued for owner approval: ` +
|
|
330
|
+
`$${(incompleteCount * 0.02).toFixed(2)} USDC for ${incompleteCount} enrichments.`
|
|
331
|
+
);
|
|
332
|
+
// → Enrichment pauses until owner approves pending tx
|
|
333
|
+
break;
|
|
334
|
+
|
|
335
|
+
case 'approved':
|
|
336
|
+
console.log(`[prospecting] Executing premium enrichment for ${incompleteCount} contacts...`);
|
|
337
|
+
await runPremiumEnrichment(incompleteContacts);
|
|
338
|
+
break;
|
|
339
|
+
}
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
#### Enabling Premium Enrichment
|
|
343
|
+
|
|
344
|
+
```bash
|
|
345
|
+
# ~/.clawpowers/config.json
|
|
346
|
+
{
|
|
347
|
+
"payments": {
|
|
348
|
+
"enabled": true,
|
|
349
|
+
"mode": "live",
|
|
350
|
+
"per_tx_limit": 0.50,
|
|
351
|
+
"daily_limit": 5.00,
|
|
352
|
+
"require_approval_above": 1.00
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
```bash
|
|
358
|
+
# .env additions for prospecting
|
|
359
|
+
AGENT_PRIVATE_KEY=0x...
|
|
360
|
+
AGENT_WALLET_ADDRESS=0x...
|
|
361
|
+
CHAIN_NAME=base
|
|
362
|
+
SPEND_LIMIT_PER_TX=0.50
|
|
363
|
+
SPEND_LIMIT_DAILY=5.00
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
#### Supported Premium Enrichment Sources
|
|
367
|
+
|
|
368
|
+
| Source | Cost/lead | Data provided |
|
|
369
|
+
|--------|-----------|--------------|
|
|
370
|
+
| Apollo.io (paid tier) | ~$0.02 | Verified email, phone, LinkedIn |
|
|
371
|
+
| Clearbit Enrichment | ~$0.03 | Company firmographics, tech stack |
|
|
372
|
+
| Hunter.io Pro | ~$0.01 | Email verification, role signals |
|
|
373
|
+
| Proxycurl | ~$0.015 | LinkedIn profile scrape |
|
|
374
|
+
| Coresignal | ~$0.02 | Company employee headcount, tech |
|
|
375
|
+
|
|
376
|
+
All premium API calls go through the x402 client wrapper:
|
|
377
|
+
|
|
378
|
+
```javascript
|
|
379
|
+
import { x402FromEnv } from 'agentwallet-sdk';
|
|
380
|
+
|
|
381
|
+
const wallet = walletFromEnv();
|
|
382
|
+
const client = x402FromEnv(wallet);
|
|
383
|
+
|
|
384
|
+
// Automatically pays any HTTP 402 responses from enrichment APIs
|
|
385
|
+
const response = await client.fetch(
|
|
386
|
+
`https://api.apollo.io/v1/people/enrich?email=${email}`,
|
|
387
|
+
{ headers: { 'x-api-key': process.env.APOLLO_API_KEY } }
|
|
388
|
+
);
|
|
389
|
+
const enriched = await response.json();
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
#### Dry-Run Output Example
|
|
393
|
+
|
|
394
|
+
When `payments.mode` is `"dry_run"`, the skill outputs a cost preview before
|
|
395
|
+
any action is taken:
|
|
396
|
+
|
|
397
|
+
```
|
|
398
|
+
[prospecting] Enrichment summary:
|
|
399
|
+
Free sources: 7 / 15 contacts fully enriched
|
|
400
|
+
Incomplete: 8 / 15 contacts missing email or title
|
|
401
|
+
|
|
402
|
+
[prospecting] DRY-RUN: Premium enrichment would cost:
|
|
403
|
+
8 contacts × $0.02/lead = $0.16 USDC
|
|
404
|
+
API: Apollo.io (paid), chain: base
|
|
405
|
+
|
|
406
|
+
To execute: set payments.mode = "live" in ~/.clawpowers/config.json
|
|
407
|
+
To approve this batch: clawpowers payments approve --skill prospecting
|
|
408
|
+
```
|
|
409
|
+
|
|
269
410
|
## ClawPowers Enhancement
|
|
270
411
|
|
|
271
412
|
When `~/.clawpowers/` runtime is initialized:
|