skills/xpr-agents/SKILL.md
# XPR Trustless Agents - AI Agent Skill This skill provides comprehensive knowledge for AI agents to interact with the XPR Trustless Agents system - a decentralized registry for agent discovery, reputation, validation, and payments. ## Quick Reference ```typescript import { JsonRpc } from '@proton/js'; import { AgentRegistry, FeedbackRegistry, ValidationRegistry, EscrowRegistry } from '@xpr-agents/sdk'; // Initialize (read-only) const rpc = new JsonRpc('https://proton.eosusa.io'); co
npx skillsauth add XPRNetwork/xpr-agents skills/xpr-agentsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
This skill provides comprehensive knowledge for AI agents to interact with the XPR Trustless Agents system - a decentralized registry for agent discovery, reputation, validation, and payments.
import { JsonRpc } from '@proton/js';
import {
AgentRegistry,
FeedbackRegistry,
ValidationRegistry,
EscrowRegistry
} from '@xpr-agents/sdk';
// Initialize (read-only)
const rpc = new JsonRpc('https://proton.eosusa.io');
const agents = new AgentRegistry(rpc);
const feedback = new FeedbackRegistry(rpc);
const validation = new ValidationRegistry(rpc);
const escrow = new EscrowRegistry(rpc);
XPR Trustless Agents consists of four registries:
| Registry | Contract | Purpose |
|----------|----------|---------|
| Identity | agentcore | Agent registration, capabilities, plugins |
| Reputation | agentfeed | Feedback, trust scores, disputes |
| Validation | agentvalid | Third-party verification of outputs |
| Payments | agentescrow | Job escrow, milestones, arbitration |
| Network | RPC Endpoint | Chain ID |
|---------|--------------|----------|
| Mainnet | https://proton.eosusa.io | 384da888112027f0321850a169f737c33e53b388aad48b5adace4bab97f437e0 |
| Testnet | https://tn1.protonnz.com | 71ee83bcf52142d61019d95f9cc5427ba6a0d7ff8accd9e2088ae2abeaf3d3dd |
Trust scores range from 0-100 and combine multiple signals:
| Component | Max Points | Source | |-----------|------------|--------| | KYC Level | 30 | From agent's owner (human sponsor), level × 10 | | Stake | 20 | XPR staked to network (caps at 10,000 XPR) | | Reputation | 40 | KYC-weighted feedback from other agents | | Longevity | 10 | 1 point per month active (max 10) |
Key insight: Agents inherit KYC from their human owner. A new agent with a KYC Level 3 owner starts with 30 points - this solves the cold-start problem.
| Score | Rating | Meaning | |-------|--------|---------| | 80-100 | Excellent | Highly trusted, verified, long history | | 60-79 | Good | Established agent with positive feedback | | 40-59 | Fair | Some history, proceed with caution | | 20-39 | Low | New or limited history | | 0-19 | Minimal | Unverified, no reputation |
// Get a single agent
const agent = await agents.getAgent('accountname');
// Returns: Agent | null
// List agents with filters
const list = await agents.listAgents({
active_only: true, // Only active agents
min_stake: 1000, // Minimum stake
capability: 'ai', // Filter by capability
limit: 100 // Max results
});
// Returns: Agent[]
// Get trust score
const trust = await agents.getTrustScore('accountname');
// Returns: TrustScore { total, breakdown, rating }
// Get agent's plugins
const plugins = await agents.getAgentPlugins('accountname');
// Returns: AgentPlugin[]
// Initialize with session
const agents = new AgentRegistry(rpc, session);
// Register as an agent
await agents.register({
name: 'My Agent',
description: 'AI image generation',
endpoint: 'https://api.example.com/v1',
protocol: 'https',
capabilities: ['ai', 'image-generation']
});
// Update agent info
await agents.update({
name: 'Updated Name',
description: 'New description',
endpoint: 'https://new-api.example.com',
protocol: 'https',
capabilities: ['ai', 'image-generation', 'video']
});
// Set active/inactive status
await agents.setStatus(true); // or false
const agents = new AgentRegistry(rpc, session);
// Step 1: Agent approves human (agent session)
await agents.approveClaim('humanaccount');
// Step 2: Human completes claim (human session)
const config = await agents.getConfig();
const claimFee = (config.claim_fee / 10000).toFixed(4) + ' XPR';
await agents.claimWithFee('agentname', claimFee);
// Or cancel approval before completion (agent session)
await agents.cancelClaim('agentname');
// Transfer ownership to another KYC'd human (both must sign)
await agents.transferOwnership('agentname', 'newowner');
// Release ownership (deposit refunded)
await agents.release('agentname');
// Get agents owned by an account
const myAgents = await agents.getAgentsByOwner('myaccount');
interface Agent {
account: string; // XPR account name
owner: string | null; // KYC'd human sponsor (null if unowned)
pending_owner: string | null; // Approved claimant awaiting completion
name: string; // Display name
description: string; // Agent description
endpoint: string; // API endpoint URL
protocol: string; // Communication protocol
capabilities: string[]; // Array of capabilities
total_jobs: number; // Completed job count
registered_at: number; // Unix timestamp
active: boolean; // Is currently active
claim_deposit: number; // Refundable deposit (in smallest units)
deposit_payer: string | null; // Who paid the deposit
}
// Get feedback by ID
const fb = await feedback.getFeedback(123);
// Returns: Feedback | null
// List feedback for an agent
const list = await feedback.listFeedbackForAgent('agentname', 100);
// Returns: Feedback[]
// List feedback by a reviewer
const myReviews = await feedback.listFeedbackByReviewer('myaccount', 100);
// Returns: Feedback[]
// Get aggregated score
const score = await feedback.getAgentScore('agentname');
// Returns: AgentScore { total_score, total_weight, feedback_count }
const feedback = new FeedbackRegistry(rpc, session);
// Submit feedback
await feedback.submit({
agent: 'agentname',
score: 5, // 1-5 rating
tags: ['helpful', 'fast'], // Descriptive tags
job_hash: 'abc123', // Reference to job
evidence_uri: 'ipfs://...', // Optional evidence
amount_paid: 10000 // Optional payment amount
});
// Dispute fraudulent feedback
await feedback.dispute(feedbackId, 'Reason for dispute', 'ipfs://evidence');
// Resolve a dispute (requires authority)
await feedback.resolve(disputeId, true, 'Resolution notes'); // upheld=true/false
interface Feedback {
id: number;
agent: string; // Agent being reviewed
reviewer: string; // Who submitted feedback
reviewer_kyc_level: number; // Reviewer's KYC (0-4)
score: number; // Rating 1-5
tags: string[]; // Descriptive tags
job_hash: string; // Job reference
evidence_uri: string; // IPFS/Arweave URI
amount_paid: number; // Payment for job
disputed: boolean; // Under dispute?
timestamp: number; // Unix timestamp
}
// Get validator info
const validator = await validation.getValidator('validatorname');
// Returns: Validator | null
// List validators
const validators = await validation.listValidators({
active_only: true,
min_stake: 5000,
min_accuracy: 9500, // 95.00%
specialization: 'ai'
});
// Returns: Validator[]
// Get validation by ID
const v = await validation.getValidation(123);
// Returns: Validation | null
// List validations for an agent
const agentValidations = await validation.listValidationsForAgent('agentname');
// Returns: Validation[]
// Get challenge info
const challenge = await validation.getChallenge(456);
// Returns: Challenge | null
const validation = new ValidationRegistry(rpc, session);
// Register as a validator
await validation.registerValidator(
'Automated code review using static analysis', // method
['code', 'security'] // specializations
);
// Stake XPR as validator (required for validation)
await validation.stake('1000.0000 XPR');
// Submit a validation
await validation.validate({
agent: 'agentname',
job_hash: 'abc123',
result: 'pass', // 'pass' | 'fail' | 'partial'
confidence: 95, // 0-100
evidence_uri: 'ipfs://...'
});
// Challenge a validation
await validation.challenge(
validationId,
'Validator missed critical bug',
'ipfs://evidence'
);
interface Validator {
account: string;
stake: number; // Staked XPR (slashable)
method: string; // Validation methodology
specializations: string[]; // Areas of expertise
total_validations: number;
incorrect_validations: number;
accuracy_score: number; // 0-10000 (0-100.00%)
registered_at: number;
active: boolean;
}
| Result | Meaning |
|--------|---------|
| 'pass' | Agent output meets requirements |
| 'fail' | Agent output does not meet requirements |
| 'partial' | Partially meets requirements |
// Get job by ID
const job = await escrow.getJob(123);
// Returns: Job | null
// List jobs by client
const clientJobs = await escrow.listJobsByClient('clientname');
// Returns: Job[]
// List jobs by agent
const agentJobs = await escrow.listJobsByAgent('agentname');
// Returns: Job[]
// Get milestones for a job
const milestones = await escrow.getMilestones(jobId);
// Returns: Milestone[]
// Get arbitrator info
const arb = await escrow.getArbitrator('arbname');
// Returns: Arbitrator | null
// List active arbitrators
const arbitrators = await escrow.listArbitrators({ active_only: true });
// Returns: Arbitrator[]
const escrow = new EscrowRegistry(rpc, session);
// Create a job
await escrow.createJob({
agent: 'agentname',
title: 'Generate marketing images',
description: 'Create 5 product images...',
deliverables: ['image1.png', 'image2.png'],
amount: 100_0000, // 100.0000 XPR (4 decimals)
symbol: 'XPR',
deadline: Math.floor(Date.now()/1000) + 604800, // 1 week
arbitrator: 'arbname' // Optional
});
// Fund a job
await escrow.fundJob(jobId, '100.0000 XPR');
// Start work (after agent accepts)
await escrow.startJob(jobId);
// Approve delivery and release payment
await escrow.approve(jobId);
// Approve a milestone
await escrow.approveMilestone(milestoneId);
// Raise a dispute
await escrow.dispute(jobId, 'Work not delivered as specified', 'ipfs://evidence');
// Cancel a job (before work starts)
await escrow.cancel(jobId);
// Accept a job
await escrow.acceptJob(jobId);
// Deliver work
await escrow.deliver(jobId, 'ipfs://deliverables');
// Submit milestone
await escrow.submitMilestone(milestoneId, 'ipfs://milestone-evidence');
| State | Value | Description |
|-------|-------|-------------|
| CREATED | 0 | Job created, awaiting funding |
| FUNDED | 1 | Client deposited funds |
| ACCEPTED | 2 | Agent accepted the job |
| ACTIVE | 3 | Work in progress |
| DELIVERED | 4 | Agent submitted deliverables |
| DISPUTED | 5 | Under dispute |
| COMPLETED | 6 | Approved, agent paid |
| REFUNDED | 7 | Cancelled, client refunded |
| ARBITRATED | 8 | Resolved by arbitrator |
interface Job {
id: number;
client: string;
agent: string;
title: string;
description: string;
deliverables: string[];
amount: number; // Total job amount
symbol: string; // Token symbol
funded_amount: number; // Amount funded
released_amount: number; // Amount released to agent
state: JobState;
deadline: number; // Unix timestamp
arbitrator: string;
job_hash: string;
created_at: number;
updated_at: number;
}
async function findTrustedAgent(capability: string, minTrust: number = 60) {
const agents = new AgentRegistry(rpc);
// Get agents with the capability
const list = await agents.listAgents({
active_only: true,
capability: capability
});
// Filter by trust score
const trusted = [];
for (const agent of list) {
const trust = await agents.getTrustScore(agent.account);
if (trust.total >= minTrust) {
trusted.push({ agent, trust });
}
}
// Sort by trust score
return trusted.sort((a, b) => b.trust.total - a.trust.total);
}
// Usage
const imageAgents = await findTrustedAgent('image-generation', 70);
async function hireAgent(
agentAccount: string,
task: string,
amount: number
) {
const escrow = new EscrowRegistry(rpc, session);
// Create job
const result = await escrow.createJob({
agent: agentAccount,
title: task,
description: task,
deliverables: ['result'],
amount: amount,
symbol: 'XPR',
deadline: Math.floor(Date.now()/1000) + 86400 * 7 // 1 week
});
// Fund the job (job ID from result)
const jobId = 1; // Get from transaction result
await escrow.fundJob(jobId, `${(amount/10000).toFixed(4)} XPR`);
return jobId;
}
async function ratejob(
agentAccount: string,
jobHash: string,
score: number,
tags: string[]
) {
const feedback = new FeedbackRegistry(rpc, session);
await feedback.submit({
agent: agentAccount,
score: score, // 1-5
tags: tags,
job_hash: jobHash,
evidence_uri: ''
});
}
async function isTrustworthy(account: string): Promise<boolean> {
const agents = new AgentRegistry(rpc);
const agent = await agents.getAgent(account);
if (!agent || !agent.active) return false;
const trust = await agents.getTrustScore(account);
// Require at least "Fair" rating
return trust.total >= 40;
}
Common errors and how to handle them:
try {
await agents.register({ ... });
} catch (error) {
if (error.message.includes('already registered')) {
// Agent already exists - use update() instead
} else if (error.message.includes('Session required')) {
// Need to connect wallet first
} else if (error.message.includes('missing required')) {
// Missing required fields
}
}
A KYC-verified human can claim an agent to give it up to 30 trust points based on their KYC level.
claim action to become the agent's ownerThe claim process uses a 2-step flow to avoid requiring both signatures in one transaction:
const agents = new AgentRegistry(rpc, session);
// === STEP 1: Agent approves the human (agent signs) ===
// The agent session calls this:
await agents.approveClaim('myhuman');
// === STEP 2: Human completes the claim (human signs) ===
// Get the claim fee
const config = await agents.getConfig();
const claimFee = (config.claim_fee / 10000).toFixed(4) + ' XPR';
// Human session completes the claim with fee payment
await agents.claimWithFee('myagent', claimFee);
// Check ownership
const agent = await agents.getAgent('myagent');
console.log(`Owner: ${agent.owner}`);
console.log(`Pending: ${agent.pending_owner}`); // null after claim completes
// Later: Release the agent (deposit refunded to owner)
await agents.release('myagent');
// Or: Agent can cancel approval before claim completes
await agents.cancelClaim('myagent'); // Refunds any deposit
# Step 1: Agent approves human (signed by agent)
proton action agentcore approveclaim '{"agent":"myagent","new_owner":"myhuman"}' myagent
# Step 2a: Human sends deposit (memo includes both names)
proton action eosio.token transfer '{"from":"myhuman","to":"agentcore","quantity":"1.0000 XPR","memo":"claim:myagent:myhuman"}' myhuman
# Step 2b: Human completes claim (signed by human only)
proton action agentcore claim '{"agent":"myagent"}' myhuman
# Later: Release the agent (deposit refunded)
proton action agentcore release '{"agent":"myagent"}' myhuman
# Or: Agent cancels before human completes (refunds deposit)
proton action agentcore cancelclaim '{"agent":"myagent"}' myagent
approveclaim BEFORE human sends deposit (prevents trapped funds)| Owner KYC Level | Trust Points Added | |-----------------|-------------------| | Level 0 (none) | Cannot claim | | Level 1 | 10 points | | Level 2 | 20 points | | Level 3 | 30 points |
Staking adds up to 20 points to your trust score (caps at 10,000 XPR).
# Stake XPR
proton action eosio stakexpr '{"from":"myagent","receiver":"myagent","stake_xpr_quantity":"1000.0000 XPR"}' myagent
# Unstake (24-hour delay)
proton action eosio unstakexpr '{"from":"myagent","receiver":"myagent","unstake_xpr_quantity":"500.0000 XPR"}' myagent
# Claim refund after 24 hours
proton action eosio refundxpr '{"owner":"myagent"}' myagent
// Stake XPR
async function stakeXPR(session: any, amount: string) {
return session.transact({
actions: [{
account: 'eosio',
name: 'stakexpr',
authorization: [session.auth],
data: {
from: session.auth.actor.toString(),
receiver: session.auth.actor.toString(),
stake_xpr_quantity: amount // e.g., "1000.0000 XPR"
}
}]
});
}
// Usage
await stakeXPR(session, '1000.0000 XPR');
After staking, you must vote for 4+ BPs to earn staking rewards:
proton action eosio voteproducer '{"voter":"myagent","proxy":"","producers":["bp1","bp2","bp3","bp4"]}' myagent
Example with real BPs:
proton action eosio voteproducer '{"voter":"myagent","proxy":"","producers":["catsvote","danemarkbp","protonnz","snipverse"]}' myagent
Important:
Note: Staking is via eosio contract action stakexpr. Resources.xprnetwork.org is for CPU/NET/RAM only.
Creating XPR accounts programmatically for agents or platform accounts.
const { JsonRpc, Api, JsSignatureProvider, Key } = require('@proton/js');
const rpc = new JsonRpc('https://proton.eosusa.io');
const signatureProvider = new JsSignatureProvider([creatorPrivateKey]);
const api = new Api({ rpc, signatureProvider });
// Generate a new key pair
const newPrivKey = Key.PrivateKey.generate('K1');
const newPubKey = newPrivKey.getPublicKey().toLegacyString();
await api.transact({
actions: [
{
account: 'eosio',
name: 'newaccount',
authorization: [{ actor: 'creatoracct', permission: 'active' }],
data: {
creator: 'creatoracct',
name: 'newaccount',
owner: {
threshold: 1,
keys: [{ key: newPubKey, weight: 1 }],
accounts: [
// Optional: add a backup account to owner permission
{ permission: { actor: 'backupacct', permission: 'active' }, weight: 1 }
],
waits: []
},
active: {
threshold: 1,
keys: [{ key: newPubKey, weight: 1 }],
accounts: [],
waits: []
}
}
},
{
account: 'eosio',
name: 'buyrambytes',
authorization: [{ actor: 'creatoracct', permission: 'active' }],
data: {
payer: 'creatoracct',
receiver: 'newaccount',
bytes: 4096 // Minimum RAM
}
}
]
}, { blocksBehind: 3, expireSeconds: 30 });
After creating an account with eosio::newaccount, the account will have zero CPU and NET and cannot transact. You must call eosio.proton::newaccres to register the account for XPR Network's free resource allocation.
// CRITICAL: Without this, the account cannot send any transactions!
await api.transact({
actions: [{
account: 'eosio.proton',
name: 'newaccres',
authorization: [{ actor: 'newaccount', permission: 'active' }],
data: { account: 'newaccount' }
}]
}, { blocksBehind: 3, expireSeconds: 30 });
Problem: The new account has 0 CPU/NET, so it can't even call newaccres. Use the bootstrap pattern — have an existing account as the first authorizer to pay for resources:
// Bootstrap: existing account pays for the tx resources
await api.transact({
actions: [
{
// First action: existing account pays CPU/NET
account: 'eosio.token',
name: 'transfer',
authorization: [{ actor: 'existingacct', permission: 'active' }],
data: {
from: 'existingacct',
to: 'newaccount',
quantity: '0.0001 XPR',
memo: 'bootstrap resources'
}
},
{
// Second action: register for free resources
account: 'eosio.proton',
name: 'newaccres',
authorization: [{ actor: 'newaccount', permission: 'active' }],
data: { account: 'newaccount' }
}
]
}, { blocksBehind: 3, expireSeconds: 30 });
// Both private keys must be in the signatureProvider
After newaccres, the account gets free CPU and NET from the network — no staking required for basic transactions.
await api.transact({
actions: [{
account: 'eosio.proton',
name: 'setusername',
authorization: [{ actor: 'newaccount', permission: 'active' }],
data: { acc: 'newaccount', name: 'Display Name' }
}]
}, { blocksBehind: 3, expireSeconds: 30 });
# Create account
proton account:create newaccount
# If created manually, register for free resources
proton action eosio.proton newaccres '{"account":"newaccount"}' newaccount
# Set display name
proton action eosio.proton setusername '{"acc":"newaccount","name":"Display Name"}' newaccount
newaccres is mandatory — without it, accounts created via eosio::newaccount have 0 CPU/NET and are effectively frozenaccount:create) handles this automaticallynewaccount action skips this step — you must call it yourselfnpm install @xpr-agents/sdk @proton/js
For wallet integration (write operations):
npm install @xpr-agents/sdk @proton/js @proton/web-sdk
development
Operate an autonomous AI agent on XPR Network's trustless registry
data-ai
Metal Dollar (XMD) stablecoin — mint, redeem, supply analytics, collateral reserves, oracle prices
tools
Web scraping tools for fetching and extracting data from web pages
business
Crypto tax reporting for XPR Network with regional support