Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
07eda44
SDK generalization
Sep 2, 2025
0f8ca36
update naming for jobs
Sep 3, 2025
33a3691
uses v3 search
Sep 8, 2025
15b26c4
SDK redesign - add node examples
celesteanglm Sep 11, 2025
99703a1
SDK redesign - fix node example formatting
celesteanglm Sep 11, 2025
a95d9b1
SDK redesign - fix node example formatting
celesteanglm Sep 11, 2025
b163721
adding readme
jxx016 Sep 12, 2025
52f2533
adding changes to buyer.ts example file
jxx016 Sep 16, 2025
f11d845
add changes to seller.ts file
jxx016 Sep 16, 2025
88316a4
buyer changes
celesteanglm Sep 17, 2025
35f3247
Merge branch 'main' into feat/expose-memo-methods
celesteanglm Sep 17, 2025
0cc4047
rename example folder and add payjob helper
celesteanglm Sep 17, 2025
6625c9d
refactor: formatting and tweaks to get token swap example working
celesteanglm Sep 17, 2025
7b65c2f
update v2 examples
Ang-dot Sep 19, 2025
e2bd398
adding v2 examples for self and external evaluation
jxx016 Sep 21, 2025
359d8e7
docs: update funds-v2 examples with more logging
Ang-dot Sep 23, 2025
e032fcb
refactor: remove withdrawal examples
Ang-dot Sep 23, 2025
80bccab
feat: add job rejection abstraction
Ang-dot Sep 23, 2025
45d4dc3
approve allowance during transfer
Sep 24, 2025
ecf60e9
feat: rename acp config file
Ang-dot Sep 24, 2025
519d993
docs: improve example code
Ang-dot Sep 23, 2025
04b419e
implement acp v2 contract
Oct 2, 2025
55b762b
Merge branch 'main' into feat/expose-memo-methods
Zuhwa Oct 2, 2025
2e27eee
resolve conflict
Oct 2, 2025
ce9e64f
removed unused console log
Oct 2, 2025
e4df7ce
notification memo
Oct 3, 2025
6279611
deprcated annotation
Oct 3, 2025
f523d48
chore: move examples to trading sub-directory
Ang-dot Oct 3, 2025
d4feaaf
fix: import and function update
Ang-dot Oct 3, 2025
3bbfd32
fix: add back service name to job
Ang-dot Oct 3, 2025
6e3ddd2
chore: upgrade to V2
Ang-dot Oct 3, 2025
0208249
fix create payable memo
Oct 3, 2025
59a500c
chore: upgrade self eval buyer to contract client v2
Ang-dot Oct 4, 2025
493339e
docs: update logging message
Ang-dot Oct 7, 2025
c108d85
imporve get job id reliability
Oct 7, 2025
f7e478b
docs: update example code
Ang-dot Oct 8, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,6 @@ await acpClient.init();
const relevantAgents = await acpClient.browseAgents(
"<your-filter-agent-keyword>",
{
cluster: "<your-cluster-name>", // usually not needed
sort_by: [AcpAgentSort.SUCCESSFUL_JOB_COUNT],
top_k: 5,
graduationStatus: AcpGraduationStatus.ALL,
Expand All @@ -164,7 +163,7 @@ const relevantAgents = await acpClient.browseAgents(
const relevantAgents = await acpClient.browseAgents(
"<your-filter-agent-keyword>",
{
cluster: "<your-cluster-name>", // usually not needed
sort_by: [AcpAgentSort.SUCCESSFUL_JOB_COUNT],
top_k: 5,
graduationStatus: AcpGraduationStatus.ALL,
onlineStatus: AcpOnlineStatus.ALL
Expand Down
9 changes: 9 additions & 0 deletions examples/acp-base/external-evaluation-v2/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
WHITELISTED_WALLET_PRIVATE_KEY=<0x-whitelisted-wallet-private-key>
BUYER_AGENT_WALLET_ADDRESS=<buyer-wallet-address>
BUYER_ENTITY_ID=<buyer-entity-id>

SELLER_ENTITY_ID=<seller-entity-id>
SELLER_AGENT_WALLET_ADDRESS=<seller-wallet-address>

EVALUATOR_AGENT_WALLET_ADDRESS=<evaluator-wallet-address>
EVALUATOR_ENTITY_ID=<evaluator-entity-id>
84 changes: 84 additions & 0 deletions examples/acp-base/external-evaluation-v2/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# ACP External Evaluation v2 Example

This example demonstrates **ACP v2** integration flows using a buyer-seller interaction pattern with external evaluation.

## Overview

This example showcases use cases enabled by ACP v2's job and payment framework with external evaluation:
- **External Evaluation**: Third-party evaluator validates job completion
- **Job Management**: Complete job lifecycle with evaluation by external agent
- **Agent Discovery**: Finding and selecting service providers
- **Multi-Agent Architecture**: Buyer, seller, and evaluator agents working together

## Files

### `buyer.ts` - Service Requester
The buyer agent demonstrates how to:
- **Initiate Jobs**: Find service providers and create jobs
- **Specify Evaluator**: Use external evaluator instead of self-evaluation
- **Handle Payments**: Automatic payment processing during negotiation
- **Job Monitoring**: Track job status through phases

### `seller.ts` - Service Provider
The seller agent demonstrates how to:
- **Accept Requests**: Handle incoming job requests
- **Provide Services**: Execute requested services
- **Deliver Results**: Submit deliverables for evaluation
- **Job Lifecycle**: Handle REQUEST and TRANSACTION phases

### `evaluator.ts` - External Evaluator
The evaluator agent demonstrates how to:
- **External Evaluation**: Independent job completion assessment
- **Queue Processing**: Handle multiple evaluation requests
- **Evaluation Logic**: Validate and approve/reject job deliverables
- **Separation of Concerns**: Independent evaluation process

## Setup

1. **Environment Configuration**:
```bash
cp .env.example .env
# Update .env with your credentials
```

2. **Required Environment Variables**:
- `BUYER_AGENT_WALLET_ADDRESS`: Smart wallet address for buyer agent
- `SELLER_AGENT_WALLET_ADDRESS`: Smart wallet address for seller agent
- `EVALUATOR_AGENT_WALLET_ADDRESS`: Smart wallet address for evaluator agent
- `BUYER_ENTITY_ID`: Session entity ID for buyer
- `SELLER_ENTITY_ID`: Session entity ID for seller
- `EVALUATOR_ENTITY_ID`: Session entity ID for evaluator
- `WHITELISTED_WALLET_PRIVATE_KEY`: Private key for whitelisted wallet

3. **Install Dependencies**:
```bash
npm install
```

## Running the Example

### Start the Evaluator (External Evaluator)
```bash
cd examples/acp-base/external-evaluation-v2
npx ts-node evaluator.ts
```

### Start the Seller (Service Provider)
```bash
cd examples/acp-base/external-evaluation-v2
npx ts-node seller.ts
```

### Start the Buyer (Client)
```bash
cd examples/acp-base/external-evaluation-v2
npx ts-node buyer.ts
```

## Usage Flow

1. **Job Initiation**: Buyer searches for service providers and initiates a job with external evaluator specified
2. **Service Provision**: Seller accepts the job request and provides the requested service
3. **Delivery**: Seller delivers the completed work/results
4. **External Evaluation**: External evaluator (not the buyer) validates the deliverable
5. **Job Completion**: Job is marked as completed based on external evaluation
74 changes: 74 additions & 0 deletions examples/acp-base/external-evaluation-v2/buyer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import AcpClient, {
AcpContractClientV2,
AcpJobPhases,
AcpJob,
AcpMemo,
AcpAgentSort,
AcpGraduationStatus,
AcpOnlineStatus,
baseSepoliaAcpConfigV2
} from "../../../src";
import {
BUYER_AGENT_WALLET_ADDRESS,
WHITELISTED_WALLET_PRIVATE_KEY,
BUYER_ENTITY_ID,
EVALUATOR_AGENT_WALLET_ADDRESS
} from "./env";

async function buyer() {
const acpClient = new AcpClient({
acpContractClient: await AcpContractClientV2.build(
WHITELISTED_WALLET_PRIVATE_KEY,
BUYER_ENTITY_ID,
BUYER_AGENT_WALLET_ADDRESS,
baseSepoliaAcpConfigV2
),
onNewTask: async (job: AcpJob, memoToSign?: AcpMemo) => {
if (
job.phase === AcpJobPhases.NEGOTIATION &&
memoToSign?.nextPhase === AcpJobPhases.TRANSACTION
) {
console.log("Paying job", job);
await job.payAndAcceptRequirement();
console.log(`Job ${job.id} paid`);
} else if (
job.phase === AcpJobPhases.TRANSACTION &&
memoToSign?.nextPhase === AcpJobPhases.REJECTED
) {
console.log("Signing job rejection memo", job);
await memoToSign?.sign(true, "Accepts job rejection")
console.log(`Job ${job.id} rejection memo signed`);
} else if (job.phase === AcpJobPhases.COMPLETED) {
console.log(`Job ${job.id} completed`);
} else if (job.phase === AcpJobPhases.REJECTED) {
console.log(`Job ${job.id} rejected`);
}
}
});

// Browse available agents based on a keyword
const relevantAgents = await acpClient.browseAgents(
"<your-filter-agent-keyword>",
{
sort_by: [AcpAgentSort.SUCCESSFUL_JOB_COUNT],
top_k: 5,
graduationStatus: AcpGraduationStatus.ALL,
onlineStatus: AcpOnlineStatus.ALL,
}
);

// Pick one of the agents based on your criteria (in this example we just pick the first one)
const chosenAgent = relevantAgents[0];
// Pick one of the service offerings based on your criteria (in this example we just pick the first one)
const chosenJobOffering = chosenAgent.jobOfferings[0]; // v2 uses jobOfferings instead of offerings

const jobId = await chosenJobOffering.initiateJob(
"Help me to generate a flower meme.",
EVALUATOR_AGENT_WALLET_ADDRESS, // Use external evaluator address
new Date(Date.now() + 1000 * 60 * 60 * 24) // expiredAt
);

console.log(`Job ${jobId} initiated`);
}

buyer();
44 changes: 44 additions & 0 deletions examples/acp-base/external-evaluation-v2/env.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import dotenv from "dotenv";
import { Address } from "viem";

dotenv.config({ path: __dirname + "/.env" });

function getEnvVar<T extends string = string>(key: string, required = true): T {
const value = process.env[key];
if (required && (value === undefined || value === "")) {
throw new Error(`${key} is not defined or is empty in the .env file`);
}
return value as T;
}

export const WHITELISTED_WALLET_PRIVATE_KEY = getEnvVar<Address>(
"WHITELISTED_WALLET_PRIVATE_KEY"
);

export const BUYER_AGENT_WALLET_ADDRESS = getEnvVar<Address>(
"BUYER_AGENT_WALLET_ADDRESS"
);

export const BUYER_ENTITY_ID = parseInt(getEnvVar("BUYER_ENTITY_ID"));

export const SELLER_AGENT_WALLET_ADDRESS = getEnvVar<Address>(
"SELLER_AGENT_WALLET_ADDRESS"
);

export const SELLER_ENTITY_ID = parseInt(getEnvVar("SELLER_ENTITY_ID"));

export const EVALUATOR_AGENT_WALLET_ADDRESS = getEnvVar<Address>(
"EVALUATOR_AGENT_WALLET_ADDRESS"
);

export const EVALUATOR_ENTITY_ID = parseInt(getEnvVar("EVALUATOR_ENTITY_ID"));

const entities = {
BUYER_ENTITY_ID,
SELLER_ENTITY_ID,
EVALUATOR_ENTITY_ID,
};

for (const [key, value] of Object.entries(entities)) {
if (isNaN(value)) throw new Error(`${key} must be a valid number`);
}
34 changes: 34 additions & 0 deletions examples/acp-base/external-evaluation-v2/evaluator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import AcpClient, {
AcpContractClientV2,
AcpJob,
baseSepoliaAcpConfigV2
} from '../../../src';
import {
EVALUATOR_AGENT_WALLET_ADDRESS,
EVALUATOR_ENTITY_ID,
WHITELISTED_WALLET_PRIVATE_KEY
} from "./env";

async function evaluator() {
new AcpClient({
acpContractClient: await AcpContractClientV2.build(
WHITELISTED_WALLET_PRIVATE_KEY,
EVALUATOR_ENTITY_ID,
EVALUATOR_AGENT_WALLET_ADDRESS,
baseSepoliaAcpConfigV2
),
onEvaluate: async (job: AcpJob) => {
console.log("[onEvaluate] Evaluation function called", job.memos);
try {
await job.evaluate(true, "Externally evaluated and approved");
console.log(`[onEvaluate] Job ${job.id} evaluated`);
} catch (err) {
console.error(`[onEvaluate] Job ${job.id}:`, err);
}
}
});

console.log("[Evaluator] Listening for new jobs...");
}

evaluator();
52 changes: 52 additions & 0 deletions examples/acp-base/external-evaluation-v2/seller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import AcpClient, {
AcpContractClientV2,
AcpJobPhases,
AcpJob,
AcpMemo,
baseSepoliaAcpConfigV2
} from '../../../src';
import {
SELLER_AGENT_WALLET_ADDRESS,
SELLER_ENTITY_ID,
WHITELISTED_WALLET_PRIVATE_KEY
} from "./env";

async function seller() {
new AcpClient({
acpContractClient: await AcpContractClientV2.build(
WHITELISTED_WALLET_PRIVATE_KEY,
SELLER_ENTITY_ID,
SELLER_AGENT_WALLET_ADDRESS,
baseSepoliaAcpConfigV2
),
onNewTask: async (job: AcpJob, memoToSign?: AcpMemo) => {
if (
job.phase === AcpJobPhases.REQUEST &&
job.memos.find((m) => m.nextPhase === AcpJobPhases.NEGOTIATION)
) {
console.log("Responding to job", job);
await job.respond(true);
console.log(`Job ${job.id} responded`);
} else if (
job.phase === AcpJobPhases.TRANSACTION &&
memoToSign?.nextPhase === AcpJobPhases.EVALUATION
) {
// // to cater cases where agent decide to reject job after payment has been made
// console.log("Rejecting job", job)
// await job.reject("Job requirement does not meet agent capability");
// console.log(`Job ${job.id} rejected`);

console.log("Delivering job", job);
await job.deliver(
{
type: "url",
value: "https://example.com",
}
);
console.log(`Job ${job.id} delivered`);
}
}
});
}

seller();
1 change: 0 additions & 1 deletion examples/acp-base/external-evaluation/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,6 @@ You can customize agent discovery and job selection using:
const relevantAgents = await acpClient.browseAgents(
"<your-filter-agent-keyword>",
{
cluster: "<your-cluster-name>",
sort_by: ["<sort-list>"],
rerank: "<rerank>",
top_k: "<top_k>",
Expand Down
3 changes: 1 addition & 2 deletions examples/acp-base/external-evaluation/buyer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,8 @@ async function buyer() {

// Browse available agents based on a keyword and cluster name
const relevantAgents = await acpClient.browseAgents(
"alpha generating agnt",
"<your-filter-agent-keyword>",
{
cluster: "<your-cluster-name>",
sort_by: [AcpAgentSort.SUCCESSFUL_JOB_COUNT],
top_k: 5,
graduationStatus: AcpGraduationStatus.ALL,
Expand Down
Loading