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.
Files changed (3) hide show
  1. package/README.md +6 -21
  2. package/dist/tools.js +776 -84
  3. 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
- const res = await fetch(`${API_BASE}/api/humans/${id}`);
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. All filters are optional — you can combine any of them or use just one. IMPORTANT: When the user wants workers with platform experience or completed jobs, use min_completed_jobs=1 (no skill filter needed) to find ALL humans with completed jobs across any skill. Supports filtering by skill, equipment, language, location (text or coordinates with radius), and rate. Use sort_by to control result ordering "completed_jobs" (default) surfaces workers with proven platform experience first, "rating" sorts by reviews, "experience" by years of professional experience. When using text location, provide a fully-qualified name (e.g., "Richmond, Virginia, USA" not just "Richmond") for accurate geocoding. The response includes a resolvedLocation field showing what location was matched verify this is correct. Default search radius is 30km. Contact info available via get_human_profile (requires registered agent).',
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 detailed information about a specific human by their ID, including their bio, skills, and service offerings. Contact info, wallets, and social links available via get_human_profile (requires registered agent).',
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 as an agent on Human Pages. Agents are auto-activated on PRO tier (free during launch). Returns an API key that you MUST save and use for all subsequent calls. The API key cannot be retrieved later.',
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. This is used for receiving USDC payments and checking balance. The address must be a valid EVM address (0x + 40 hex chars). Network defaults to "base" (recommended for low fees).',
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. Returns structured info about how the developer can fund the agent with USDC, or alternatively pay workers directly via fiat platforms. Present this to the developer when they need to fund the agent for hiring workers. Think about what you know about the developer their country, existing accounts, crypto experience, friends with crypto and tailor your recommendation of which option fits them best.',
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: 'Create a job offer for a human. Requires a registered agent API key or x402 platform fee ($0.25 via x402 protocol). RATE LIMITS: PRO tier = 15 offers/day. x402 payments bypass tier limits. Prices are denominated in USD payment method (crypto or fiat) is flexible and agreed between agent and human after acceptance. SPAM FILTERS: Humans can set minOfferPrice and maxOfferDistance - if your offer violates these, it will be rejected with a specific error code.',
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 offer. Use this to see if the human has accepted, and if the job is ready for payment.',
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 that payment has been sent for an ACCEPTED job. Supports both crypto (verified on-chain) and fiat (self-reported, human confirms receipt). For crypto payments, provide a transaction hash and network for on-chain verification. For fiat payments (PayPal, bank transfer, etc.), provide a payment reference the human will be asked to confirm receipt.',
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. Use this when the human has submitted their work for review (status = SUBMITTED) and you are satisfied with the evidence. Moves the job to COMPLETED, after which you can pay and leave a review.',
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 revision on submitted work. Use this when the human has submitted their work (status = SUBMITTED) but it does not meet requirements. The job moves back to ACCEPTED and the human can resubmit. Include a clear reason explaining what needs to be fixed.',
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: 'Leave a review for a COMPLETED job. Reviews are only allowed after the human marks the job as complete.',
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 the full profile of a human including contact info, payment methods (crypto wallets and fiat options like PayPal), and social links. Requires a registered agent API key. Alternative: pay $0.05 per view via x402 platform fee. Note: crypto addresses are shown directly; fiat payment details (PayPal, Venmo handles) are shown if the human opted to share them. Full bank details are never exposed — the human provides those directly after job acceptance.',
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 the current activation status, tier, and expiry for your agent.',
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 on a job. Agents can message the human they hired, and vice versa. Works on PENDING, ACCEPTED, PAID, STREAMING, and PAUSED jobs. The human receives email and Telegram notifications for agent messages. Rate limit: 10 messages/minute.',
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, ordered chronologically. Returns messages from both the agent and the human. Use this to check for replies after sending a message or receiving a webhook notification.',
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 listing on the Human Pages job board for humans to discover and apply to. Unlike create_job_offer (which targets a specific human), listings let you describe work and wait for qualified humans to come to you. Requires a registered agent or x402 platform fee ($0.50). RATE LIMITS: PRO = 5 listings/day. x402 bypasses limits.',
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 Human Pages job board. Returns listings with agent reputation and application counts. Supports filtering by skill, category, work mode, budget range, and location.',
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 a listing you created. Returns applicant profiles with skills, location, reputation, and their pitch message. Use this to evaluate candidates before making an offer.',
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: 'Make a job offer to a listing applicant. This creates a standard job from the listing and notifies the human. This is a binding commitment — by making this offer, you commit to paying the listed budget if the human accepts and completes the work.',
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.join(', ') || 'None listed'}
953
- Equipment: ${h.equipment.join(', ') || 'None listed'}
954
- Languages: ${h.languages.join(', ') || 'Not specified'}
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
- Notification channels: ${h.channelCount || 0}/4 active`;
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.join(', ') || 'None listed'}
1011
- - **Equipment:** ${human.equipment.join(', ') || 'None listed'}
1012
- - **Languages:** ${human.languages.join(', ') || 'Not specified'}
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
- Your wallet is now configured. Use \`get_funding_info\` to check your balance and get funding instructions for your developer.`,
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: 'credit_card', label: 'Buy with card', provider: 'Transak', url: transakUrl },
1154
- { method: 'peer_exchange', label: 'Convert via Peer', provider: 'Peer (peer.xyz)', url: 'https://peer.xyz', description: 'Convert from Wise, PayPal, Venmo, or 15+ other platforms to USDC' },
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 message = `I need funding to hire workers on your behalf.
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${hasWallet ? '' : ' (no wallet set — use set_wallet first)'}
1685
+ Current balance: $${balance} USDC
1164
1686
  Wallet address: ${addr}
1165
1687
  Network: ${network}
1166
1688
 
1167
- **Option A — Fund with USDC (for crypto payments):**
1689
+ **Best funding options${countryLabel}:**
1168
1690
 
1169
- 1. Already have crypto? Send USDC to my wallet address above on ${network}.
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
- **Option BPay workers directly with fiat:**
1175
- I can find workers who accept payment platforms you already use (Wise, PayPal, Venmo, Revolut, etc.). You'd pay them directly — no crypto needed.
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
- **Think about what works best for you** consider what accounts you already have, whether you or anyone you know holds crypto, and what payment methods are common where you are. I can help figure out the easiest path.
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. Register first with register_agent to get an API key.');
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
  }