From 50573214cd15bbcc6cf5bcdd9f7aeaab1a30fdbd Mon Sep 17 00:00:00 2001 From: Vladimir Bichev Date: Tue, 16 Jun 2026 21:59:30 -0400 Subject: [PATCH] feat: add AgentRadar action provider Adds a network-agnostic, read-only AgentRadar action provider with two actions: - verify_agent: composite on-chain trust score (0-100) + verdict + per-signal breakdown (ERC-8004 reputation, scam database, static analysis) for an EVM address, so agents can check trust before interacting with or paying a counterparty. - get_trust_badge: embeddable trust-badge SVG URL for an address. No API key required. Includes schemas, types, unit tests, README, and a changeset. Co-authored-by: Cursor --- .../.changeset/agentradar-action-provider.md | 5 + typescript/agentkit/README.md | 13 +++ .../src/action-providers/agentradar/README.md | 70 +++++++++++ .../agentRadarActionProvider.test.ts | 84 +++++++++++++ .../agentradar/agentRadarActionProvider.ts | 110 ++++++++++++++++++ .../action-providers/agentradar/constants.ts | 9 ++ .../src/action-providers/agentradar/index.ts | 1 + .../action-providers/agentradar/schemas.ts | 30 +++++ .../src/action-providers/agentradar/types.ts | 31 +++++ .../agentkit/src/action-providers/index.ts | 1 + 10 files changed, 354 insertions(+) create mode 100644 typescript/.changeset/agentradar-action-provider.md create mode 100644 typescript/agentkit/src/action-providers/agentradar/README.md create mode 100644 typescript/agentkit/src/action-providers/agentradar/agentRadarActionProvider.test.ts create mode 100644 typescript/agentkit/src/action-providers/agentradar/agentRadarActionProvider.ts create mode 100644 typescript/agentkit/src/action-providers/agentradar/constants.ts create mode 100644 typescript/agentkit/src/action-providers/agentradar/index.ts create mode 100644 typescript/agentkit/src/action-providers/agentradar/schemas.ts create mode 100644 typescript/agentkit/src/action-providers/agentradar/types.ts diff --git a/typescript/.changeset/agentradar-action-provider.md b/typescript/.changeset/agentradar-action-provider.md new file mode 100644 index 000000000..b4827c08c --- /dev/null +++ b/typescript/.changeset/agentradar-action-provider.md @@ -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. diff --git a/typescript/agentkit/README.md b/typescript/agentkit/README.md index 37b14207f..a05ccd586 100644 --- a/typescript/agentkit/README.md +++ b/typescript/agentkit/README.md @@ -178,6 +178,19 @@ const agent = createAgent({
+AgentRadar + + + + + + + + + +
verify_agentChecks 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.
get_trust_badgeReturns an embeddable AgentRadar trust-badge image URL (SVG) for an EVM address.
+
+
Base Account diff --git a/typescript/agentkit/src/action-providers/agentradar/README.md b/typescript/agentkit/src/action-providers/agentradar/README.md new file mode 100644 index 000000000..54ce6374f --- /dev/null +++ b/typescript/agentkit/src/action-providers/agentradar/README.md @@ -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). +``` diff --git a/typescript/agentkit/src/action-providers/agentradar/agentRadarActionProvider.test.ts b/typescript/agentkit/src/action-providers/agentradar/agentRadarActionProvider.test.ts new file mode 100644 index 000000000..793195ed5 --- /dev/null +++ b/typescript/agentkit/src/action-providers/agentradar/agentRadarActionProvider.test.ts @@ -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); + }); + }); +}); diff --git a/typescript/agentkit/src/action-providers/agentradar/agentRadarActionProvider.ts b/typescript/agentkit/src/action-providers/agentradar/agentRadarActionProvider.ts new file mode 100644 index 000000000..2a4ee4108 --- /dev/null +++ b/typescript/agentkit/src/action-providers/agentradar/agentRadarActionProvider.ts @@ -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): Promise { + 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): Promise { + 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(); diff --git a/typescript/agentkit/src/action-providers/agentradar/constants.ts b/typescript/agentkit/src/action-providers/agentradar/constants.ts new file mode 100644 index 000000000..b752c4952 --- /dev/null +++ b/typescript/agentkit/src/action-providers/agentradar/constants.ts @@ -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}$/; diff --git a/typescript/agentkit/src/action-providers/agentradar/index.ts b/typescript/agentkit/src/action-providers/agentradar/index.ts new file mode 100644 index 000000000..3502f3cac --- /dev/null +++ b/typescript/agentkit/src/action-providers/agentradar/index.ts @@ -0,0 +1 @@ +export * from "./agentRadarActionProvider"; diff --git a/typescript/agentkit/src/action-providers/agentradar/schemas.ts b/typescript/agentkit/src/action-providers/agentradar/schemas.ts new file mode 100644 index 000000000..5d7609b75 --- /dev/null +++ b/typescript/agentkit/src/action-providers/agentradar/schemas.ts @@ -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(); diff --git a/typescript/agentkit/src/action-providers/agentradar/types.ts b/typescript/agentkit/src/action-providers/agentradar/types.ts new file mode 100644 index 000000000..ba0921b6d --- /dev/null +++ b/typescript/agentkit/src/action-providers/agentradar/types.ts @@ -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[]; +} diff --git a/typescript/agentkit/src/action-providers/index.ts b/typescript/agentkit/src/action-providers/index.ts index 9f7164086..44bc3fd74 100644 --- a/typescript/agentkit/src/action-providers/index.ts +++ b/typescript/agentkit/src/action-providers/index.ts @@ -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";