Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 5 additions & 0 deletions typescript/.changeset/agentradar-action-provider.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@coinbase/agentkit": minor
---

Added AgentRadar action provider with `verify_agent` and `get_trust_badge` actions to check an AI agent or wallet's on-chain trust score (a composite of ERC-8004 reputation, a scam-wallet database, and static analysis) before interacting with or paying it. The provider is network-agnostic, read-only, and requires no API key.
13 changes: 13 additions & 0 deletions typescript/agentkit/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,19 @@ const agent = createAgent({
</table>
</details>
<details>
<summary><strong>AgentRadar</strong></summary>
<table width="100%">
<tr>
<td width="200"><code>verify_agent</code></td>
<td width="768">Checks an AI agent or wallet's on-chain trust score (composite of ERC-8004 reputation, scam database, and static analysis) before interacting or paying. Returns a score (0-100), verdict, and per-signal breakdown.</td>
</tr>
<tr>
<td width="200"><code>get_trust_badge</code></td>
<td width="768">Returns an embeddable AgentRadar trust-badge image URL (SVG) for an EVM address.</td>
</tr>
</table>
</details>
<details>
<summary><strong>Base Account</strong></summary>
<table width="100%">
<tr>
Expand Down
70 changes: 70 additions & 0 deletions typescript/agentkit/src/action-providers/agentradar/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# AgentRadar Action Provider

This directory contains the AgentRadar action provider implementation, which provides actions to check the on-chain trust of an AI agent or wallet via the [AgentRadar](https://api.vvpro.ai) API before interacting with or paying it.

AgentRadar computes a composite trust score from ERC-8004 reputation, a scam-wallet database, and static analysis. The provider is **network-agnostic**, **read-only**, and requires **no API key**.

## Directory Structure

```
agentradar/
├── agentRadarActionProvider.ts # Main provider with AgentRadar API functionality
├── agentRadarActionProvider.test.ts # Tests for the provider
├── constants.ts # API base URL and constants
├── index.ts # Main exports
├── README.md # Documentation
├── schemas.ts # AgentRadar action schemas
└── types.ts # Type definitions
```

## Actions

- `verify_agent`: Check an agent or wallet's trust score before interacting or paying

- Returns a composite trust score (0-100) and a verdict (`TRUSTED`, `VERIFIED`, `CAUTION`, `RISKY`, or `BLOCKED`)
- Returns per-signal scores (identity, reputation, scam detection, and more)
- Returns a descriptive error message on invalid input or request failure

- `get_trust_badge`: Get an embeddable AgentRadar trust-badge image URL (SVG)
- Accepts an address and an optional style (`flat`, `pill`, or `detailed`)
- Returns a URL pointing to the badge SVG

## Usage

```typescript
import { agentRadarActionProvider } from "@coinbase/agentkit";

const provider = agentRadarActionProvider();
```

Register it with AgentKit:

```typescript
import { AgentKit } from "@coinbase/agentkit";
import { agentRadarActionProvider } from "@coinbase/agentkit";

const agentkit = await AgentKit.from({
walletProvider,
actionProviders: [agentRadarActionProvider()],
});
```

## Examples

### Verifying an Agent

```
Verify 0x036CbD53842c5426634e7929541eC2318f3dCF7e before I pay it.
```

### Getting a Trust Badge

```
Get the AgentRadar trust badge for 0x036CbD53842c5426634e7929541eC2318f3dCF7e.
```

## Notes

- The provider calls the public AgentRadar API at `https://api.vvpro.ai`. No credentials are required.
- For more details, see the [AgentRadar API](https://api.vvpro.ai).
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { agentRadarActionProvider } from "./agentRadarActionProvider";
import { AgentRadarVerifyResponse } from "./types";

describe("AgentRadarActionProvider", () => {
const fetchMock = jest.fn();
global.fetch = fetchMock;

const provider = agentRadarActionProvider();
const address = "0x036CbD53842c5426634e7929541eC2318f3dCF7e";

beforeEach(() => {
jest.resetAllMocks();
});

describe("verifyAgent", () => {
it("should return the trust result when the API call is successful", async () => {
const mockResponse: AgentRadarVerifyResponse = {
address,
score: 82,
verdict: "TRUSTED",
signals: { identity: 90, reputation: 80, scamDetection: 95 },
riskFlags: [],
};
fetchMock.mockResolvedValue({ ok: true, json: jest.fn().mockResolvedValue(mockResponse) });

const result = await provider.verifyAgent({ address });
const parsed = JSON.parse(result);

expect(parsed.score).toBe(82);
expect(parsed.verdict).toBe("TRUSTED");
expect(parsed.signals.scamDetection).toBe(95);
expect(fetchMock).toHaveBeenCalledWith(
expect.stringContaining("/verify?target="),
expect.objectContaining({ headers: { accept: "application/json" } }),
);
});

it("should lowercase the address in the request", async () => {
fetchMock.mockResolvedValue({
ok: true,
json: jest.fn().mockResolvedValue({ address, score: 50, verdict: "CAUTION" }),
});

await provider.verifyAgent({ address });

expect(fetchMock).toHaveBeenCalledWith(
expect.stringContaining(address.toLowerCase()),
expect.any(Object),
);
});

it("should handle API errors gracefully", async () => {
fetchMock.mockResolvedValue({ ok: false, status: 500 });
const result = await provider.verifyAgent({ address });
expect(result).toContain("Error verifying agent");
});

it("should handle network errors", async () => {
fetchMock.mockRejectedValue(new Error("Network error"));
const result = await provider.verifyAgent({ address });
expect(result).toContain("Error verifying agent");
expect(result).toContain("Network error");
});
});

describe("getTrustBadge", () => {
it("should return a badge URL with the default style", async () => {
const result = await provider.getTrustBadge({ address, style: null });
expect(result).toContain(`/badge/${address.toLowerCase()}`);
expect(result).toContain("style=flat");
});

it("should return a badge URL with a custom style", async () => {
const result = await provider.getTrustBadge({ address, style: "detailed" });
expect(result).toContain("style=detailed");
});
});

describe("supportsNetwork", () => {
it("should return true for any network", () => {
expect(provider.supportsNetwork()).toBe(true);
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import { z } from "zod";
import { ActionProvider } from "../actionProvider";
import { CreateAction } from "../actionDecorator";
import { VerifyAgentSchema, GetTrustBadgeSchema } from "./schemas";
import { AGENTRADAR_BASE_URL } from "./constants";
import { AgentRadarVerifyResponse } from "./types";

/**
* AgentRadarActionProvider provides actions to check the on-chain trust of an AI
* agent or wallet via the AgentRadar API (a composite of ERC-8004 reputation, a
* scam-wallet database, and static analysis) before interacting with or paying it.
*
* AgentRadar is network-agnostic and read-only, so these actions require no wallet
* and no API key.
*/
export class AgentRadarActionProvider extends ActionProvider {
/**
* Constructor for the AgentRadarActionProvider class.
*/
constructor() {
super("agentradar", []);
}

/**
* Verifies an AI agent or wallet's trust with AgentRadar.
*
* @param args - The verification parameters (the address to check)
* @returns A JSON string with the trust score, verdict, and per-signal scores, or an error message
*/
@CreateAction({
name: "verify_agent",
description: `This tool checks an AI agent or wallet's AgentRadar trust score BEFORE you interact with or pay it.
It takes the following input:
- address: the EVM address (0x...) to verify

Important notes:
- Returns a composite trust score (0-100), a verdict (TRUSTED, VERIFIED, CAUTION, RISKY, or BLOCKED), and per-signal scores (identity, reputation, scam detection, and more)
- Use this to avoid paying or trusting scam or low-reputation agents
- Returns an error message string if the address is invalid or the request fails`,
schema: VerifyAgentSchema,
})
async verifyAgent(args: z.infer<typeof VerifyAgentSchema>): Promise<string> {
try {
const address = args.address.toLowerCase();
const response = await fetch(`${AGENTRADAR_BASE_URL}/verify?target=${address}`, {
headers: { accept: "application/json" },
});

if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}

const data = (await response.json()) as AgentRadarVerifyResponse;

return JSON.stringify(
{
address: data.address,
score: data.score,
verdict: data.verdict,
signals: data.signals,
riskFlags: data.riskFlags ?? [],
report: `${AGENTRADAR_BASE_URL}/verify?target=${address}`,
},
null,
2,
);
} catch (error: unknown) {
return `Error verifying agent: ${error instanceof Error ? error.message : String(error)}`;
}
}

/**
* Builds an embeddable AgentRadar trust-badge URL for an address.
*
* @param args - The badge parameters (the address and an optional style)
* @returns A URL string pointing to the badge SVG
*/
@CreateAction({
name: "get_trust_badge",
description: `This tool returns an embeddable AgentRadar trust-badge image URL (SVG) for an EVM address.
It takes the following inputs:
- address: the EVM address (0x...) to render a badge for
- style: optional badge style ('flat', 'pill', or 'detailed'; defaults to 'flat')

Returns a URL string pointing to the badge SVG.`,
schema: GetTrustBadgeSchema,
})
async getTrustBadge(args: z.infer<typeof GetTrustBadgeSchema>): Promise<string> {
const address = args.address.toLowerCase();
const style = args.style ?? "flat";
return `${AGENTRADAR_BASE_URL}/badge/${address}?style=${style}`;
}

/**
* Checks if the AgentRadar action provider supports the given network.
* AgentRadar is network-agnostic, so this always returns true.
*
* @returns True, as AgentRadar actions are supported on all networks.
*/
supportsNetwork(): boolean {
return true;
}
}

/**
* Creates a new instance of the AgentRadar action provider.
*
* @returns A new AgentRadarActionProvider instance
*/
export const agentRadarActionProvider = () => new AgentRadarActionProvider();
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/**
* Base URL for the AgentRadar API.
*/
export const AGENTRADAR_BASE_URL = "https://api.vvpro.ai";

/**
* Matches a 0x-prefixed, 20-byte EVM address.
*/
export const EVM_ADDRESS_REGEX = /^0x[0-9a-fA-F]{40}$/;
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./agentRadarActionProvider";
30 changes: 30 additions & 0 deletions typescript/agentkit/src/action-providers/agentradar/schemas.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { z } from "zod";
import { EVM_ADDRESS_REGEX } from "./constants";

/**
* Input schema for verifying an agent or wallet's trust.
*/
export const VerifyAgentSchema = z
.object({
address: z
.string()
.regex(EVM_ADDRESS_REGEX, "Must be a valid 0x-prefixed EVM address")
.describe("The EVM address (0x...) of the agent or wallet to verify"),
})
.strict();

/**
* Input schema for fetching an embeddable trust badge.
*/
export const GetTrustBadgeSchema = z
.object({
address: z
.string()
.regex(EVM_ADDRESS_REGEX, "Must be a valid 0x-prefixed EVM address")
.describe("The EVM address (0x...) to render a trust badge for"),
style: z
.enum(["flat", "pill", "detailed"])
.nullable()
.describe("Optional badge style: 'flat', 'pill', or 'detailed'. Defaults to 'flat'."),
})
.strict();
31 changes: 31 additions & 0 deletions typescript/agentkit/src/action-providers/agentradar/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/**
* Per-signal trust scores returned by AgentRadar (each 0-100).
*/
export interface AgentRadarSignals {
identity?: number;
fidelity?: number;
health?: number;
reputation?: number;
scamDetection?: number;
external?: number;
}

/**
* A risk flag attached to an AgentRadar verification result.
*/
export interface AgentRadarRiskFlag {
severity: string;
message: string;
}

/**
* Response shape from the AgentRadar `/verify` endpoint.
*/
export interface AgentRadarVerifyResponse {
address: string;
caip10?: string;
score: number;
verdict: string;
signals?: AgentRadarSignals;
riskFlags?: AgentRadarRiskFlag[];
}
1 change: 1 addition & 0 deletions typescript/agentkit/src/action-providers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export * from "./actionProvider";
export * from "./customActionProvider";

export * from "./across";
export * from "./agentradar";
export * from "./alchemy";
export * from "./baseAccount";
export * from "./basename";
Expand Down
Loading