From f4c4c5f6aace08bc06bbf57f577145ad99b4d48d Mon Sep 17 00:00:00 2001 From: Andy Wang <41224501+andy-t-wang@users.noreply.github.com> Date: Fri, 27 Mar 2026 14:13:19 -0700 Subject: [PATCH 1/2] chore: bump @worldcoin/agentkit to 0.1.6 Co-Authored-By: Claude Opus 4.6 (1M context) --- agentkit/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agentkit/package.json b/agentkit/package.json index 8a3119c..4503b06 100644 --- a/agentkit/package.json +++ b/agentkit/package.json @@ -1,6 +1,6 @@ { "name": "@worldcoin/agentkit", - "version": "0.1.5", + "version": "0.1.6", "main": "./dist/cjs/index.js", "module": "./dist/esm/index.mjs", "types": "./dist/cjs/index.d.ts", From 67aedad3bfa0f55653fd74327586fe4634077242 Mon Sep 17 00:00:00 2001 From: Andy Wang <41224501+andy-t-wang@users.noreply.github.com> Date: Sat, 28 Mar 2026 13:39:12 -0700 Subject: [PATCH 2/2] clean up --- AGENTS.md | 14 -- CLAUDE.md | 87 ++++++++++ README.md | 44 +++-- skills/agentkit-register-agent/SKILL.md | 23 --- skills/agentkit-x402/SKILL.md | 214 ++++++++++++++++++++++++ 5 files changed, 327 insertions(+), 55 deletions(-) delete mode 100644 AGENTS.md create mode 100644 CLAUDE.md delete mode 100644 skills/agentkit-register-agent/SKILL.md create mode 100644 skills/agentkit-x402/SKILL.md diff --git a/AGENTS.md b/AGENTS.md deleted file mode 100644 index ef670f3..0000000 --- a/AGENTS.md +++ /dev/null @@ -1,14 +0,0 @@ -# Agent Instructions - -Default registration path: `npx @worldcoin/agentkit-cli --llms` - -## Available Skills - -### `agentkit-register-agent` - -- Use for CLI registration flow, World ID verification, `--auto`, or `AgentBook.register(...)`. -- Start with `npx @worldcoin/agentkit-cli --llms`. - -### `integrate-agentkit` - -- Use for end-to-end AgentKit + x402 integration: World Chain payments, free/free-trial/discount modes, facilitator wiring, signature verification, or AgentBook lookups. diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..8b902fb --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,87 @@ +# CLAUDE.md + +## Project Overview + +Monorepo for the Worldcoin AgentKit — a toolkit for building AI agents that integrate with World ID and World Chain. + +- `agentkit/` — Core TypeScript library (`@worldcoin/agentkit`). Handles signature verification, x402 payments, AgentBook lookups, and agent registration validation. +- `cli/` — CLI tool (`@worldcoin/agentkit-cli`) for agent registration via World ID. +- `contracts/` — Solidity smart contracts (AgentBook registry). Built with Foundry. +- `skills/` — Claude AI integration skills for agentkit. + +## Dev Commands + +```bash +# Install dependencies +bun install + +# Build the library +bun run build:agentkit + +# Run TypeScript tests (from agentkit/) +cd agentkit && bun test + +# Run Solidity checks (from contracts/) +cd contracts && forge build && forge test -vvv + +# Format code +npx prettier --write . +``` + +## Pre-PR Checklist + +CI only runs Solidity checks — TypeScript issues must be caught locally. + +1. `bun run build:agentkit` — must compile cleanly +2. `cd agentkit && bun test` — all tests must pass +3. `npx prettier --write .` — format all files +4. If touching contracts: `cd contracts && forge fmt && forge build && forge test -vvv` + +## Code Style + +Prettier handles formatting (config in root `package.json`), but know the conventions: + +- **Tabs** for indentation, **no semicolons**, **single quotes** +- 120 character line width +- Trailing commas in ES5 positions +- Imports sorted descending via `prettier-plugin-sort-imports-desc` + +### Naming + +- `camelCase` for functions and variables +- `PascalCase` for types and interfaces +- `kebab-case` for file names +- `SCREAMING_SNAKE_CASE` for constants + +### Exports + +- Named exports only — no default exports +- Barrel file at `agentkit/src/index.ts` grouped by category (constants, types, server, chain utilities, etc.) +- New public exports must be added to the barrel file + +### TypeScript + +- Strict mode is enabled — do not weaken it +- Use the `type` keyword for type-only imports: `import type { Foo } from './bar'` +- Zod schemas co-located with their types, derive types via `z.infer` +- Bun native test runner (`bun:test`) — not Jest, not Vitest + +## Architecture Constraints + +- **Viem** for all Ethereum interactions — never ethers.js +- **World Chain** is the only supported chain — do not add other chains +- **Zod** for runtime validation at system boundaries +- **Dual ESM/CJS** output from tsup — do not break either format +- **Bun** is the package manager and test runner + +## Commit Conventions + +Follow conventional commits with short imperative descriptions: + +``` +fix: correct signature validation for empty payloads +feat(agent-book): add batch lookup support +chore: bump @worldcoin/agentkit to 0.1.7 +``` + +Use scopes when the change is isolated to a specific module (e.g., `agent-book`, `cli`, `evm`, `solana`). diff --git a/README.md b/README.md index c9ef37b..d5fc216 100644 --- a/README.md +++ b/README.md @@ -2,34 +2,42 @@ Verify that an agent is backed by a real, World ID-verified human. -Agent Kit is an x402 extension for websites and APIs that want to recognize human-backed agents. It combines: +## What It Does -- `@worldcoin/agentkit-cli` for registering an agent wallet in AgentBook with a World ID proof -- `@worldcoin/agentkit` for integrating AgentBook-backed verification into x402 server flows +1. An agent wallet is registered in AgentBook using a World ID proof. +2. A website or API using x402 challenges the agent to sign a CAIP-122 message. +3. The server verifies the signature, resolves the registering human from AgentBook, and applies the configured access policy. -Registration does not have to require the agent to hold gas. `agentkit register
` now defaults to Base mainnet plus the shared hosted relay at `https://x402-worldchain.vercel.app`, and `API_URL` can override it. +This lets applications distinguish between arbitrary automation and automation acting on behalf of a real human, without exposing the human's underlying identity. -For the common production setup, keep payments on World Chain and keep AgentBook lookup on Base mainnet. +## For Agents -## Default Registration Flow +### Register -Start with this prompt: +Register your wallet in AgentBook so servers can verify you are human-backed. Registration is gasless by default (uses a hosted relay on Base mainnet). -```text -Run `npx @worldcoin/agentkit-cli --llms`, then help me register your wallet address in the AgentBook. +```bash +npx @worldcoin/agentkit-cli register ``` -Use the CLI's machine-readable guidance instead of reconstructing the flow from repo docs. +This will prompt a World ID verification via World App. You only need to register once per wallet. -## What It Does +For the full registration guide (manual mode, custom relays, Base Sepolia): [`./cli/REGISTRATION.md`](./cli/REGISTRATION.md) -1. An agent wallet is registered in AgentBook using a World ID proof. -2. A website or API using x402 challenges the agent to sign a CAIP-122 message. -3. The server verifies the signature, resolves the registering human from AgentBook, and applies the configured access policy. +### Use -This lets applications distinguish between arbitrary automation and automation acting on behalf of a real human, without exposing the human's underlying identity. +Once registered, you can authenticate with any x402 endpoint that has the AgentKit extension to get free or discounted access instead of paying. + +The full flow — parsing 402 responses, signing the CAIP-122 challenge, and sending the `agentkit` header — is documented in the agent skill: [`./skills/agentkit-x402/SKILL.md`](./skills/agentkit-x402/SKILL.md) + +## For x402 Developers -## Docs +### Integrate + +Add AgentKit to your x402 server to offer human-backed agents free access, free trials, or discounts. + +```bash +npm install @worldcoin/agentkit +``` -- Registration: [`./cli/REGISTRATION.md`](./cli/REGISTRATION.md) -- Server integration: [`./agentkit/DOCS.md`](./agentkit/DOCS.md) for the end-to-end x402 flow, including World Chain payments plus Base AgentBook lookup +For the full integration guide (hooks setup, access modes, World Chain payments, AgentBook configuration): [`./agentkit/DOCS.md`](./agentkit/DOCS.md) diff --git a/skills/agentkit-register-agent/SKILL.md b/skills/agentkit-register-agent/SKILL.md deleted file mode 100644 index 39c09b6..0000000 --- a/skills/agentkit-register-agent/SKILL.md +++ /dev/null @@ -1,23 +0,0 @@ ---- -name: agentkit-register-agent -description: Use this skill when working on AgentBook registration flows: CLI usage, World ID verification, QR or deep-link registration steps, manual vs --auto submission, register payloads, or public docs for registering an agent wallet. ---- - -# AgentKit Register Agent - -Use this skill for registering an agent wallet with AgentBook. - -## Workflow - -1. Default to the CLI's machine-readable guidance first: `npx @worldcoin/agentkit-cli --llms`. -2. Read [`../../cli/REGISTRATION.md`](../../cli/REGISTRATION.md) for the public registration flow if more context is needed. -3. Read [`../../cli/src/index.ts`](../../cli/src/index.ts) if you need exact runtime behavior, env vars, or response fields. -4. If the task references the contract call directly, confirm the function signature in [`../../contracts/src/interfaces/IAgentBook.sol`](../../contracts/src/interfaces/IAgentBook.sol). - -## Ground rules - -- Treat `npx @worldcoin/agentkit-cli --llms` as the default entrypoint for agent-driven registration help. -- Treat the CLI source as the source of truth for supported flags and request payloads. -- Keep examples aligned with the actual supported networks: `base` and `base-sepolia`. -- When documenting registration defaults, note that `register
` defaults to Base mainnet plus automatic relay submission. For Base mainnet, the default relay is `https://x402-worldchain.vercel.app` unless `API_URL` overrides it. Use `POST {API_URL}/register` for the relay payload shape. -- Do not claim the CLI submits on-chain transactions unless `--auto` is used. For networks without a default relay, `API_URL` must be configured. diff --git a/skills/agentkit-x402/SKILL.md b/skills/agentkit-x402/SKILL.md new file mode 100644 index 0000000..609e466 --- /dev/null +++ b/skills/agentkit-x402/SKILL.md @@ -0,0 +1,214 @@ +--- +name: agentkit-x402 +description: Use this skill when you receive a 402 Payment Required response that contains an `agentkit` extension. Covers checking 402 responses for the AgentKit extension before paying, constructing and signing a CAIP-122 challenge (SIWE for EVM, SIWS for Solana), sending the signed `agentkit` HTTP header, and interpreting access modes (free, free-trial, discount). Supports both EOA wallets (EIP-191) and Smart Contract Wallets (ERC-1271, e.g. Coinbase Smart Wallet, Safe). +--- + +# Using AgentKit as an Agent + +This skill is for AI agents that interact with websites and APIs protected by x402 + AgentKit. It tells you how to prove you are backed by a real human so you can get free or discounted access instead of paying. + +## Critical Rule: Always Check for AgentKit Before Paying + +When you receive a `402 Payment Required` response, **do not immediately proceed to payment**. First inspect the response for an `agentkit` extension. If one is present, attempt the AgentKit authentication flow below. Only fall back to payment if: + +- There is no `agentkit` extension in the 402 response +- Your AgentKit authentication attempt fails and the server requires payment +- You have exhausted your free-trial uses + +## Wallet Types + +Your wallet determines how you sign the challenge. There are two types: + +### EOA (Externally Owned Account) + +A standard wallet where you directly hold the private key (e.g. a raw private key, a mnemonic-derived wallet). + +- **Signature type:** `eip191` +- **How to sign:** Use `personal_sign` (EIP-191) to sign the SIWE message +- **Example:** `wallet.signMessage(siweMessage)` + +### Smart Contract Wallet (SCW) + +A wallet where the "account" is a smart contract and signing is done by an underlying owner key. The server verifies your signature on-chain via the contract's `isValidSignature` method (ERC-1271). + +Examples: Coinbase Smart Wallet, Safe, any ERC-4337 account. + +- **Signature type:** `eip1271` +- **How to sign:** Sign the SIWE message using the wallet's SDK or internal signer. The signature format depends on the wallet implementation — use whatever the wallet SDK provides. +- **Example (Coinbase CDP):** `account.signMessage({ message: siweMessage })` + +If you are unsure which type your wallet is: if you created it from a private key or mnemonic, it is an EOA. If you created it through a wallet SDK (Coinbase CDP, Safe SDK, etc.), it is likely an SCW. + +## The AgentKit Flow + +### Step 1: Parse the 402 Response + +When you receive a `402 Payment Required`, look for the `agentkit` extension in the response body. The 402 response contains `x402` data with extensions. The `agentkit` extension looks like: + +```json +{ + "agentkit": { + "info": { + "domain": "api.example.com", + "uri": "https://api.example.com/data", + "version": "1", + "nonce": "abc123", + "issuedAt": "2025-01-01T00:00:00.000Z", + "statement": "Verify your agent is backed by a real human" + }, + "supportedChains": [ + { "chainId": "eip155:8453", "type": "eip191" }, + { "chainId": "eip155:8453", "type": "eip1271" } + ], + "schema": { ... } + } +} +``` + +Key fields to extract: + +- **`info`** — the challenge data you must sign +- **`supportedChains`** — which chains and signature types the server accepts +- **`mode`** (if present) — tells you the access policy: `free`, `free-trial`, or `discount` + +### Step 2: Pick a Chain and Signature Type + +Match your wallet to one of the `supportedChains` entries: + +| Your wallet | Match `chainId` | Use `type` | +|--------------------|----------------------|-------------| +| EVM EOA | `eip155:*` | `eip191` | +| EVM Smart Contract | `eip155:*` | `eip1271` | + +Pick the entry that matches both your chain and wallet type. + +### Step 3: Construct and Sign the SIWE/SIWS Message + +Construct a SIWE (EIP-4361) message string from the challenge `info` fields. The format is a plain text string with this exact structure: + +``` +{domain} wants you to sign in with your Ethereum account: +{address} + +{statement} + +URI: {uri} +Version: {version} +Chain ID: {numericChainId} +Nonce: {nonce} +Issued At: {issuedAt} +``` + +Where `{numericChainId}` is extracted from the CAIP-2 chain ID (e.g. `eip155:8453` becomes `8453`), and `{address}` must be EIP-55 checksummed. + +If the challenge includes optional fields, append them in this order (only include lines for fields that are present): + +``` +Expiration Time: {expirationTime} +Not Before: {notBefore} +Request ID: {requestId} +Resources: +- {resources[0]} +- {resources[1]} +``` + +Full example: + +``` +api.example.com wants you to sign in with your Ethereum account: +0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 + +Verify your agent is backed by a real human + +URI: https://api.example.com/data +Version: 1 +Chain ID: 8453 +Nonce: abc123def +Issued At: 2025-01-01T00:00:00.000Z +Expiration Time: 2025-01-01T00:05:00.000Z +Request ID: req-456 +Resources: +- https://api.example.com/tos +``` + +**Important formatting rules:** +- There must be a blank line before and after the `{statement}` line +- If there is no statement, there must be a single blank line between the address and `URI:` +- Each line ends with `\n` (LF, not CRLF) +- No trailing newline after the last line + +Then sign the message string: + +```typescript +// EOA — use personal_sign (EIP-191) +const signature = await wallet.signMessage(messageToSign) + +// SCW — use the wallet SDK's signMessage +const signature = await smartWallet.signMessage({ message: messageToSign }) +``` + +### Step 4: Send the Request + +Base64-encode a JSON object containing the challenge `info` fields plus your `address`, `type`, and `signature`, and send it as the `agentkit` HTTP header: + +```typescript +const header = btoa(JSON.stringify({ + ...info, // all fields from the challenge + address: walletAddress, + chainId: "eip155:8453", // from the supportedChains entry you picked + type: "eip191", // "eip191" for EOA, "eip1271" for SCW + signature: signature, // hex string from signing +})) + +const response = await fetch("https://api.example.com/data", { + headers: { + "agentkit": header, + }, +}) +``` + +If the server grants access (based on the mode), you will receive the resource directly — no payment needed. + +If the server responds with another 402 or an error, fall back to the normal x402 payment flow. + +## Access Modes + +The `mode` field in the AgentKit extension tells you what to expect: + +| Mode | What it means | +|--------------|----------------------------------------------------------------| +| `free` | Human-backed agents always get free access | +| `free-trial` | First N requests are free, then you must pay (N is per human, not per agent) | +| `discount` | You get N% off the price (pay the discounted amount via x402) | + +For `discount` mode: send **both** the `agentkit` header and the x402 payment header, but pay the discounted price. The server will reconcile the underpayment using your human-backed status. + +## Common Errors and How to Handle Them + +### "Agent is not registered in the AgentBook" + +Your wallet address is not registered. You need to register first using the AgentKit CLI: + +```bash +npx @worldcoin/agentkit-cli register +``` + +This opens a World ID verification flow that ties your wallet to an anonymous human identifier on-chain. Registration only needs to happen once per wallet. + +### "Signature verification failed" + +- **Wrong signature type:** Make sure `type` matches your wallet. Use `eip191` for EOA, `eip1271` for SCW. +- **Wrong message format:** The SIWE message must follow the exact format described in Step 3. Pay close attention to blank lines around the statement and field ordering. +- **Wrong chain ID:** The `chainId` in the payload must be CAIP-2 format (`eip155:8453`), but the SIWE message `chainId` field must be the numeric chain ID (`8453`). + +### "Invalid agentkit header: not valid base64" + +Your header is not properly base64-encoded. Ensure you are encoding the full JSON string: `btoa(JSON.stringify(payload))`. + +### "Message validation failed" / "issuedAt is too old" + +The challenge has expired. Re-fetch the 402 response to get a fresh challenge (new nonce, new `issuedAt`) and sign again. Challenges expire after 5 minutes by default. + +### "Unsupported chain namespace" + +You are using a chain that the server does not support. Check `supportedChains` in the 402 response and pick a chain/type pair that matches your wallet.