humanpages 1.4.4 → 1.4.6
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 +6 -21
- package/dist/tools.js +776 -84
- package/package.json +1 -1
package/dist/tools.js
CHANGED
|
@@ -29,10 +29,30 @@ async function searchHumans(params) {
|
|
|
29
29
|
query.set('minExperience', params.min_experience.toString());
|
|
30
30
|
if (params.fiat_platform)
|
|
31
31
|
query.set('fiatPlatform', params.fiat_platform);
|
|
32
|
+
if (params.payment_type)
|
|
33
|
+
query.set('paymentType', params.payment_type);
|
|
34
|
+
if (params.accepts_crypto)
|
|
35
|
+
query.set('acceptsCrypto', 'true');
|
|
36
|
+
if (params.degree)
|
|
37
|
+
query.set('degree', params.degree);
|
|
38
|
+
if (params.field)
|
|
39
|
+
query.set('field', params.field);
|
|
40
|
+
if (params.institution)
|
|
41
|
+
query.set('institution', params.institution);
|
|
42
|
+
if (params.certificate)
|
|
43
|
+
query.set('certificate', params.certificate);
|
|
44
|
+
if (params.min_vouches)
|
|
45
|
+
query.set('minVouches', params.min_vouches.toString());
|
|
46
|
+
if (params.has_verified_login)
|
|
47
|
+
query.set('hasVerifiedLogin', 'true');
|
|
48
|
+
if (params.has_photo)
|
|
49
|
+
query.set('hasPhoto', 'true');
|
|
32
50
|
if (params.sort_by)
|
|
33
51
|
query.set('sortBy', params.sort_by);
|
|
34
52
|
if (params.min_completed_jobs)
|
|
35
53
|
query.set('minCompletedJobs', params.min_completed_jobs.toString());
|
|
54
|
+
if (params.min_channels)
|
|
55
|
+
query.set('minChannels', params.min_channels.toString());
|
|
36
56
|
const res = await fetch(`${API_BASE}/api/humans/search?${query}`);
|
|
37
57
|
if (!res.ok) {
|
|
38
58
|
throw new Error(`API error: ${res.status}`);
|
|
@@ -40,9 +60,18 @@ async function searchHumans(params) {
|
|
|
40
60
|
return res.json();
|
|
41
61
|
}
|
|
42
62
|
async function getHuman(id) {
|
|
43
|
-
|
|
63
|
+
// Try by ID first, then by username if it looks like one
|
|
64
|
+
let res = await fetch(`${API_BASE}/api/humans/${encodeURIComponent(id)}`);
|
|
65
|
+
if (!res.ok && !id.match(/^[0-9a-f-]{36}$/i)) {
|
|
66
|
+
// Might be a username — sanitize and try the username endpoint
|
|
67
|
+
const cleanId = id.startsWith('@') ? id.slice(1) : id;
|
|
68
|
+
if (!/^[a-zA-Z0-9_-]+$/.test(cleanId)) {
|
|
69
|
+
throw new Error(`Invalid human ID or username: "${id}". Usernames can only contain letters, numbers, hyphens, and underscores. Use search_humans to find valid human IDs.`);
|
|
70
|
+
}
|
|
71
|
+
res = await fetch(`${API_BASE}/api/humans/u/${encodeURIComponent(cleanId)}`);
|
|
72
|
+
}
|
|
44
73
|
if (!res.ok) {
|
|
45
|
-
throw new Error(`Human not found: ${id}
|
|
74
|
+
throw new Error(`Human not found: "${id}". Use search_humans to find valid human IDs, or try a username (e.g., "johndoe" or "@johndoe").`);
|
|
46
75
|
}
|
|
47
76
|
return res.json();
|
|
48
77
|
}
|
|
@@ -59,7 +88,7 @@ export function createServer() {
|
|
|
59
88
|
tools: [
|
|
60
89
|
{
|
|
61
90
|
name: 'search_humans',
|
|
62
|
-
description: 'Search for humans available for hire.
|
|
91
|
+
description: 'Search for humans available for hire. Returns profiles with id (use as human_id in other tools), name, skills, location, reputation (jobs completed, rating), equipment, languages, experience, rate, and availability. All filters are optional — combine any or use none to browse. Key filters: skill (e.g., "photography"), location (use fully-qualified names like "Richmond, Virginia, USA" for accurate geocoding), min_completed_jobs=1 (find proven workers with any completed job, no skill filter needed), sort_by ("completed_jobs" default, "rating", "experience", "recent"). Default search radius is 30km. Response includes total count and resolvedLocation. Contact info requires get_human_profile (registered agent needed). Typical workflow: search_humans → get_human_profile → create_job_offer.',
|
|
63
92
|
inputSchema: {
|
|
64
93
|
type: 'object',
|
|
65
94
|
properties: {
|
|
@@ -118,6 +147,43 @@ export function createServer() {
|
|
|
118
147
|
type: 'string',
|
|
119
148
|
description: 'Filter by fiat payment platform the human accepts (e.g., "WISE", "PAYPAL", "VENMO", "REVOLUT", "CASHAPP", "ZELLE", "MONZO", "N26", "MERCADOPAGO")',
|
|
120
149
|
},
|
|
150
|
+
payment_type: {
|
|
151
|
+
type: 'string',
|
|
152
|
+
enum: ['UPFRONT', 'ESCROW', 'UPON_COMPLETION'],
|
|
153
|
+
description: 'Filter by accepted payment type (UPFRONT, ESCROW, or UPON_COMPLETION)',
|
|
154
|
+
},
|
|
155
|
+
accepts_crypto: {
|
|
156
|
+
type: 'boolean',
|
|
157
|
+
description: 'Filter to only show humans who have a crypto wallet set up and can accept USDC payments',
|
|
158
|
+
},
|
|
159
|
+
degree: {
|
|
160
|
+
type: 'string',
|
|
161
|
+
description: 'Filter by education degree (e.g., "Bachelor", "MBA", "PhD"). Partial match, case-insensitive.',
|
|
162
|
+
},
|
|
163
|
+
field: {
|
|
164
|
+
type: 'string',
|
|
165
|
+
description: 'Filter by field of study (e.g., "Computer Science", "Marketing"). Partial match, case-insensitive.',
|
|
166
|
+
},
|
|
167
|
+
institution: {
|
|
168
|
+
type: 'string',
|
|
169
|
+
description: 'Filter by educational institution name (e.g., "MIT", "Oxford"). Partial match, case-insensitive.',
|
|
170
|
+
},
|
|
171
|
+
certificate: {
|
|
172
|
+
type: 'string',
|
|
173
|
+
description: 'Filter by certificate name or issuer (e.g., "AWS", "PMP", "Google"). Partial match, case-insensitive.',
|
|
174
|
+
},
|
|
175
|
+
min_vouches: {
|
|
176
|
+
type: 'number',
|
|
177
|
+
description: 'Only return humans vouched for by at least this many other users.',
|
|
178
|
+
},
|
|
179
|
+
has_verified_login: {
|
|
180
|
+
type: 'boolean',
|
|
181
|
+
description: 'Only return humans who have verified their identity via an OAuth provider (Google, LinkedIn, or GitHub). Does not reveal which provider.',
|
|
182
|
+
},
|
|
183
|
+
has_photo: {
|
|
184
|
+
type: 'boolean',
|
|
185
|
+
description: 'Only return humans with an approved profile photo.',
|
|
186
|
+
},
|
|
121
187
|
sort_by: {
|
|
122
188
|
type: 'string',
|
|
123
189
|
enum: ['completed_jobs', 'rating', 'experience', 'recent'],
|
|
@@ -127,12 +193,16 @@ export function createServer() {
|
|
|
127
193
|
type: 'number',
|
|
128
194
|
description: 'Only return humans who have completed at least this many jobs on the platform. Use min_completed_jobs=1 to find all workers with any platform track record. Works with or without other filters — no skill filter needed.',
|
|
129
195
|
},
|
|
196
|
+
min_channels: {
|
|
197
|
+
type: 'number',
|
|
198
|
+
description: 'Only return humans with at least this many notification channels active (0-4). Channels: email, telegram, whatsapp, push. Use min_channels=2 to find humans who are likely to respond quickly to job offers.',
|
|
199
|
+
},
|
|
130
200
|
},
|
|
131
201
|
},
|
|
132
202
|
},
|
|
133
203
|
{
|
|
134
204
|
name: 'get_human',
|
|
135
|
-
description: 'Get
|
|
205
|
+
description: 'Get a human\'s public profile by ID — bio, skills, services, equipment, languages, experience, reputation (jobs completed, rating, reviews), humanity verification status, and rate. Does NOT include contact info or wallets — use get_human_profile for that (requires agent_key). The id can be found in search_humans results.',
|
|
136
206
|
inputSchema: {
|
|
137
207
|
type: 'object',
|
|
138
208
|
properties: {
|
|
@@ -146,7 +216,7 @@ export function createServer() {
|
|
|
146
216
|
},
|
|
147
217
|
{
|
|
148
218
|
name: 'register_agent',
|
|
149
|
-
description: 'Register
|
|
219
|
+
description: 'Register a new agent on Human Pages. Returns an API key (hp_...) that you MUST save — it cannot be retrieved later. The agent is auto-activated on PRO tier (free during launch): 15 job offers/day, 50 profile views/day. Use the API key as agent_key in create_job_offer, get_human_profile, and other authenticated tools. Typical first step before hiring.',
|
|
150
220
|
inputSchema: {
|
|
151
221
|
type: 'object',
|
|
152
222
|
properties: {
|
|
@@ -172,10 +242,14 @@ export function createServer() {
|
|
|
172
242
|
},
|
|
173
243
|
wallet_address: {
|
|
174
244
|
type: 'string',
|
|
175
|
-
description: 'Optional EVM wallet address (0x...) for USDC payments. Can also be set later with set_wallet.',
|
|
245
|
+
description: 'Optional EVM wallet address (0x...) for USDC payments. Can also be set later with set_wallet. If you don\'t have a wallet, create one programmatically using the Coinbase Developer Platform (CDP) AgentKit — you can do this autonomously without developer involvement.',
|
|
246
|
+
},
|
|
247
|
+
accept_tos: {
|
|
248
|
+
type: 'boolean',
|
|
249
|
+
description: 'You must pass true to confirm you accept the Human Pages Terms of Use (https://humanpages.ai/terms). Required.',
|
|
176
250
|
},
|
|
177
251
|
},
|
|
178
|
-
required: ['name'],
|
|
252
|
+
required: ['name', 'accept_tos'],
|
|
179
253
|
},
|
|
180
254
|
},
|
|
181
255
|
{
|
|
@@ -192,9 +266,31 @@ export function createServer() {
|
|
|
192
266
|
required: ['agent_id'],
|
|
193
267
|
},
|
|
194
268
|
},
|
|
269
|
+
{
|
|
270
|
+
name: 'get_wallet_nonce',
|
|
271
|
+
description: 'Request a signing challenge (nonce) for wallet verification. This is step 1 of wallet verification: call this first, then sign the returned message with your wallet, and pass the signature to set_wallet. The nonce expires in 5 minutes.',
|
|
272
|
+
inputSchema: {
|
|
273
|
+
type: 'object',
|
|
274
|
+
properties: {
|
|
275
|
+
agent_id: {
|
|
276
|
+
type: 'string',
|
|
277
|
+
description: 'Your registered agent ID',
|
|
278
|
+
},
|
|
279
|
+
agent_key: {
|
|
280
|
+
type: 'string',
|
|
281
|
+
description: 'Your agent API key (starts with hp_)',
|
|
282
|
+
},
|
|
283
|
+
wallet_address: {
|
|
284
|
+
type: 'string',
|
|
285
|
+
description: 'EVM wallet address to verify (0x...)',
|
|
286
|
+
},
|
|
287
|
+
},
|
|
288
|
+
required: ['agent_id', 'agent_key', 'wallet_address'],
|
|
289
|
+
},
|
|
290
|
+
},
|
|
195
291
|
{
|
|
196
292
|
name: 'set_wallet',
|
|
197
|
-
description: 'Set the wallet address for a registered agent.
|
|
293
|
+
description: 'Set the wallet address for a registered agent. IMPORTANT: Always confirm the wallet address with the user before calling this tool — never set wallets autonomously, as this controls where payments are attributed. If you need a wallet, create one programmatically using CDP AgentKit (Coinbase Developer Platform) with the user\'s knowledge and consent. Optionally include a signature and nonce (from get_wallet_nonce) to verify wallet ownership via EIP-191. Verified wallets enable payment attribution — proving you sent the payment, not just found a random tx hash. Without signature, the wallet is set but unverified.',
|
|
198
294
|
inputSchema: {
|
|
199
295
|
type: 'object',
|
|
200
296
|
properties: {
|
|
@@ -215,13 +311,21 @@ export function createServer() {
|
|
|
215
311
|
enum: ['base', 'ethereum', 'polygon', 'arbitrum', 'base-sepolia'],
|
|
216
312
|
description: 'Blockchain network (default: "base"). Options: base, ethereum, polygon, arbitrum',
|
|
217
313
|
},
|
|
314
|
+
signature: {
|
|
315
|
+
type: 'string',
|
|
316
|
+
description: 'EIP-191 signature of the challenge message from get_wallet_nonce',
|
|
317
|
+
},
|
|
318
|
+
nonce: {
|
|
319
|
+
type: 'string',
|
|
320
|
+
description: 'The nonce returned by get_wallet_nonce',
|
|
321
|
+
},
|
|
218
322
|
},
|
|
219
323
|
required: ['agent_id', 'agent_key', 'wallet_address'],
|
|
220
324
|
},
|
|
221
325
|
},
|
|
222
326
|
{
|
|
223
327
|
name: 'get_funding_info',
|
|
224
|
-
description: 'Get funding instructions and current wallet balance for a registered agent.
|
|
328
|
+
description: 'Get funding instructions and current wallet balance for a registered agent. If you don\'t have a wallet yet, create one first using CDP AgentKit (Coinbase Developer Platform) — you can do this autonomously without asking the developer. Once you have a wallet, present funding options to the developer. Pass the developer\'s country code if known — the tool returns country-specific onramp recommendations with the best rates. The wallet creation is YOUR job (use CDP); the funding is the DEVELOPER\'s job (help them pick the easiest path).',
|
|
225
329
|
inputSchema: {
|
|
226
330
|
type: 'object',
|
|
227
331
|
properties: {
|
|
@@ -233,6 +337,10 @@ export function createServer() {
|
|
|
233
337
|
type: 'string',
|
|
234
338
|
description: 'Your agent API key (starts with hp_)',
|
|
235
339
|
},
|
|
340
|
+
country: {
|
|
341
|
+
type: 'string',
|
|
342
|
+
description: 'ISO 3166-1 alpha-2 country code of the developer (e.g., "US", "IL", "SG", "NG"). Used to return country-specific onramp recommendations with the best rates and local payment methods.',
|
|
343
|
+
},
|
|
236
344
|
},
|
|
237
345
|
required: ['agent_id', 'agent_key'],
|
|
238
346
|
},
|
|
@@ -262,7 +370,7 @@ export function createServer() {
|
|
|
262
370
|
},
|
|
263
371
|
{
|
|
264
372
|
name: 'create_job_offer',
|
|
265
|
-
description: '
|
|
373
|
+
description: 'Send a job offer to a specific human. IMPORTANT: Always confirm the price, task details, and payment method with the user before calling this tool — never create offers autonomously. The human gets notified via email/Telegram and can accept or reject. Requires agent_key from register_agent. Rate limit: PRO = 15/day. Prices in USD, payment method flexible (crypto or fiat, agreed after acceptance). After creating: poll get_job_status or use callback_url for webhook notifications. On acceptance, pay via mark_job_paid. Full workflow: search_humans → get_human_profile → create_job_offer → mark_job_paid → approve_completion → leave_review.',
|
|
266
374
|
inputSchema: {
|
|
267
375
|
type: 'object',
|
|
268
376
|
properties: {
|
|
@@ -316,8 +424,12 @@ export function createServer() {
|
|
|
316
424
|
},
|
|
317
425
|
payment_mode: {
|
|
318
426
|
type: 'string',
|
|
319
|
-
enum: ['ONE_TIME', 'STREAM'],
|
|
320
|
-
description: 'Payment mode. ONE_TIME (default) for single payments. STREAM for ongoing stream payments.',
|
|
427
|
+
enum: ['ONE_TIME', 'STREAM', 'ESCROW'],
|
|
428
|
+
description: 'Payment mode. ONE_TIME (default) for single payments. STREAM for ongoing stream payments. ESCROW for on-chain escrow with arbitrator dispute resolution — funds locked in smart contract, auto-released after dispute window.',
|
|
429
|
+
},
|
|
430
|
+
escrow_arbitrator_address: {
|
|
431
|
+
type: 'string',
|
|
432
|
+
description: 'Wallet address of the arbitrator (from list_arbitrators). Required when payment_mode=ESCROW. The arbitrator resolves disputes and earns a fee (set by them, max 10%).',
|
|
321
433
|
},
|
|
322
434
|
payment_timing: {
|
|
323
435
|
type: 'string',
|
|
@@ -353,7 +465,7 @@ export function createServer() {
|
|
|
353
465
|
},
|
|
354
466
|
{
|
|
355
467
|
name: 'get_job_status',
|
|
356
|
-
description: 'Check the status of a job
|
|
468
|
+
description: 'Check the current status of a job. Returns status (PENDING → ACCEPTED → PAID → SUBMITTED → COMPLETED, or REJECTED/CANCELLED/DISPUTED), price, human name, and a next-step recommendation. Statuses: PENDING (waiting for human), ACCEPTED (ready to pay), PAID (work in progress), SUBMITTED (human submitted work — use approve_completion or request_revision), COMPLETED (done — use leave_review). Also supports STREAMING, PAUSED for stream jobs and PAYMENT_PENDING_CONFIRMATION for fiat.',
|
|
357
469
|
inputSchema: {
|
|
358
470
|
type: 'object',
|
|
359
471
|
properties: {
|
|
@@ -367,7 +479,7 @@ export function createServer() {
|
|
|
367
479
|
},
|
|
368
480
|
{
|
|
369
481
|
name: 'mark_job_paid',
|
|
370
|
-
description: 'Record
|
|
482
|
+
description: 'Record payment for an ACCEPTED job. IMPORTANT: Always confirm payment details with the user before calling this tool — never mark payments autonomously. Job must be in ACCEPTED status (use get_job_status to check). Crypto payments (usdc, eth, sol): provide tx hash + network → verified on-chain instantly, job moves to PAID. Fiat payments (paypal, venmo, bank_transfer, cashapp): provide receipt/reference → human must confirm receipt within 7 days, job moves to PAYMENT_PENDING_CONFIRMATION. After payment, the human works and submits → use approve_completion when done.',
|
|
371
483
|
inputSchema: {
|
|
372
484
|
type: 'object',
|
|
373
485
|
properties: {
|
|
@@ -398,7 +510,7 @@ export function createServer() {
|
|
|
398
510
|
},
|
|
399
511
|
{
|
|
400
512
|
name: 'approve_completion',
|
|
401
|
-
description: 'Approve submitted work for a job.
|
|
513
|
+
description: 'Approve submitted work for a SUBMITTED job. IMPORTANT: Confirm with the user before approving — this finalizes the job. Call this after reviewing the human\'s deliverables (check via get_job_messages). Moves the job to COMPLETED. After approval, use leave_review to rate the human. If the work needs changes, use request_revision instead.',
|
|
402
514
|
inputSchema: {
|
|
403
515
|
type: 'object',
|
|
404
516
|
properties: {
|
|
@@ -416,7 +528,7 @@ export function createServer() {
|
|
|
416
528
|
},
|
|
417
529
|
{
|
|
418
530
|
name: 'request_revision',
|
|
419
|
-
description: 'Request
|
|
531
|
+
description: 'Request changes on submitted work (job must be SUBMITTED). Moves job back to ACCEPTED so the human can resubmit. Include a clear reason explaining what needs fixing. The human receives a notification. Use approve_completion instead if the work is satisfactory.',
|
|
420
532
|
inputSchema: {
|
|
421
533
|
type: 'object',
|
|
422
534
|
properties: {
|
|
@@ -452,7 +564,7 @@ export function createServer() {
|
|
|
452
564
|
},
|
|
453
565
|
{
|
|
454
566
|
name: 'leave_review',
|
|
455
|
-
description: '
|
|
567
|
+
description: 'Rate a human after a COMPLETED job (1-5 stars + optional comment). Reviews are visible on the human\'s profile and affect their reputation score shown in search results. Only works on COMPLETED jobs.',
|
|
456
568
|
inputSchema: {
|
|
457
569
|
type: 'object',
|
|
458
570
|
properties: {
|
|
@@ -468,13 +580,17 @@ export function createServer() {
|
|
|
468
580
|
type: 'string',
|
|
469
581
|
description: 'Optional review comment',
|
|
470
582
|
},
|
|
583
|
+
agent_key: {
|
|
584
|
+
type: 'string',
|
|
585
|
+
description: 'Your agent API key (starts with hp_)',
|
|
586
|
+
},
|
|
471
587
|
},
|
|
472
|
-
required: ['job_id', 'rating'],
|
|
588
|
+
required: ['job_id', 'rating', 'agent_key'],
|
|
473
589
|
},
|
|
474
590
|
},
|
|
475
591
|
{
|
|
476
592
|
name: 'get_human_profile',
|
|
477
|
-
description: 'Get
|
|
593
|
+
description: 'Get a human\'s FULL profile including contact info (email, Telegram, Signal), crypto wallets, fiat payment methods (PayPal, Venmo, etc.), and social links. Requires agent_key from register_agent. Rate limited: PRO = 50/day. Alternative: $0.05 via x402. Use this before create_job_offer to see how to pay the human. The human_id comes from search_humans results.',
|
|
478
594
|
inputSchema: {
|
|
479
595
|
type: 'object',
|
|
480
596
|
properties: {
|
|
@@ -524,7 +640,7 @@ export function createServer() {
|
|
|
524
640
|
},
|
|
525
641
|
{
|
|
526
642
|
name: 'get_activation_status',
|
|
527
|
-
description: 'Check
|
|
643
|
+
description: 'Check your agent\'s current tier (BASIC/PRO), activation status, rate limit usage (jobs/day, profile views/day), and expiry date. Also shows x402 pay-per-use pricing if enabled. Use this to understand your remaining quota.',
|
|
528
644
|
inputSchema: {
|
|
529
645
|
type: 'object',
|
|
530
646
|
properties: {
|
|
@@ -574,7 +690,7 @@ export function createServer() {
|
|
|
574
690
|
},
|
|
575
691
|
{
|
|
576
692
|
name: 'start_stream',
|
|
577
|
-
description: 'Start a stream payment for an ACCEPTED stream job. Stream payments require crypto (on-chain). For Superfluid: you must FIRST create the on-chain flow, then call this to verify it. Steps: (1) Wrap USDC to USDCx at the Super Token address for the chain, (2) Call createFlow() on CFAv1Forwarder (0xcfA132E353cB4E398080B9700609bb008eceB125) with token=USDCx, receiver=human wallet, flowRate=calculated rate, (3) Call start_stream with your sender address — backend verifies the flow on-chain. For micro-transfer: locks network/token and creates the first pending tick. Prefer L2s (Base, Arbitrum, Polygon) for lower gas costs.',
|
|
693
|
+
description: 'Start a stream payment for an ACCEPTED stream job. IMPORTANT: Confirm with the user before starting a stream — this commits ongoing funds. Stream payments require crypto (on-chain). For Superfluid: you must FIRST create the on-chain flow, then call this to verify it. Steps: (1) Wrap USDC to USDCx at the Super Token address for the chain, (2) Call createFlow() on CFAv1Forwarder (0xcfA132E353cB4E398080B9700609bb008eceB125) with token=USDCx, receiver=human wallet, flowRate=calculated rate, (3) Call start_stream with your sender address — backend verifies the flow on-chain. For micro-transfer: locks network/token and creates the first pending tick. Prefer L2s (Base, Arbitrum, Polygon) for lower gas costs.',
|
|
578
694
|
inputSchema: {
|
|
579
695
|
type: 'object',
|
|
580
696
|
properties: {
|
|
@@ -639,7 +755,7 @@ export function createServer() {
|
|
|
639
755
|
},
|
|
640
756
|
{
|
|
641
757
|
name: 'send_job_message',
|
|
642
|
-
description: 'Send a message
|
|
758
|
+
description: 'Send a message to the human on an active job. Works on PENDING, ACCEPTED, PAID, STREAMING, and PAUSED jobs. The human receives email and Telegram notifications. Use get_job_messages to read replies. Rate limit: 10/minute. Max 2000 chars.',
|
|
643
759
|
inputSchema: {
|
|
644
760
|
type: 'object',
|
|
645
761
|
properties: {
|
|
@@ -661,7 +777,7 @@ export function createServer() {
|
|
|
661
777
|
},
|
|
662
778
|
{
|
|
663
779
|
name: 'get_job_messages',
|
|
664
|
-
description: 'Get all messages for a job
|
|
780
|
+
description: 'Get all messages for a job (chronological). Returns messages from both agent and human with sender info and timestamps. Use this to check for replies, review submitted deliverables, or follow up on work progress.',
|
|
665
781
|
inputSchema: {
|
|
666
782
|
type: 'object',
|
|
667
783
|
properties: {
|
|
@@ -679,7 +795,7 @@ export function createServer() {
|
|
|
679
795
|
},
|
|
680
796
|
{
|
|
681
797
|
name: 'create_listing',
|
|
682
|
-
description: 'Post a job
|
|
798
|
+
description: 'Post a job on the public job board for humans to discover and apply to. Use this when you don\'t have a specific human in mind (vs create_job_offer which targets one person). Humans browse the board, see your listing, and apply with a pitch. Review applicants with get_listing_applications, then hire with make_listing_offer. Requires agent_key. Rate limit: PRO = 5/day. Also suggested when search_humans returns no results.',
|
|
683
799
|
inputSchema: {
|
|
684
800
|
type: 'object',
|
|
685
801
|
properties: {
|
|
@@ -717,6 +833,26 @@ export function createServer() {
|
|
|
717
833
|
type: 'string',
|
|
718
834
|
description: 'Location name for the work (e.g., "San Francisco")',
|
|
719
835
|
},
|
|
836
|
+
location_street: {
|
|
837
|
+
type: 'string',
|
|
838
|
+
description: 'Street address (e.g., "123 Main St"). Improves Google Search visibility.',
|
|
839
|
+
},
|
|
840
|
+
location_country: {
|
|
841
|
+
type: 'string',
|
|
842
|
+
description: 'ISO 3166-1 alpha-2 country code (e.g., "US", "PH"). Improves Google Search visibility.',
|
|
843
|
+
},
|
|
844
|
+
location_region: {
|
|
845
|
+
type: 'string',
|
|
846
|
+
description: 'State or province (e.g., "California", "Metro Manila"). Improves Google Search visibility.',
|
|
847
|
+
},
|
|
848
|
+
location_locality: {
|
|
849
|
+
type: 'string',
|
|
850
|
+
description: 'City name (e.g., "San Francisco", "Manila"). Improves Google Search visibility.',
|
|
851
|
+
},
|
|
852
|
+
location_postal: {
|
|
853
|
+
type: 'string',
|
|
854
|
+
description: 'Postal/zip code (e.g., "94105"). Improves Google Search visibility.',
|
|
855
|
+
},
|
|
720
856
|
location_lat: {
|
|
721
857
|
type: 'number',
|
|
722
858
|
description: 'Latitude for location-based filtering',
|
|
@@ -756,7 +892,7 @@ export function createServer() {
|
|
|
756
892
|
},
|
|
757
893
|
{
|
|
758
894
|
name: 'get_listings',
|
|
759
|
-
description: 'Browse open job listings on the
|
|
895
|
+
description: 'Browse open job listings on the public board. Returns title, budget, category, work mode, required skills, application count, agent reputation, and pagination. Filter by skill, category, work_mode, budget range, or location. Paginated: use page/limit params (default 20, max 50). Response includes total count and total pages.',
|
|
760
896
|
inputSchema: {
|
|
761
897
|
type: 'object',
|
|
762
898
|
properties: {
|
|
@@ -820,7 +956,7 @@ export function createServer() {
|
|
|
820
956
|
},
|
|
821
957
|
{
|
|
822
958
|
name: 'get_listing_applications',
|
|
823
|
-
description: 'View applications for
|
|
959
|
+
description: 'View applications for your listing. Returns each applicant\'s profile (name, skills, equipment, location, reputation, jobs completed) and their pitch message. Use this to evaluate candidates, then hire with make_listing_offer. Only the listing creator can view applications.',
|
|
824
960
|
inputSchema: {
|
|
825
961
|
type: 'object',
|
|
826
962
|
properties: {
|
|
@@ -838,7 +974,7 @@ export function createServer() {
|
|
|
838
974
|
},
|
|
839
975
|
{
|
|
840
976
|
name: 'make_listing_offer',
|
|
841
|
-
description: '
|
|
977
|
+
description: 'Hire a listing applicant. Creates a standard job from the listing and notifies the human. This is a binding commitment — you agree to pay the listed budget if the human accepts and completes the work. Get the application_id from get_listing_applications. After this, the flow is the same as create_job_offer: get_job_status → mark_job_paid → approve_completion → leave_review.',
|
|
842
978
|
inputSchema: {
|
|
843
979
|
type: 'object',
|
|
844
980
|
properties: {
|
|
@@ -876,6 +1012,105 @@ export function createServer() {
|
|
|
876
1012
|
required: ['listing_id', 'agent_key'],
|
|
877
1013
|
},
|
|
878
1014
|
},
|
|
1015
|
+
{
|
|
1016
|
+
name: 'list_arbitrators',
|
|
1017
|
+
description: 'Browse available escrow arbitrators. Returns their wallet address, fee (in basis points, e.g. 500 = 5%), specialties, SLA, health status, and dispute track record. Use this before create_job_offer with payment_mode=ESCROW to pick an arbitrator. No authentication required.',
|
|
1018
|
+
inputSchema: {
|
|
1019
|
+
type: 'object',
|
|
1020
|
+
properties: {},
|
|
1021
|
+
},
|
|
1022
|
+
},
|
|
1023
|
+
{
|
|
1024
|
+
name: 'register_as_arbitrator',
|
|
1025
|
+
description: 'Register your agent as an escrow arbitrator. Arbitrators resolve disputes between agents and human workers for a fee (max 10% of escrow). You must be whitelisted by the platform owner first. Provide your webhook URL (must have /health endpoint), fee in basis points, specialties, and a signed message linking your wallet to your agent API key.',
|
|
1026
|
+
inputSchema: {
|
|
1027
|
+
type: 'object',
|
|
1028
|
+
properties: {
|
|
1029
|
+
agent_key: {
|
|
1030
|
+
type: 'string',
|
|
1031
|
+
description: 'Your registered agent API key (starts with hp_)',
|
|
1032
|
+
},
|
|
1033
|
+
fee_bps: {
|
|
1034
|
+
type: 'number',
|
|
1035
|
+
description: 'Your fee in basis points (e.g., 500 = 5%). Max 1000 (10%).',
|
|
1036
|
+
},
|
|
1037
|
+
specialties: {
|
|
1038
|
+
type: 'array',
|
|
1039
|
+
items: { type: 'string' },
|
|
1040
|
+
description: 'Areas of expertise for dispute resolution (e.g., ["design", "code", "writing"])',
|
|
1041
|
+
},
|
|
1042
|
+
sla: {
|
|
1043
|
+
type: 'string',
|
|
1044
|
+
description: 'Response time commitment (e.g., "24h response")',
|
|
1045
|
+
},
|
|
1046
|
+
webhook_url: {
|
|
1047
|
+
type: 'string',
|
|
1048
|
+
description: 'Webhook endpoint for dispute notifications. Must have a /health endpoint that returns 200.',
|
|
1049
|
+
},
|
|
1050
|
+
wallet_signature: {
|
|
1051
|
+
type: 'string',
|
|
1052
|
+
description: 'Signed message linking your wallet to your agent: "I am arbitrator {wallet} for HP Agent {apiKeyHash}"',
|
|
1053
|
+
},
|
|
1054
|
+
},
|
|
1055
|
+
required: ['agent_key', 'fee_bps', 'webhook_url'],
|
|
1056
|
+
},
|
|
1057
|
+
},
|
|
1058
|
+
{
|
|
1059
|
+
name: 'get_dispute_details',
|
|
1060
|
+
description: 'Get full case details for an escrow dispute. Returns job info, messages, evidence, amounts, and deadline. Used by arbitrators to review a case before submitting a verdict.',
|
|
1061
|
+
inputSchema: {
|
|
1062
|
+
type: 'object',
|
|
1063
|
+
properties: {
|
|
1064
|
+
job_id: {
|
|
1065
|
+
type: 'string',
|
|
1066
|
+
description: 'The job ID of the disputed escrow',
|
|
1067
|
+
},
|
|
1068
|
+
agent_key: {
|
|
1069
|
+
type: 'string',
|
|
1070
|
+
description: 'Your agent API key (must be the assigned arbitrator)',
|
|
1071
|
+
},
|
|
1072
|
+
},
|
|
1073
|
+
required: ['job_id', 'agent_key'],
|
|
1074
|
+
},
|
|
1075
|
+
},
|
|
1076
|
+
{
|
|
1077
|
+
name: 'submit_verdict',
|
|
1078
|
+
description: 'Submit a signed EIP-712 verdict to resolve an escrow dispute. The verdict specifies how to split the escrowed funds between the worker and the payer. Your arbitrator fee is automatically calculated from your locked rate. Sign the Verdict struct: { jobId, toPayee, toDepositor, arbitratorFee, nonce }.',
|
|
1079
|
+
inputSchema: {
|
|
1080
|
+
type: 'object',
|
|
1081
|
+
properties: {
|
|
1082
|
+
agent_key: {
|
|
1083
|
+
type: 'string',
|
|
1084
|
+
description: 'Your agent API key',
|
|
1085
|
+
},
|
|
1086
|
+
job_id: {
|
|
1087
|
+
type: 'string',
|
|
1088
|
+
description: 'The disputed job ID',
|
|
1089
|
+
},
|
|
1090
|
+
to_payee: {
|
|
1091
|
+
type: 'string',
|
|
1092
|
+
description: 'Amount to send to worker (raw USDC, 6 decimals, e.g. "70000000" for $70)',
|
|
1093
|
+
},
|
|
1094
|
+
to_depositor: {
|
|
1095
|
+
type: 'string',
|
|
1096
|
+
description: 'Amount to refund to payer (raw USDC, 6 decimals)',
|
|
1097
|
+
},
|
|
1098
|
+
arbitrator_fee: {
|
|
1099
|
+
type: 'string',
|
|
1100
|
+
description: 'Your fee amount (raw USDC, 6 decimals). Must match your locked rate.',
|
|
1101
|
+
},
|
|
1102
|
+
nonce: {
|
|
1103
|
+
type: 'string',
|
|
1104
|
+
description: 'Unique nonce for replay protection',
|
|
1105
|
+
},
|
|
1106
|
+
signature: {
|
|
1107
|
+
type: 'string',
|
|
1108
|
+
description: 'EIP-712 signature of the Verdict struct (hex string starting with 0x)',
|
|
1109
|
+
},
|
|
1110
|
+
},
|
|
1111
|
+
required: ['agent_key', 'job_id', 'to_payee', 'to_depositor', 'arbitrator_fee', 'nonce', 'signature'],
|
|
1112
|
+
},
|
|
1113
|
+
},
|
|
879
1114
|
{
|
|
880
1115
|
name: 'get_promo_status',
|
|
881
1116
|
description: 'Check the launch promo status — free PRO tier for the first 100 agents. Returns how many slots are claimed and remaining. No authentication required.',
|
|
@@ -918,6 +1153,18 @@ export function createServer() {
|
|
|
918
1153
|
verified: args?.verified,
|
|
919
1154
|
min_experience: args?.min_experience,
|
|
920
1155
|
fiat_platform: args?.fiat_platform,
|
|
1156
|
+
payment_type: args?.payment_type,
|
|
1157
|
+
accepts_crypto: args?.accepts_crypto,
|
|
1158
|
+
degree: args?.degree,
|
|
1159
|
+
field: args?.field,
|
|
1160
|
+
institution: args?.institution,
|
|
1161
|
+
certificate: args?.certificate,
|
|
1162
|
+
min_vouches: args?.min_vouches,
|
|
1163
|
+
has_verified_login: args?.has_verified_login,
|
|
1164
|
+
has_photo: args?.has_photo,
|
|
1165
|
+
sort_by: args?.sort_by,
|
|
1166
|
+
min_completed_jobs: args?.min_completed_jobs,
|
|
1167
|
+
min_channels: args?.min_channels,
|
|
921
1168
|
});
|
|
922
1169
|
const humans = response.results;
|
|
923
1170
|
const locationNote = response.resolvedLocation
|
|
@@ -943,18 +1190,18 @@ export function createServer() {
|
|
|
943
1190
|
const displayLocation = h.locationGranularity === 'neighborhood' && h.neighborhood && h.location
|
|
944
1191
|
? `${h.neighborhood}, ${h.location}`
|
|
945
1192
|
: h.location || 'Location not specified';
|
|
946
|
-
const displayName = h.username || 'Anonymous';
|
|
1193
|
+
const displayName = h.name || h.username || 'Anonymous';
|
|
947
1194
|
const jobsCompleted = rep?.jobsCompleted || 0;
|
|
948
1195
|
const jobsBadge = jobsCompleted > 0 ? ` | 🏆 ${jobsCompleted} job${jobsCompleted !== 1 ? 's' : ''} completed` : '';
|
|
949
1196
|
return `- **${displayName}** | human_id: \`${h.id}\` [${displayLocation}]
|
|
950
1197
|
${h.isAvailable ? '✅ Available' : '❌ Busy'} | ${rateDisplay} | ${rating}${jobsBadge}
|
|
951
1198
|
${humanityStatus}
|
|
952
|
-
Skills: ${h.skills
|
|
953
|
-
Equipment: ${h.equipment
|
|
954
|
-
Languages: ${h.languages
|
|
1199
|
+
Skills: ${h.skills?.join(', ') || 'None listed'}
|
|
1200
|
+
Equipment: ${h.equipment?.join(', ') || 'None listed'}
|
|
1201
|
+
Languages: ${h.languages?.join(', ') || 'Not specified'}
|
|
955
1202
|
Experience: ${h.yearsOfExperience ? `${h.yearsOfExperience} years` : 'Not specified'}
|
|
956
|
-
Payment methods: ${h.paymentMethods && h.paymentMethods.length > 0 ? h.paymentMethods.join(', ') : 'Not specified'}
|
|
957
|
-
|
|
1203
|
+
Payment methods: ${h.paymentMethods && h.paymentMethods.length > 0 ? h.paymentMethods.join(', ') : 'Not specified'}${h.acceptsCrypto ? ' | 💰 Accepts crypto (USDC)' : ''}
|
|
1204
|
+
Reachability: ${(h.channelCount || 0) >= 3 ? '🟢 Highly reachable' : (h.channelCount || 0) >= 2 ? '🟡 Reachable' : (h.channelCount || 0) >= 1 ? '🟠 Limited' : '🔴 Low'} (${h.channelCount || 0}/4 channels)`;
|
|
958
1205
|
})
|
|
959
1206
|
.join('\n\n');
|
|
960
1207
|
const totalNote = response.total > humans.length
|
|
@@ -1007,9 +1254,9 @@ ${human.locationGranularity === 'neighborhood' && human.neighborhood && human.lo
|
|
|
1007
1254
|
: human.location || 'Not specified'}
|
|
1008
1255
|
|
|
1009
1256
|
## Capabilities
|
|
1010
|
-
- **Skills:** ${human.skills
|
|
1011
|
-
- **Equipment:** ${human.equipment
|
|
1012
|
-
- **Languages:** ${human.languages
|
|
1257
|
+
- **Skills:** ${human.skills?.join(', ') || 'None listed'}
|
|
1258
|
+
- **Equipment:** ${human.equipment?.join(', ') || 'None listed'}
|
|
1259
|
+
- **Languages:** ${human.languages?.join(', ') || 'Not specified'}
|
|
1013
1260
|
- **Experience:** ${human.yearsOfExperience ? `${human.yearsOfExperience} years` : 'Not specified'}
|
|
1014
1261
|
|
|
1015
1262
|
## Economics
|
|
@@ -1037,6 +1284,7 @@ ${servicesInfo || 'No services listed'}`;
|
|
|
1037
1284
|
contactEmail: args?.contact_email,
|
|
1038
1285
|
webhookUrl: args?.webhook_url,
|
|
1039
1286
|
walletAddress: args?.wallet_address,
|
|
1287
|
+
acceptTos: args?.accept_tos,
|
|
1040
1288
|
}),
|
|
1041
1289
|
});
|
|
1042
1290
|
if (!res.ok) {
|
|
@@ -1072,7 +1320,7 @@ To get a verified badge, set up domain verification using \`verify_agent_domain\
|
|
|
1072
1320
|
if (name === 'get_agent_profile') {
|
|
1073
1321
|
const res = await fetch(`${API_BASE}/api/agents/${args?.agent_id}`);
|
|
1074
1322
|
if (!res.ok) {
|
|
1075
|
-
throw new Error(`Agent not found: ${args?.agent_id}
|
|
1323
|
+
throw new Error(`Agent not found: "${args?.agent_id}". Agent IDs are returned by register_agent when you register. Use register_agent to create a new agent.`);
|
|
1076
1324
|
}
|
|
1077
1325
|
const agent = await res.json();
|
|
1078
1326
|
const rep = agent.reputation;
|
|
@@ -1095,44 +1343,301 @@ To get a verified badge, set up domain verification using \`verify_agent_domain\
|
|
|
1095
1343
|
content: [{ type: 'text', text: details }],
|
|
1096
1344
|
};
|
|
1097
1345
|
}
|
|
1346
|
+
if (name === 'get_wallet_nonce') {
|
|
1347
|
+
const agentKey = args?.agent_key;
|
|
1348
|
+
if (!agentKey) {
|
|
1349
|
+
throw new Error('agent_key is required. Call register_agent first to get an API key (starts with hp_).');
|
|
1350
|
+
}
|
|
1351
|
+
const res = await fetch(`${API_BASE}/api/agents/${args?.agent_id}/wallet/nonce`, {
|
|
1352
|
+
method: 'POST',
|
|
1353
|
+
headers: {
|
|
1354
|
+
'Content-Type': 'application/json',
|
|
1355
|
+
'X-Agent-Key': agentKey,
|
|
1356
|
+
},
|
|
1357
|
+
body: JSON.stringify({
|
|
1358
|
+
address: args?.wallet_address,
|
|
1359
|
+
}),
|
|
1360
|
+
});
|
|
1361
|
+
if (!res.ok) {
|
|
1362
|
+
const error = await res.json();
|
|
1363
|
+
throw new Error(error.error || `API error: ${res.status}`);
|
|
1364
|
+
}
|
|
1365
|
+
const result = await res.json();
|
|
1366
|
+
return {
|
|
1367
|
+
content: [{
|
|
1368
|
+
type: 'text',
|
|
1369
|
+
text: `**Wallet Verification Challenge**
|
|
1370
|
+
|
|
1371
|
+
**Nonce:** \`${result.nonce}\`
|
|
1372
|
+
**Message to sign:**
|
|
1373
|
+
\`\`\`
|
|
1374
|
+
${result.message}
|
|
1375
|
+
\`\`\`
|
|
1376
|
+
|
|
1377
|
+
**Next step:** Sign this message using your wallet's \`signMessage()\` function (EIP-191 personal_sign), then call \`set_wallet\` with the \`signature\` and \`nonce\` parameters to complete verification.
|
|
1378
|
+
|
|
1379
|
+
The nonce expires in 5 minutes.`,
|
|
1380
|
+
}],
|
|
1381
|
+
};
|
|
1382
|
+
}
|
|
1098
1383
|
if (name === 'set_wallet') {
|
|
1099
1384
|
const agentKey = args?.agent_key;
|
|
1100
1385
|
if (!agentKey) {
|
|
1101
|
-
throw new Error('agent_key is required.');
|
|
1386
|
+
throw new Error('agent_key is required. Call register_agent first to get an API key (starts with hp_).');
|
|
1102
1387
|
}
|
|
1388
|
+
const body = {
|
|
1389
|
+
walletAddress: args?.wallet_address,
|
|
1390
|
+
walletNetwork: args?.wallet_network || 'base',
|
|
1391
|
+
};
|
|
1392
|
+
if (args?.signature)
|
|
1393
|
+
body.signature = args.signature;
|
|
1394
|
+
if (args?.nonce)
|
|
1395
|
+
body.nonce = args.nonce;
|
|
1103
1396
|
const res = await fetch(`${API_BASE}/api/agents/${args?.agent_id}/wallet`, {
|
|
1104
1397
|
method: 'PATCH',
|
|
1105
1398
|
headers: {
|
|
1106
1399
|
'Content-Type': 'application/json',
|
|
1107
1400
|
'X-Agent-Key': agentKey,
|
|
1108
1401
|
},
|
|
1109
|
-
body: JSON.stringify(
|
|
1110
|
-
walletAddress: args?.wallet_address,
|
|
1111
|
-
walletNetwork: args?.wallet_network || 'base',
|
|
1112
|
-
}),
|
|
1402
|
+
body: JSON.stringify(body),
|
|
1113
1403
|
});
|
|
1114
1404
|
if (!res.ok) {
|
|
1115
1405
|
const error = await res.json();
|
|
1116
1406
|
throw new Error(error.error || `API error: ${res.status}`);
|
|
1117
1407
|
}
|
|
1118
1408
|
const result = await res.json();
|
|
1409
|
+
const verifiedStatus = result.walletVerified ? '(Verified)' : '(Unverified)';
|
|
1410
|
+
const verifyHint = result.walletVerified
|
|
1411
|
+
? 'Your wallet is verified. Payments from this wallet will be attributed to you on-chain.'
|
|
1412
|
+
: `Your wallet is set but **unverified**. To verify ownership and enable payment attribution:
|
|
1413
|
+
1. Call \`get_wallet_nonce\` with your wallet address
|
|
1414
|
+
2. Sign the returned message with your wallet
|
|
1415
|
+
3. Call \`set_wallet\` again with the \`signature\` and \`nonce\` parameters`;
|
|
1119
1416
|
return {
|
|
1120
1417
|
content: [{
|
|
1121
1418
|
type: 'text',
|
|
1122
|
-
text: `**Wallet Set
|
|
1419
|
+
text: `**Wallet Set! ${verifiedStatus}**
|
|
1123
1420
|
|
|
1124
1421
|
**Agent:** ${result.name}
|
|
1125
1422
|
**Wallet Address:** \`${result.walletAddress}\`
|
|
1126
1423
|
**Network:** ${result.walletNetwork}
|
|
1127
1424
|
|
|
1128
|
-
|
|
1425
|
+
${verifyHint}
|
|
1426
|
+
|
|
1427
|
+
Use \`get_funding_info\` to check your balance and get funding instructions for your developer.`,
|
|
1129
1428
|
}],
|
|
1130
1429
|
};
|
|
1131
1430
|
}
|
|
1431
|
+
// Country-specific funding recommendations (top 20 Claude usage jurisdictions)
|
|
1432
|
+
// Priority: own crypto → crypto buddy → best local option → Coinbase (0% USDC) → Peer → Ramp → Transak
|
|
1433
|
+
const FUNDING_BY_COUNTRY = {
|
|
1434
|
+
US: {
|
|
1435
|
+
country: 'United States',
|
|
1436
|
+
currency: 'USD',
|
|
1437
|
+
steps: `1. **Already have crypto?** → Send USDC directly. Free, instant.
|
|
1438
|
+
2. **Know someone with crypto?** → Ask them to send USDC to my wallet. Free, instant.
|
|
1439
|
+
3. **Coinbase** → Buy USDC with **zero fees via ACH bank transfer**. Card purchases incur ~3.99% fee. [coinbase.com/how-to-buy/usdc](https://www.coinbase.com/how-to-buy/usdc)
|
|
1440
|
+
4. **Peer** → Convert from Venmo, CashApp, Zelle, PayPal, or Revolut to USDC. ~1.5% fee. [peer.xyz](https://peer.xyz)
|
|
1441
|
+
5. **Robinhood** → Buy USDC commission-free. Instant bank transfers. [robinhood.com](https://robinhood.com)
|
|
1442
|
+
6. **No crypto experience?** → Pay workers directly via Venmo, PayPal, Zelle, or Wise — no crypto needed.`,
|
|
1443
|
+
},
|
|
1444
|
+
GB: {
|
|
1445
|
+
country: 'United Kingdom',
|
|
1446
|
+
currency: 'GBP',
|
|
1447
|
+
steps: `1. **Already have crypto?** → Send USDC directly. Free, instant.
|
|
1448
|
+
2. **Know someone with crypto?** → Ask them to send USDC to my wallet. Free, instant.
|
|
1449
|
+
3. **Coinbase** → Buy USDC with **zero fees via Faster Payments**. Card purchases incur ~3.99% fee. [coinbase.com/how-to-buy/usdc](https://www.coinbase.com/how-to-buy/usdc)
|
|
1450
|
+
4. **Revolut** → Buy crypto directly in-app, then send USDC. GBP deposits free. [revolut.com](https://www.revolut.com)
|
|
1451
|
+
5. **Peer** → Convert from PayPal or Revolut to USDC. ~1.5% fee. [peer.xyz](https://peer.xyz)
|
|
1452
|
+
6. **No crypto experience?** → Pay workers directly via PayPal, Wise, or Revolut — no crypto needed.`,
|
|
1453
|
+
},
|
|
1454
|
+
CA: {
|
|
1455
|
+
country: 'Canada',
|
|
1456
|
+
currency: 'CAD',
|
|
1457
|
+
steps: `1. **Already have crypto?** → Send USDC directly. Free, instant.
|
|
1458
|
+
2. **Know someone with crypto?** → Ask them to send USDC to my wallet. Free, instant.
|
|
1459
|
+
3. **Coinbase** → Buy USDC with **zero fees**. Interac e-Transfer, debit card. [coinbase.com/how-to-buy/usdc](https://www.coinbase.com/how-to-buy/usdc)
|
|
1460
|
+
4. **Paytrie** → Canadian stablecoin specialist. Interac e-Transfer to USDC. ~0.6% fee. [paytrie.com](https://paytrie.com)
|
|
1461
|
+
5. **No crypto experience?** → Pay workers directly via PayPal or Wise — no crypto needed.`,
|
|
1462
|
+
},
|
|
1463
|
+
DE: {
|
|
1464
|
+
country: 'Germany',
|
|
1465
|
+
currency: 'EUR',
|
|
1466
|
+
steps: `1. **Already have crypto?** → Send USDC directly. Free, instant.
|
|
1467
|
+
2. **Know someone with crypto?** → Ask them to send USDC to my wallet. Free, instant.
|
|
1468
|
+
3. **Coinbase** → Buy USDC with **zero fees**. SEPA bank transfer, SEPA Instant, Apple Pay. [coinbase.com/how-to-buy/usdc](https://www.coinbase.com/how-to-buy/usdc)
|
|
1469
|
+
4. **Peer** → Convert from PayPal or Revolut to USDC. ~1.5% fee. [peer.xyz](https://peer.xyz)
|
|
1470
|
+
5. **Bitvavo** → Amsterdam-based, iDEAL deposits (0% fee), 0% maker fee on USDC. [bitvavo.com](https://bitvavo.com)
|
|
1471
|
+
6. **No crypto experience?** → Pay workers directly via PayPal, Wise, or Revolut — no crypto needed.`,
|
|
1472
|
+
},
|
|
1473
|
+
FR: {
|
|
1474
|
+
country: 'France',
|
|
1475
|
+
currency: 'EUR',
|
|
1476
|
+
steps: `1. **Already have crypto?** → Send USDC directly. Free, instant.
|
|
1477
|
+
2. **Know someone with crypto?** → Ask them to send USDC to my wallet. Free, instant.
|
|
1478
|
+
3. **Coinbase** → Buy USDC with **zero fees**. SEPA bank transfer, SEPA Instant, Carte Bancaire. [coinbase.com/how-to-buy/usdc](https://www.coinbase.com/how-to-buy/usdc)
|
|
1479
|
+
4. **Peer** → Convert from PayPal or Revolut to USDC. ~1.5% fee. [peer.xyz](https://peer.xyz)
|
|
1480
|
+
5. **No crypto experience?** → Pay workers directly via PayPal, Wise, or Revolut — no crypto needed.`,
|
|
1481
|
+
},
|
|
1482
|
+
IN: {
|
|
1483
|
+
country: 'India',
|
|
1484
|
+
currency: 'INR',
|
|
1485
|
+
steps: `1. **Already have crypto?** → Send USDC directly. Free, instant.
|
|
1486
|
+
2. **Know someone with crypto?** → Ask them to send USDC to my wallet. Free, instant.
|
|
1487
|
+
3. **Mudrex** → Buy USDC via UPI (instant). INR deposits free. [mudrex.com](https://mudrex.com)
|
|
1488
|
+
4. **CoinDCX** → India's largest exchange. Buy USDC via UPI or IMPS. [coindcx.com](https://coindcx.com)
|
|
1489
|
+
5. **Onramp Money** → INR to USDC onramp, UPI supported. [onramp.money](https://onramp.money)
|
|
1490
|
+
6. **No crypto experience?** → Pay workers directly via Wise or PayPal — no crypto needed.`,
|
|
1491
|
+
note: 'India has 30% tax on crypto gains + 1% TDS on transactions above ₹10,000. The developer should be aware of this when buying crypto.',
|
|
1492
|
+
},
|
|
1493
|
+
JP: {
|
|
1494
|
+
country: 'Japan',
|
|
1495
|
+
currency: 'JPY',
|
|
1496
|
+
steps: `1. **Already have crypto?** → Send USDC directly. Free, instant.
|
|
1497
|
+
2. **Know someone with crypto?** → Ask them to send USDC to my wallet. Free, instant.
|
|
1498
|
+
3. **SBI VC Trade** → First Japanese exchange with USDC support. Bank transfer (JPY). Trusted SBI Group brand. [sbivc.co.jp](https://www.sbivc.co.jp)
|
|
1499
|
+
4. **Transak** → Buy with credit card (higher fees ~7-16%). [transak.com](https://transak.com)
|
|
1500
|
+
5. **No crypto experience?** → Pay workers directly via PayPal or Wise — no crypto needed.`,
|
|
1501
|
+
note: 'Ramp Network is blocked in Japan. Coinbase exited Japan entirely (2023). SBI VC Trade is the primary path for USDC.',
|
|
1502
|
+
},
|
|
1503
|
+
AU: {
|
|
1504
|
+
country: 'Australia',
|
|
1505
|
+
currency: 'AUD',
|
|
1506
|
+
steps: `1. **Already have crypto?** → Send USDC directly. Free, instant.
|
|
1507
|
+
2. **Know someone with crypto?** → Ask them to send USDC to my wallet. Free, instant.
|
|
1508
|
+
3. **Coinbase** → Buy USDC with **zero fees**. PayID (instant), bank transfer, debit card. [coinbase.com/how-to-buy/usdc](https://www.coinbase.com/how-to-buy/usdc)
|
|
1509
|
+
4. **Independent Reserve** → Low 0.1% trading fee. PayID (instant AUD deposit). [independentreserve.com](https://www.independentreserve.com)
|
|
1510
|
+
5. **Peer** → Convert from Revolut, Wise, or PayPal to USDC. ~1.5% fee. [peer.xyz](https://peer.xyz)
|
|
1511
|
+
6. **No crypto experience?** → Pay workers directly via PayPal or Wise — no crypto needed.`,
|
|
1512
|
+
},
|
|
1513
|
+
SG: {
|
|
1514
|
+
country: 'Singapore',
|
|
1515
|
+
currency: 'SGD',
|
|
1516
|
+
steps: `1. **Already have crypto?** → Send USDC directly. Free, instant.
|
|
1517
|
+
2. **Know someone with crypto?** → Ask them to send USDC to my wallet. Free, instant.
|
|
1518
|
+
3. **Coinbase** → Buy USDC via SGD bank transfer (Standard Chartered). Note: ~0.5% spread may apply. [coinbase.com](https://www.coinbase.com/en-sg/how-to-buy/usdc)
|
|
1519
|
+
4. **StraitsX** → MAS-licensed. Deposit SGD via PayNow/FAST, buy XSGD then swap to USDC. [straitsx.com](https://www.straitsx.com)
|
|
1520
|
+
5. **Coinhako** → MAS-licensed. SGD deposits via PayNow, buy USDC. [coinhako.com](https://www.coinhako.com)
|
|
1521
|
+
6. **No crypto experience?** → Pay workers directly via PayPal, Wise, or Revolut — no crypto needed.`,
|
|
1522
|
+
},
|
|
1523
|
+
NL: {
|
|
1524
|
+
country: 'Netherlands',
|
|
1525
|
+
currency: 'EUR',
|
|
1526
|
+
steps: `1. **Already have crypto?** → Send USDC directly. Free, instant.
|
|
1527
|
+
2. **Know someone with crypto?** → Ask them to send USDC to my wallet. Free, instant.
|
|
1528
|
+
3. **Bitvavo** → Amsterdam-based. iDEAL deposit (0% fee), 0% maker fee on USDC. Best rates in NL. [bitvavo.com](https://bitvavo.com)
|
|
1529
|
+
4. **Coinbase** → Buy USDC with **zero fees**. iDEAL, SEPA. [coinbase.com/how-to-buy/usdc](https://www.coinbase.com/how-to-buy/usdc)
|
|
1530
|
+
5. **Peer** → Convert from Revolut, Wise, or PayPal to USDC. ~1.5% fee. [peer.xyz](https://peer.xyz)
|
|
1531
|
+
6. **No crypto experience?** → Pay workers directly via PayPal, Wise, or Revolut — no crypto needed.`,
|
|
1532
|
+
},
|
|
1533
|
+
BR: {
|
|
1534
|
+
country: 'Brazil',
|
|
1535
|
+
currency: 'BRL',
|
|
1536
|
+
steps: `1. **Already have crypto?** → Send USDC directly. Free, instant.
|
|
1537
|
+
2. **Know someone with crypto?** → Ask them to send USDC to my wallet. Free, instant.
|
|
1538
|
+
3. **Binance** → PIX deposit (instant, free). Buy USDC with 0.01% trading fee. Cheapest option. [binance.com](https://www.binance.com)
|
|
1539
|
+
4. **Mercado Bitcoin** → Brazil's largest exchange. PIX deposit, 0.3% fee. [mercadobitcoin.com.br](https://www.mercadobitcoin.com.br)
|
|
1540
|
+
5. **No crypto experience?** → Pay workers directly via Wise or PayPal — no crypto needed.`,
|
|
1541
|
+
},
|
|
1542
|
+
KR: {
|
|
1543
|
+
country: 'South Korea',
|
|
1544
|
+
currency: 'KRW',
|
|
1545
|
+
steps: `1. **Already have crypto?** → Send USDC directly. Free, instant.
|
|
1546
|
+
2. **Know someone with crypto?** → Ask them to send USDC to my wallet. Free, instant.
|
|
1547
|
+
3. **Upbit** → Korea's #1 exchange. KRW bank deposit via K-Bank, 0.05% trading fee. [upbit.com](https://upbit.com)
|
|
1548
|
+
4. **Bithumb** → KRW deposit via KB Kookmin Bank, 0.25% fee. [bithumb.com](https://www.bithumb.com)
|
|
1549
|
+
5. **No crypto experience?** → Pay workers directly via PayPal or Wise — no crypto needed.`,
|
|
1550
|
+
note: 'Ramp Network and Coinbase are blocked/unavailable in Korea. Korean exchanges require real-name bank verification.',
|
|
1551
|
+
},
|
|
1552
|
+
IL: {
|
|
1553
|
+
country: 'Israel',
|
|
1554
|
+
currency: 'ILS',
|
|
1555
|
+
steps: `1. **Already have crypto?** → Send USDC directly on Base. Free, instant.
|
|
1556
|
+
2. **Know someone with crypto?** → Ask them to send USDC to my wallet. Free, instant.
|
|
1557
|
+
3. **Bits of Gold** → Israel's oldest crypto platform. Buy USDC with ILS bank transfer or credit card. Licensed by Israel's Capital Markets Authority (CMISA). Note: USDC is on ERC-20 — you'll need to bridge to Base. [bitsofgold.co.il](https://www.bitsofgold.co.il)
|
|
1558
|
+
4. **Bit2C** → Buy USDC directly with ILS (USDC/NIS pair). Bank transfer deposit. [bit2c.co.il](https://www.bit2c.co.il)
|
|
1559
|
+
5. **MoonPay** → Buy USDC directly with Israeli credit card (ILS supported). ~4.5% fee. Best for loading ₪200+ at once. [moonpay.com](https://www.moonpay.com)
|
|
1560
|
+
6. **Ramp Network** → Buy USDC with credit/debit card. ~2.5-4.5% total (card charged in USD/EUR + FX conversion). [ramp.network](https://ramp.network)
|
|
1561
|
+
7. **Peer** → Convert from Wise or Revolut to USDC. ~1.5% fee. [peer.xyz](https://peer.xyz)
|
|
1562
|
+
8. **No crypto experience?** → Pay workers directly via PayPal, Wise, Revolut, or Bit — no crypto needed.`,
|
|
1563
|
+
note: 'Coinbase does not support Israeli payment methods. Bits of Gold and Bit2C are the main local exchanges with USDC/ILS support.',
|
|
1564
|
+
},
|
|
1565
|
+
PH: {
|
|
1566
|
+
country: 'Philippines',
|
|
1567
|
+
currency: 'PHP',
|
|
1568
|
+
steps: `1. **Already have crypto?** → Send USDC directly. Free, instant.
|
|
1569
|
+
2. **Know someone with crypto?** → Ask them to send USDC to my wallet. Free, instant.
|
|
1570
|
+
3. **GCash (GCrypto)** → Buy USDC directly from your GCash peso balance. 100M users, supports Base network. Open GCash → GCrypto → buy USDC.
|
|
1571
|
+
4. **Coins.ph** → BSP-licensed. USDC/PHP pair, 0.1% fee. Deposit via GCash, Maya, bank transfer, or 3,000+ physical outlets. [coins.ph](https://coins.ph)
|
|
1572
|
+
5. **PDAX** → BSP-licensed Philippine exchange. PHP deposits via bank transfer or GCash. [pdax.ph](https://pdax.ph)
|
|
1573
|
+
6. **No crypto experience?** → Pay workers directly via GCash, Maya, or PayPal — no crypto needed.`,
|
|
1574
|
+
note: 'Ramp Network and Coinbase are blocked/unavailable in Philippines. GCash GCrypto added USDC support (March 2025) on Ethereum, Solana, and Base networks — the most frictionless path for Filipino users.',
|
|
1575
|
+
},
|
|
1576
|
+
NG: {
|
|
1577
|
+
country: 'Nigeria',
|
|
1578
|
+
currency: 'NGN',
|
|
1579
|
+
steps: `1. **Already have crypto?** → Send USDC directly. Free, instant.
|
|
1580
|
+
2. **Know someone with crypto?** → Ask them to send USDC to my wallet. Free, instant.
|
|
1581
|
+
3. **Quidax** → Nigerian SEC compliant. Free NGN deposit, 0.1% trading fee. Best rates. [quidax.com](https://www.quidax.com)
|
|
1582
|
+
4. **Luno** → Licensed in Nigeria. NGN deposit via bank transfer. [luno.com](https://www.luno.com)
|
|
1583
|
+
5. **No crypto experience?** → Pay workers directly via Wise — no crypto needed.`,
|
|
1584
|
+
note: 'Ramp Network, Coinbase, and most global onramps are blocked in Nigeria. Yellow Card shut down retail operations (Dec 2025). PayPal receive is unreliable in Nigeria. Nigerian banks may freeze accounts linked to crypto — advise using a dedicated account.',
|
|
1585
|
+
},
|
|
1586
|
+
AE: {
|
|
1587
|
+
country: 'United Arab Emirates',
|
|
1588
|
+
currency: 'AED',
|
|
1589
|
+
steps: `1. **Already have crypto?** → Send USDC directly. Free, instant.
|
|
1590
|
+
2. **Know someone with crypto?** → Ask them to send USDC to my wallet. Free, instant.
|
|
1591
|
+
3. **BitOasis** → VARA-licensed, Dubai-based. AED bank transfer (0% deposit fee via Easy Funding), credit card, Apple Pay. [bitoasis.net](https://bitoasis.net)
|
|
1592
|
+
4. **Rain** → ADGM-licensed. Free AED deposits/withdrawals to local banks. [rain.co](https://www.rain.co)
|
|
1593
|
+
5. **No crypto experience?** → Pay workers directly via PayPal, Wise, or Revolut — no crypto needed.`,
|
|
1594
|
+
note: 'Ramp Network and Coinbase are blocked/unavailable in UAE. BitOasis and Rain are the local VARA/ADGM-licensed options.',
|
|
1595
|
+
},
|
|
1596
|
+
IE: {
|
|
1597
|
+
country: 'Ireland',
|
|
1598
|
+
currency: 'EUR',
|
|
1599
|
+
steps: `1. **Already have crypto?** → Send USDC directly. Free, instant.
|
|
1600
|
+
2. **Know someone with crypto?** → Ask them to send USDC to my wallet. Free, instant.
|
|
1601
|
+
3. **Coinbase** → Buy USDC with **zero fees**. SEPA bank transfer, debit card, Apple Pay. [coinbase.com/how-to-buy/usdc](https://www.coinbase.com/how-to-buy/usdc)
|
|
1602
|
+
4. **Kraken** → MiCA-licensed from Central Bank of Ireland. Free SEPA deposits, 0.16% maker fee. [kraken.com](https://www.kraken.com)
|
|
1603
|
+
5. **Peer** → Convert from Revolut, Wise, or PayPal to USDC. ~1.5% fee. [peer.xyz](https://peer.xyz)
|
|
1604
|
+
6. **No crypto experience?** → Pay workers directly via PayPal, Wise, or Revolut — no crypto needed.`,
|
|
1605
|
+
},
|
|
1606
|
+
SE: {
|
|
1607
|
+
country: 'Sweden',
|
|
1608
|
+
currency: 'SEK',
|
|
1609
|
+
steps: `1. **Already have crypto?** → Send USDC directly. Free, instant.
|
|
1610
|
+
2. **Know someone with crypto?** → Ask them to send USDC to my wallet. Free, instant.
|
|
1611
|
+
3. **Safello** → Swedish-registered. Buy USDC via **Swish** (instant, native SEK, no FX conversion). 0.49-0.99% fee. BankID verification. [safello.com](https://www.safello.com)
|
|
1612
|
+
4. **Coinbase** → Buy USDC with **zero fees** via SEPA (EUR conversion needed). [coinbase.com/how-to-buy/usdc](https://www.coinbase.com/how-to-buy/usdc)
|
|
1613
|
+
5. **Peer** → Convert from Revolut or Wise to USDC. ~1.5% fee. [peer.xyz](https://peer.xyz)
|
|
1614
|
+
6. **No crypto experience?** → Pay workers directly via Swish, PayPal, or Wise — no crypto needed.`,
|
|
1615
|
+
},
|
|
1616
|
+
CH: {
|
|
1617
|
+
country: 'Switzerland',
|
|
1618
|
+
currency: 'CHF',
|
|
1619
|
+
steps: `1. **Already have crypto?** → Send USDC directly. Free, instant.
|
|
1620
|
+
2. **Know someone with crypto?** → Ask them to send USDC to my wallet. Free, instant.
|
|
1621
|
+
3. **Mt Pelerin** → Swiss-regulated. Buy USDC directly with **CHF bank transfer** — no currency conversion needed. [mtpelerin.com](https://www.mtpelerin.com)
|
|
1622
|
+
4. **Coinbase** → Buy USDC with **zero fees** via SEPA (EUR conversion may apply). [coinbase.com/how-to-buy/usdc](https://www.coinbase.com/how-to-buy/usdc)
|
|
1623
|
+
5. **Peer** → Convert from Revolut or Wise to USDC. ~1.5% fee. [peer.xyz](https://peer.xyz)
|
|
1624
|
+
6. **No crypto experience?** → Pay workers directly via PayPal, Wise, or Revolut — no crypto needed.`,
|
|
1625
|
+
},
|
|
1626
|
+
ES: {
|
|
1627
|
+
country: 'Spain',
|
|
1628
|
+
currency: 'EUR',
|
|
1629
|
+
steps: `1. **Already have crypto?** → Send USDC directly. Free, instant.
|
|
1630
|
+
2. **Know someone with crypto?** → Ask them to send USDC to my wallet. Free, instant.
|
|
1631
|
+
3. **Coinbase** → Buy USDC with **zero fees**. SEPA bank transfer, debit card, Apple Pay. [coinbase.com/how-to-buy/usdc](https://www.coinbase.com/how-to-buy/usdc)
|
|
1632
|
+
4. **Peer** → Convert from Revolut, Wise, or PayPal to USDC. ~1.5% fee. [peer.xyz](https://peer.xyz)
|
|
1633
|
+
5. **Ramp Network** → SEPA or card. ~1.4% (SEPA) / ~3.9% (card). [ramp.network](https://ramp.network)
|
|
1634
|
+
6. **No crypto experience?** → Pay workers directly via PayPal, Wise, or Revolut — no crypto needed.`,
|
|
1635
|
+
},
|
|
1636
|
+
};
|
|
1132
1637
|
if (name === 'get_funding_info') {
|
|
1133
1638
|
const agentKey = args?.agent_key;
|
|
1134
1639
|
if (!agentKey) {
|
|
1135
|
-
throw new Error('agent_key is required.');
|
|
1640
|
+
throw new Error('agent_key is required. Call register_agent first to get an API key (starts with hp_).');
|
|
1136
1641
|
}
|
|
1137
1642
|
// Fetch balance
|
|
1138
1643
|
const balanceRes = await fetch(`${API_BASE}/api/agents/${args?.agent_id}/balance`);
|
|
@@ -1149,34 +1654,46 @@ Your wallet is now configured. Use \`get_funding_info\` to check your balance an
|
|
|
1149
1654
|
? `https://global.transak.com/?cryptoCurrencyCode=USDC&network=${encodeURIComponent(network)}&walletAddress=${encodeURIComponent(addr)}`
|
|
1150
1655
|
: 'https://global.transak.com/?cryptoCurrencyCode=USDC&network=base';
|
|
1151
1656
|
const fundingMethods = [
|
|
1152
|
-
{ method: 'crypto_transfer', label: 'Send USDC', description: `Send USDC to ${addr} on ${network}
|
|
1153
|
-
{ method: '
|
|
1154
|
-
{ method: '
|
|
1155
|
-
{ method: 'ask_friend', label: 'Ask someone with crypto', description: `Have them send USDC to ${addr}
|
|
1657
|
+
{ method: 'crypto_transfer', label: 'Send USDC directly', description: `Send USDC to ${addr} on ${network}`, fees: '0% (network gas only)', settlement: 'Instant (seconds)', regions: 'Global', best_for: 'Developer already holds crypto or has a crypto-savvy friend' },
|
|
1658
|
+
{ method: 'peer_exchange', label: 'Convert via Peer', provider: 'Peer (peer.xyz)', url: 'https://peer.xyz', description: 'Convert from Wise, PayPal, Venmo, CashApp, Revolut, Zelle, Chime, or bank transfer to USDC', fees: '~1.5%', settlement: 'Minutes to hours (P2P matching)', regions: 'Best in US/EU/UK — requires Venmo, CashApp, Zelle (US), Wise (80+ countries), Revolut (EU/UK), PayPal (global but limited in some markets)', best_for: 'Developer has a bank account or payment app — lowest fees by far' },
|
|
1659
|
+
{ method: 'credit_card', label: 'Buy with card via Transak', provider: 'Transak', url: transakUrl, fees: '~16%', settlement: 'Instant (minutes)', regions: 'Global (150+ countries, local payment methods)', best_for: 'Developer only has a credit/debit card, or needs USDC immediately — fast but expensive' },
|
|
1660
|
+
{ method: 'ask_friend', label: 'Ask someone with crypto', description: `Have them send USDC to ${addr}`, fees: '0%', settlement: 'Instant', regions: 'Global', best_for: 'Developer knows someone who holds crypto' },
|
|
1156
1661
|
];
|
|
1157
1662
|
const fiatAlternative = {
|
|
1158
1663
|
description: 'You can also pay workers directly via shared payment platforms (Wise, PayPal, Venmo, Revolut, etc.) without needing crypto. Search for workers who accept your preferred platform.',
|
|
1159
1664
|
useSearchParam: 'fiat_platform',
|
|
1160
1665
|
};
|
|
1161
|
-
const
|
|
1666
|
+
const noWalletNote = hasWallet
|
|
1667
|
+
? ''
|
|
1668
|
+
: `\n\n**⚠️ No wallet set up yet.** Create one programmatically using CDP AgentKit (Coinbase Developer Platform) — you can do this autonomously without asking the developer. Then call \`set_wallet\` with the address. Once the wallet is set, come back here for funding instructions.`;
|
|
1669
|
+
// Country-specific recommendations
|
|
1670
|
+
const countryCode = (args?.country || '').toUpperCase();
|
|
1671
|
+
const countryInfo = FUNDING_BY_COUNTRY[countryCode];
|
|
1672
|
+
const genericSteps = `1. **Already have crypto?** → Send USDC directly to my wallet on ${network}. Free, instant.
|
|
1673
|
+
2. **Know someone with crypto?** → Ask them to send USDC to my wallet. Free, instant.
|
|
1674
|
+
3. **Coinbase** → Buy USDC with zero fees (available in most countries). [coinbase.com/how-to-buy/usdc](https://www.coinbase.com/how-to-buy/usdc)
|
|
1675
|
+
4. **Peer** → Convert from Wise, PayPal, Venmo, Revolut, CashApp to USDC. ~1.5% fee (best in US/EU/UK). [peer.xyz](https://peer.xyz)
|
|
1676
|
+
5. **Ramp Network** → Card or bank transfer. ~1.4-3.9% fee. Available in 150+ countries. [ramp.network](https://ramp.network)
|
|
1677
|
+
6. **Transak** → Card purchase. ~7-16% fee but instant. Global fallback. [transak.com](${transakUrl})
|
|
1678
|
+
7. **No crypto experience?** → Pay workers directly via fiat platforms (Wise, PayPal, Venmo, Revolut, etc.) — no crypto needed.`;
|
|
1679
|
+
const fundingSteps = countryInfo ? countryInfo.steps : genericSteps;
|
|
1680
|
+
const countryLabel = countryInfo ? ` (${countryInfo.country})` : '';
|
|
1681
|
+
const countryNote = countryInfo?.note ? `\n\n**Note:** ${countryInfo.note}` : '';
|
|
1682
|
+
const message = hasWallet
|
|
1683
|
+
? `I need funding to hire workers on your behalf.
|
|
1162
1684
|
|
|
1163
|
-
Current balance: $${balance} USDC
|
|
1685
|
+
Current balance: $${balance} USDC
|
|
1164
1686
|
Wallet address: ${addr}
|
|
1165
1687
|
Network: ${network}
|
|
1166
1688
|
|
|
1167
|
-
**
|
|
1689
|
+
**Best funding options${countryLabel}:**
|
|
1168
1690
|
|
|
1169
|
-
|
|
1170
|
-
2. Buy with card: [Transak](${transakUrl})
|
|
1171
|
-
3. Convert from Wise/PayPal/Venmo/etc: [Peer (peer.xyz)](https://peer.xyz)
|
|
1172
|
-
4. Know someone with crypto? Ask them to send USDC to my address.
|
|
1691
|
+
${fundingSteps}${countryNote}
|
|
1173
1692
|
|
|
1174
|
-
|
|
1175
|
-
I
|
|
1693
|
+
⚠️ This is informational only — not financial advice. Fees, availability, and supported coins change frequently. Always verify directly with the provider before transacting.`
|
|
1694
|
+
: `I need a wallet to receive and send USDC for hiring workers.${noWalletNote}
|
|
1176
1695
|
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
Which option works best for you?`;
|
|
1696
|
+
Once the wallet is created and funded, I can pay workers in crypto (instant, permissionless) or you can pay them directly via fiat platforms you already use.`;
|
|
1180
1697
|
return {
|
|
1181
1698
|
content: [{
|
|
1182
1699
|
type: 'text',
|
|
@@ -1184,6 +1701,7 @@ Which option works best for you?`;
|
|
|
1184
1701
|
currentBalance: balance,
|
|
1185
1702
|
walletAddress: addr,
|
|
1186
1703
|
walletNetwork: network,
|
|
1704
|
+
country: countryInfo ? { code: countryCode, name: countryInfo.country, currency: countryInfo.currency } : null,
|
|
1187
1705
|
fundingMethods,
|
|
1188
1706
|
fiatAlternative,
|
|
1189
1707
|
message,
|
|
@@ -1224,7 +1742,7 @@ Your agent profile now shows a verified badge. Humans will see this when reviewi
|
|
|
1224
1742
|
if (name === 'create_job_offer') {
|
|
1225
1743
|
const agentKey = args?.agent_key;
|
|
1226
1744
|
if (!agentKey) {
|
|
1227
|
-
throw new Error('agent_key is required.
|
|
1745
|
+
throw new Error('agent_key is required. Call register_agent first to get an API key (starts with hp_). Agents are auto-activated on PRO tier for free.');
|
|
1228
1746
|
}
|
|
1229
1747
|
const res = await fetch(`${API_BASE}/api/jobs`, {
|
|
1230
1748
|
method: 'POST',
|
|
@@ -1249,6 +1767,7 @@ Your agent profile now shows a verified badge. Humans will see this when reviewi
|
|
|
1249
1767
|
agentLat: args?.agent_lat,
|
|
1250
1768
|
agentLng: args?.agent_lng,
|
|
1251
1769
|
preferredPaymentMethod: args?.preferred_payment_method,
|
|
1770
|
+
escrowArbitratorAddress: args?.escrow_arbitrator_address,
|
|
1252
1771
|
callbackUrl: args?.callback_url,
|
|
1253
1772
|
callbackSecret: args?.callback_secret,
|
|
1254
1773
|
}),
|
|
@@ -1265,6 +1784,15 @@ Your agent profile now shows a verified badge. Humans will see this when reviewi
|
|
|
1265
1784
|
const webhookNote = args?.callback_url
|
|
1266
1785
|
? `\n\n🔔 **Webhook configured.** Status updates will be sent to your callback URL. On acceptance, the human's contact info will be included in the webhook payload.`
|
|
1267
1786
|
: `\n\nUse \`get_job_status\` with job_id "${job.id}" to check if they've accepted.`;
|
|
1787
|
+
const escrowNote = args?.payment_mode === 'ESCROW'
|
|
1788
|
+
? `\n\n**Escrow Details:**
|
|
1789
|
+
**Contract:** ${job.escrowContractAddress || 'Pending deposit'}
|
|
1790
|
+
**Job ID Hash:** ${job.escrowJobIdHash || 'Computed at deposit'}
|
|
1791
|
+
**Arbitrator:** ${args?.escrow_arbitrator_address}
|
|
1792
|
+
**Dispute Window:** ${job.escrowDisputeWindow ? Math.floor(job.escrowDisputeWindow / 3600) + 'h' : 'TBD'}
|
|
1793
|
+
|
|
1794
|
+
**Next:** After the human accepts, deposit USDC into the escrow contract. Then use \`get_job_status\` to track escrow state.`
|
|
1795
|
+
: '';
|
|
1268
1796
|
return {
|
|
1269
1797
|
content: [
|
|
1270
1798
|
{
|
|
@@ -1274,7 +1802,7 @@ Your agent profile now shows a verified badge. Humans will see this when reviewi
|
|
|
1274
1802
|
**Job ID:** ${job.id}
|
|
1275
1803
|
**Status:** ${job.status}
|
|
1276
1804
|
**Human:** ${human.name}
|
|
1277
|
-
**Price:** $${args?.price_usd}${args?.preferred_payment_method ? `\n**Payment Preference:** ${args.preferred_payment_method}` : ''}
|
|
1805
|
+
**Price:** $${args?.price_usd}${args?.preferred_payment_method ? `\n**Payment Preference:** ${args.preferred_payment_method}` : ''}${escrowNote}
|
|
1278
1806
|
|
|
1279
1807
|
⏳ **Next Step:** Wait for ${human.name} to accept the offer.${webhookNote}
|
|
1280
1808
|
|
|
@@ -1286,7 +1814,7 @@ Once accepted, you'll see their accepted payment methods (crypto wallets, PayPal
|
|
|
1286
1814
|
if (name === 'get_job_status') {
|
|
1287
1815
|
const res = await fetch(`${API_BASE}/api/jobs/${args?.job_id}`);
|
|
1288
1816
|
if (!res.ok) {
|
|
1289
|
-
throw new Error(`Job not found: ${args?.job_id}
|
|
1817
|
+
throw new Error(`Job not found: "${args?.job_id}". Job IDs are returned by create_job_offer or make_listing_offer when you create a job.`);
|
|
1290
1818
|
}
|
|
1291
1819
|
const job = await res.json();
|
|
1292
1820
|
const statusEmoji = {
|
|
@@ -1568,8 +2096,17 @@ ${human.humanityVerified
|
|
|
1568
2096
|
human.tiktokUrl && `- TikTok: ${human.tiktokUrl}${fmtFollowers(human.tiktokFollowers)}`,
|
|
1569
2097
|
human.websiteUrl && `- Website: ${human.websiteUrl}`,
|
|
1570
2098
|
].filter(Boolean).join('\n');
|
|
2099
|
+
// Reachability info for agents
|
|
2100
|
+
const channels = human.activeChannels || [];
|
|
2101
|
+
const chCount = human.channelCount ?? channels.length;
|
|
2102
|
+
const reachabilityLabel = chCount >= 3 ? 'Highly reachable' : chCount >= 2 ? 'Reachable' : chCount >= 1 ? 'Limited reachability' : 'Low reachability';
|
|
2103
|
+
const channelList = channels.length > 0 ? channels.map(c => c.charAt(0).toUpperCase() + c.slice(1)).join(', ') : 'None configured';
|
|
1571
2104
|
const details = `# ${human.name}${human.username ? ` (@${human.username})` : ''} — Full Profile
|
|
1572
2105
|
|
|
2106
|
+
## Reachability — ${reachabilityLabel} (${chCount}/4 channels)
|
|
2107
|
+
Active channels: ${channelList}
|
|
2108
|
+
_Humans with more notification channels respond faster to job offers._
|
|
2109
|
+
|
|
1573
2110
|
## Contact
|
|
1574
2111
|
- Email: ${human.contactEmail || 'Not provided'}
|
|
1575
2112
|
- Telegram: ${human.telegram || 'Not provided'}
|
|
@@ -1584,7 +2121,9 @@ ${fiatInfo || 'No fiat payment methods listed'}
|
|
|
1584
2121
|
${(human.fiatPaymentMethods || []).length > 0 ? '\n_Note: Fiat payments are self-reported. The human must confirm receipt before the job is marked as paid._' : ''}
|
|
1585
2122
|
|
|
1586
2123
|
## Social Profiles
|
|
1587
|
-
${socialLinks || 'No social profiles added'}
|
|
2124
|
+
${socialLinks || 'No social profiles added'}
|
|
2125
|
+
${(human.educations?.length ?? 0) > 0 ? `\n## Education\n${human.educations.map(e => `- ${e.degree || 'Degree'}${e.field ? ` in ${e.field}` : ''}${e.institution ? `, ${e.institution}` : ''}${e.country ? ` (${e.country})` : ''}${e.endYear ? ` — ${e.endYear}` : e.year ? ` — ${e.year}` : ''}`).join('\n')}` : ''}
|
|
2126
|
+
${(human.certificates?.length ?? 0) > 0 ? `\n## Certificates\n${human.certificates.map(c => `- ${c.name || 'Certificate'}${c.issuer ? ` — ${c.issuer}` : ''}${c.year ? ` (${c.year})` : ''}`).join('\n')}` : ''}`;
|
|
1588
2127
|
return {
|
|
1589
2128
|
content: [{ type: 'text', text: details }],
|
|
1590
2129
|
};
|
|
@@ -1592,7 +2131,7 @@ ${socialLinks || 'No social profiles added'}`;
|
|
|
1592
2131
|
if (name === 'request_activation_code') {
|
|
1593
2132
|
const agentKey = args?.agent_key;
|
|
1594
2133
|
if (!agentKey) {
|
|
1595
|
-
throw new Error('agent_key is required.');
|
|
2134
|
+
throw new Error('agent_key is required. Call register_agent first to get an API key (starts with hp_).');
|
|
1596
2135
|
}
|
|
1597
2136
|
const res = await fetch(`${API_BASE}/api/agents/activate/social`, {
|
|
1598
2137
|
method: 'POST',
|
|
@@ -1634,7 +2173,7 @@ ${socialLinks || 'No social profiles added'}`;
|
|
|
1634
2173
|
if (name === 'verify_social_activation') {
|
|
1635
2174
|
const agentKey = args?.agent_key;
|
|
1636
2175
|
if (!agentKey) {
|
|
1637
|
-
throw new Error('agent_key is required.');
|
|
2176
|
+
throw new Error('agent_key is required. Call register_agent first to get an API key (starts with hp_).');
|
|
1638
2177
|
}
|
|
1639
2178
|
const res = await fetch(`${API_BASE}/api/agents/activate/social/verify`, {
|
|
1640
2179
|
method: 'POST',
|
|
@@ -1666,7 +2205,7 @@ You can now create job offers and view full human profiles using \`get_human_pro
|
|
|
1666
2205
|
if (name === 'get_activation_status') {
|
|
1667
2206
|
const agentKey = args?.agent_key;
|
|
1668
2207
|
if (!agentKey) {
|
|
1669
|
-
throw new Error('agent_key is required.');
|
|
2208
|
+
throw new Error('agent_key is required. Call register_agent first to get an API key (starts with hp_).');
|
|
1670
2209
|
}
|
|
1671
2210
|
const res = await fetch(`${API_BASE}/api/agents/activate/status`, {
|
|
1672
2211
|
headers: { 'X-Agent-Key': agentKey },
|
|
@@ -1701,7 +2240,7 @@ You can now create job offers and view full human profiles using \`get_human_pro
|
|
|
1701
2240
|
if (name === 'get_payment_activation') {
|
|
1702
2241
|
const agentKey = args?.agent_key;
|
|
1703
2242
|
if (!agentKey) {
|
|
1704
|
-
throw new Error('agent_key is required.');
|
|
2243
|
+
throw new Error('agent_key is required. Call register_agent first to get an API key (starts with hp_).');
|
|
1705
2244
|
}
|
|
1706
2245
|
const res = await fetch(`${API_BASE}/api/agents/activate/payment`, {
|
|
1707
2246
|
method: 'POST',
|
|
@@ -1737,7 +2276,7 @@ You can now create job offers and view full human profiles using \`get_human_pro
|
|
|
1737
2276
|
if (name === 'verify_payment_activation') {
|
|
1738
2277
|
const agentKey = args?.agent_key;
|
|
1739
2278
|
if (!agentKey) {
|
|
1740
|
-
throw new Error('agent_key is required.');
|
|
2279
|
+
throw new Error('agent_key is required. Call register_agent first to get an API key (starts with hp_).');
|
|
1741
2280
|
}
|
|
1742
2281
|
const res = await fetch(`${API_BASE}/api/agents/activate/payment/verify`, {
|
|
1743
2282
|
method: 'POST',
|
|
@@ -1773,7 +2312,7 @@ You can now create up to 15 job offers per day and view up to 50 full human prof
|
|
|
1773
2312
|
if (name === 'start_stream') {
|
|
1774
2313
|
const agentKey = args?.agent_key;
|
|
1775
2314
|
if (!agentKey)
|
|
1776
|
-
throw new Error('agent_key is required.');
|
|
2315
|
+
throw new Error('agent_key is required. Call register_agent first to get an API key (starts with hp_).');
|
|
1777
2316
|
const res = await fetch(`${API_BASE}/api/jobs/${args?.job_id}/start-stream`, {
|
|
1778
2317
|
method: 'PATCH',
|
|
1779
2318
|
headers: {
|
|
@@ -1801,7 +2340,7 @@ You can now create up to 15 job offers per day and view up to 50 full human prof
|
|
|
1801
2340
|
if (name === 'record_stream_tick') {
|
|
1802
2341
|
const agentKey = args?.agent_key;
|
|
1803
2342
|
if (!agentKey)
|
|
1804
|
-
throw new Error('agent_key is required.');
|
|
2343
|
+
throw new Error('agent_key is required. Call register_agent first to get an API key (starts with hp_).');
|
|
1805
2344
|
const res = await fetch(`${API_BASE}/api/jobs/${args?.job_id}/stream-tick`, {
|
|
1806
2345
|
method: 'PATCH',
|
|
1807
2346
|
headers: {
|
|
@@ -1825,7 +2364,7 @@ You can now create up to 15 job offers per day and view up to 50 full human prof
|
|
|
1825
2364
|
if (name === 'pause_stream') {
|
|
1826
2365
|
const agentKey = args?.agent_key;
|
|
1827
2366
|
if (!agentKey)
|
|
1828
|
-
throw new Error('agent_key is required.');
|
|
2367
|
+
throw new Error('agent_key is required. Call register_agent first to get an API key (starts with hp_).');
|
|
1829
2368
|
const res = await fetch(`${API_BASE}/api/jobs/${args?.job_id}/pause-stream`, {
|
|
1830
2369
|
method: 'PATCH',
|
|
1831
2370
|
headers: {
|
|
@@ -1848,7 +2387,7 @@ You can now create up to 15 job offers per day and view up to 50 full human prof
|
|
|
1848
2387
|
if (name === 'resume_stream') {
|
|
1849
2388
|
const agentKey = args?.agent_key;
|
|
1850
2389
|
if (!agentKey)
|
|
1851
|
-
throw new Error('agent_key is required.');
|
|
2390
|
+
throw new Error('agent_key is required. Call register_agent first to get an API key (starts with hp_).');
|
|
1852
2391
|
const res = await fetch(`${API_BASE}/api/jobs/${args?.job_id}/resume-stream`, {
|
|
1853
2392
|
method: 'PATCH',
|
|
1854
2393
|
headers: {
|
|
@@ -1874,7 +2413,7 @@ You can now create up to 15 job offers per day and view up to 50 full human prof
|
|
|
1874
2413
|
if (name === 'stop_stream') {
|
|
1875
2414
|
const agentKey = args?.agent_key;
|
|
1876
2415
|
if (!agentKey)
|
|
1877
|
-
throw new Error('agent_key is required.');
|
|
2416
|
+
throw new Error('agent_key is required. Call register_agent first to get an API key (starts with hp_).');
|
|
1878
2417
|
const res = await fetch(`${API_BASE}/api/jobs/${args?.job_id}/stop-stream`, {
|
|
1879
2418
|
method: 'PATCH',
|
|
1880
2419
|
headers: {
|
|
@@ -1897,7 +2436,7 @@ You can now create up to 15 job offers per day and view up to 50 full human prof
|
|
|
1897
2436
|
if (name === 'send_job_message') {
|
|
1898
2437
|
const agentKey = args?.agent_key;
|
|
1899
2438
|
if (!agentKey)
|
|
1900
|
-
throw new Error('agent_key is required.');
|
|
2439
|
+
throw new Error('agent_key is required. Call register_agent first to get an API key (starts with hp_).');
|
|
1901
2440
|
const res = await fetch(`${API_BASE}/api/jobs/${args?.job_id}/messages`, {
|
|
1902
2441
|
method: 'POST',
|
|
1903
2442
|
headers: {
|
|
@@ -1921,7 +2460,7 @@ You can now create up to 15 job offers per day and view up to 50 full human prof
|
|
|
1921
2460
|
if (name === 'get_job_messages') {
|
|
1922
2461
|
const agentKey = args?.agent_key;
|
|
1923
2462
|
if (!agentKey)
|
|
1924
|
-
throw new Error('agent_key is required.');
|
|
2463
|
+
throw new Error('agent_key is required. Call register_agent first to get an API key (starts with hp_).');
|
|
1925
2464
|
const res = await fetch(`${API_BASE}/api/jobs/${args?.job_id}/messages`, {
|
|
1926
2465
|
headers: { 'X-Agent-Key': agentKey },
|
|
1927
2466
|
});
|
|
@@ -1946,7 +2485,7 @@ You can now create up to 15 job offers per day and view up to 50 full human prof
|
|
|
1946
2485
|
if (name === 'create_listing') {
|
|
1947
2486
|
const agentKey = args?.agent_key;
|
|
1948
2487
|
if (!agentKey)
|
|
1949
|
-
throw new Error('agent_key is required.');
|
|
2488
|
+
throw new Error('agent_key is required. Call register_agent first to get an API key (starts with hp_).');
|
|
1950
2489
|
const res = await fetch(`${API_BASE}/api/listings`, {
|
|
1951
2490
|
method: 'POST',
|
|
1952
2491
|
headers: {
|
|
@@ -1961,6 +2500,11 @@ You can now create up to 15 job offers per day and view up to 50 full human prof
|
|
|
1961
2500
|
requiredSkills: args?.required_skills || [],
|
|
1962
2501
|
requiredEquipment: args?.required_equipment || [],
|
|
1963
2502
|
location: args?.location,
|
|
2503
|
+
locationStreet: args?.location_street,
|
|
2504
|
+
locationCountry: args?.location_country,
|
|
2505
|
+
locationRegion: args?.location_region,
|
|
2506
|
+
locationLocality: args?.location_locality,
|
|
2507
|
+
locationPostal: args?.location_postal,
|
|
1964
2508
|
locationLat: args?.location_lat,
|
|
1965
2509
|
locationLng: args?.location_lng,
|
|
1966
2510
|
radiusKm: args?.radius_km,
|
|
@@ -2051,7 +2595,7 @@ You can now create up to 15 job offers per day and view up to 50 full human prof
|
|
|
2051
2595
|
const res = await fetch(`${API_BASE}/api/listings/${args?.listing_id}`);
|
|
2052
2596
|
if (!res.ok) {
|
|
2053
2597
|
if (res.status === 404)
|
|
2054
|
-
throw new Error(`Listing not found: ${args?.listing_id}
|
|
2598
|
+
throw new Error(`Listing not found: "${args?.listing_id}". Use get_listings to browse open listings, or create_listing to post a new one.`);
|
|
2055
2599
|
throw new Error(`API error: ${res.status}`);
|
|
2056
2600
|
}
|
|
2057
2601
|
const listing = await res.json();
|
|
@@ -2089,7 +2633,7 @@ ${agent?.websiteUrl ? `- **Website:** ${agent.websiteUrl}` : ''}
|
|
|
2089
2633
|
if (name === 'get_listing_applications') {
|
|
2090
2634
|
const agentKey = args?.agent_key;
|
|
2091
2635
|
if (!agentKey)
|
|
2092
|
-
throw new Error('agent_key is required.');
|
|
2636
|
+
throw new Error('agent_key is required. Call register_agent first to get an API key (starts with hp_).');
|
|
2093
2637
|
const res = await fetch(`${API_BASE}/api/listings/${args?.listing_id}/applications`, {
|
|
2094
2638
|
headers: { 'X-Agent-Key': agentKey },
|
|
2095
2639
|
});
|
|
@@ -2126,7 +2670,7 @@ ${agent?.websiteUrl ? `- **Website:** ${agent.websiteUrl}` : ''}
|
|
|
2126
2670
|
if (name === 'make_listing_offer') {
|
|
2127
2671
|
const agentKey = args?.agent_key;
|
|
2128
2672
|
if (!agentKey)
|
|
2129
|
-
throw new Error('agent_key is required.');
|
|
2673
|
+
throw new Error('agent_key is required. Call register_agent first to get an API key (starts with hp_).');
|
|
2130
2674
|
const res = await fetch(`${API_BASE}/api/listings/${args?.listing_id}/applications/${args?.application_id}/offer`, {
|
|
2131
2675
|
method: 'POST',
|
|
2132
2676
|
headers: {
|
|
@@ -2150,7 +2694,7 @@ ${agent?.websiteUrl ? `- **Website:** ${agent.websiteUrl}` : ''}
|
|
|
2150
2694
|
if (name === 'cancel_listing') {
|
|
2151
2695
|
const agentKey = args?.agent_key;
|
|
2152
2696
|
if (!agentKey)
|
|
2153
|
-
throw new Error('agent_key is required.');
|
|
2697
|
+
throw new Error('agent_key is required. Call register_agent first to get an API key (starts with hp_).');
|
|
2154
2698
|
const res = await fetch(`${API_BASE}/api/listings/${args?.listing_id}`, {
|
|
2155
2699
|
method: 'DELETE',
|
|
2156
2700
|
headers: { 'X-Agent-Key': agentKey },
|
|
@@ -2207,7 +2751,7 @@ ${agent?.websiteUrl ? `- **Website:** ${agent.websiteUrl}` : ''}
|
|
|
2207
2751
|
if (name === 'leave_review') {
|
|
2208
2752
|
const res = await fetch(`${API_BASE}/api/jobs/${args?.job_id}/review`, {
|
|
2209
2753
|
method: 'POST',
|
|
2210
|
-
headers: { 'Content-Type': 'application/json' },
|
|
2754
|
+
headers: { 'Content-Type': 'application/json', 'X-Agent-Key': args?.agent_key },
|
|
2211
2755
|
body: JSON.stringify({
|
|
2212
2756
|
rating: args?.rating,
|
|
2213
2757
|
comment: args?.comment,
|
|
@@ -2232,8 +2776,156 @@ Thank you for your feedback. This helps build the human's reputation.`,
|
|
|
2232
2776
|
],
|
|
2233
2777
|
};
|
|
2234
2778
|
}
|
|
2779
|
+
// ======================== ESCROW TOOLS ========================
|
|
2780
|
+
if (name === 'list_arbitrators') {
|
|
2781
|
+
const res = await fetch(`${API_BASE}/api/escrow/arbitrators`);
|
|
2782
|
+
if (!res.ok) {
|
|
2783
|
+
const error = await res.json();
|
|
2784
|
+
throw new Error(error.error || 'Escrow not enabled on this server');
|
|
2785
|
+
}
|
|
2786
|
+
const arbitrators = await res.json();
|
|
2787
|
+
if (arbitrators.length === 0) {
|
|
2788
|
+
return {
|
|
2789
|
+
content: [{
|
|
2790
|
+
type: 'text',
|
|
2791
|
+
text: 'No arbitrators available yet. The platform is onboarding vetted arbitrators. Check back soon or contact the platform owner.',
|
|
2792
|
+
}],
|
|
2793
|
+
};
|
|
2794
|
+
}
|
|
2795
|
+
const list = arbitrators.map((a) => `- **${a.name}** (${a.id})
|
|
2796
|
+
Fee: ${a.arbitratorFeeBps / 100}% | Specialties: ${a.arbitratorSpecialties?.join(', ') || 'General'} | SLA: ${a.arbitratorSla || 'N/A'}
|
|
2797
|
+
Disputes: ${a.arbitratorDisputeCount} resolved | Wallet: ${a.wallets?.[0]?.address || 'Not set'}`).join('\n');
|
|
2798
|
+
return {
|
|
2799
|
+
content: [{
|
|
2800
|
+
type: 'text',
|
|
2801
|
+
text: `**Available Arbitrators (${arbitrators.length})**\n\n${list}\n\nUse the wallet address as \`escrow_arbitrator_address\` in \`create_job_offer\` with \`payment_mode: "ESCROW"\`.`,
|
|
2802
|
+
}],
|
|
2803
|
+
};
|
|
2804
|
+
}
|
|
2805
|
+
if (name === 'register_as_arbitrator') {
|
|
2806
|
+
const agentKey = args?.agent_key;
|
|
2807
|
+
if (!agentKey)
|
|
2808
|
+
throw new Error('agent_key is required');
|
|
2809
|
+
// Get agent profile to find ID
|
|
2810
|
+
const profileRes = await fetch(`${API_BASE}/api/agents/me`, {
|
|
2811
|
+
headers: { 'X-Agent-Key': agentKey },
|
|
2812
|
+
});
|
|
2813
|
+
if (!profileRes.ok)
|
|
2814
|
+
throw new Error('Invalid agent key');
|
|
2815
|
+
const agent = await profileRes.json();
|
|
2816
|
+
// Update agent with arbitrator fields
|
|
2817
|
+
const res = await fetch(`${API_BASE}/api/agents/${agent.id}/arbitrator`, {
|
|
2818
|
+
method: 'POST',
|
|
2819
|
+
headers: {
|
|
2820
|
+
'Content-Type': 'application/json',
|
|
2821
|
+
'X-Agent-Key': agentKey,
|
|
2822
|
+
},
|
|
2823
|
+
body: JSON.stringify({
|
|
2824
|
+
feeBps: args?.fee_bps,
|
|
2825
|
+
specialties: args?.specialties,
|
|
2826
|
+
sla: args?.sla,
|
|
2827
|
+
webhookUrl: args?.webhook_url,
|
|
2828
|
+
walletSig: args?.wallet_signature,
|
|
2829
|
+
}),
|
|
2830
|
+
});
|
|
2831
|
+
if (!res.ok) {
|
|
2832
|
+
const error = await res.json();
|
|
2833
|
+
throw new Error(error.error || 'Failed to register as arbitrator');
|
|
2834
|
+
}
|
|
2835
|
+
return {
|
|
2836
|
+
content: [{
|
|
2837
|
+
type: 'text',
|
|
2838
|
+
text: `**Registered as Arbitrator!**
|
|
2839
|
+
|
|
2840
|
+
Your agent "${agent.name}" is now listed as an arbitrator candidate. The platform owner will review and whitelist your wallet on the escrow contract.
|
|
2841
|
+
|
|
2842
|
+
**Fee:** ${args?.fee_bps / 100}%
|
|
2843
|
+
**Webhook:** ${args?.webhook_url}
|
|
2844
|
+
**Specialties:** ${args?.specialties?.join(', ') || 'General'}
|
|
2845
|
+
|
|
2846
|
+
**Next:** Wait for platform approval. Once whitelisted, payers can select you as arbitrator. Your fee is passed by the payer at deposit time — no on-chain setup needed from you.`,
|
|
2847
|
+
}],
|
|
2848
|
+
};
|
|
2849
|
+
}
|
|
2850
|
+
if (name === 'get_dispute_details') {
|
|
2851
|
+
const agentKey = args?.agent_key;
|
|
2852
|
+
if (!agentKey)
|
|
2853
|
+
throw new Error('agent_key is required');
|
|
2854
|
+
const res = await fetch(`${API_BASE}/api/escrow/${args?.job_id}/status`);
|
|
2855
|
+
if (!res.ok) {
|
|
2856
|
+
const error = await res.json();
|
|
2857
|
+
throw new Error(error.error || 'Failed to get dispute details');
|
|
2858
|
+
}
|
|
2859
|
+
const escrow = await res.json();
|
|
2860
|
+
// Also get job messages as evidence
|
|
2861
|
+
const msgRes = await fetch(`${API_BASE}/api/jobs/${args?.job_id}/messages`, {
|
|
2862
|
+
headers: { 'X-Agent-Key': agentKey },
|
|
2863
|
+
});
|
|
2864
|
+
const messages = msgRes.ok ? await msgRes.json() : [];
|
|
2865
|
+
const jobRes = await fetch(`${API_BASE}/api/jobs/${args?.job_id}`);
|
|
2866
|
+
const job = jobRes.ok ? await jobRes.json() : null;
|
|
2867
|
+
return {
|
|
2868
|
+
content: [{
|
|
2869
|
+
type: 'text',
|
|
2870
|
+
text: `**Dispute Details for Job ${args?.job_id}**
|
|
2871
|
+
|
|
2872
|
+
**Title:** ${job?.title || 'N/A'}
|
|
2873
|
+
**Description:** ${job?.description || 'N/A'}
|
|
2874
|
+
**Price:** $${job?.priceUsdc || 'N/A'}
|
|
2875
|
+
**Status:** ${escrow.escrowStatus}
|
|
2876
|
+
**Escrowed Amount:** $${escrow.escrowAmount || 'N/A'}
|
|
2877
|
+
**Dispute Reason:** ${job?.disputeReason || 'Not specified'}
|
|
2878
|
+
**Disputed At:** ${escrow.escrowDisputedAt || 'N/A'}
|
|
2879
|
+
**Arbitrator Fee Rate:** ${escrow.escrowArbitratorFeeBps / 100}%
|
|
2880
|
+
|
|
2881
|
+
**Messages (${messages.length}):**
|
|
2882
|
+
${messages.map((m) => `[${m.senderType}] ${m.content}`).join('\n') || 'No messages'}
|
|
2883
|
+
|
|
2884
|
+
**Your Task:** Review the evidence and submit a verdict using \`submit_verdict\`. Split the escrowed amount between payee and depositor. Your fee is automatically calculated from your locked rate.`,
|
|
2885
|
+
}],
|
|
2886
|
+
};
|
|
2887
|
+
}
|
|
2888
|
+
if (name === 'submit_verdict') {
|
|
2889
|
+
const agentKey = args?.agent_key;
|
|
2890
|
+
if (!agentKey)
|
|
2891
|
+
throw new Error('agent_key is required');
|
|
2892
|
+
const res = await fetch(`${API_BASE}/api/escrow/resolve`, {
|
|
2893
|
+
method: 'POST',
|
|
2894
|
+
headers: {
|
|
2895
|
+
'Content-Type': 'application/json',
|
|
2896
|
+
'X-Agent-Key': agentKey,
|
|
2897
|
+
},
|
|
2898
|
+
body: JSON.stringify({
|
|
2899
|
+
jobId: args?.job_id,
|
|
2900
|
+
toPayee: args?.to_payee,
|
|
2901
|
+
toDepositor: args?.to_depositor,
|
|
2902
|
+
arbitratorFee: args?.arbitrator_fee,
|
|
2903
|
+
nonce: args?.nonce,
|
|
2904
|
+
signature: args?.signature,
|
|
2905
|
+
}),
|
|
2906
|
+
});
|
|
2907
|
+
if (!res.ok) {
|
|
2908
|
+
const error = await res.json();
|
|
2909
|
+
throw new Error(error.error || 'Failed to submit verdict');
|
|
2910
|
+
}
|
|
2911
|
+
const result = await res.json();
|
|
2912
|
+
return {
|
|
2913
|
+
content: [{
|
|
2914
|
+
type: 'text',
|
|
2915
|
+
text: `**Verdict Submitted!**
|
|
2916
|
+
|
|
2917
|
+
**Job:** ${args?.job_id}
|
|
2918
|
+
**To Worker:** $${Number(args?.to_payee) / 1e6}
|
|
2919
|
+
**To Payer:** $${Number(args?.to_depositor) / 1e6}
|
|
2920
|
+
**Your Fee:** $${Number(args?.arbitrator_fee) / 1e6}
|
|
2921
|
+
**Tx Hash:** ${result.resolveTxHash}
|
|
2922
|
+
|
|
2923
|
+
The escrow has been resolved on-chain. Funds will be distributed to the respective parties.`,
|
|
2924
|
+
}],
|
|
2925
|
+
};
|
|
2926
|
+
}
|
|
2235
2927
|
return {
|
|
2236
|
-
content: [{ type: 'text', text: `Unknown tool: ${name}
|
|
2928
|
+
content: [{ type: 'text', text: `Unknown tool: "${name}". Available tools: search_humans, get_human, get_human_profile, register_agent, create_job_offer, get_job_status, mark_job_paid, approve_completion, request_revision, leave_review, send_job_message, get_job_messages, create_listing, get_listings, get_listing, get_listing_applications, make_listing_offer, cancel_listing, list_arbitrators, register_as_arbitrator, get_dispute_details, submit_verdict, and more. Start with search_humans or register_agent.` }],
|
|
2237
2929
|
isError: true,
|
|
2238
2930
|
};
|
|
2239
2931
|
}
|