diff --git a/.env.example b/.env.example index c161c3a1c7..fa92e4ea54 100644 --- a/.env.example +++ b/.env.example @@ -352,6 +352,11 @@ COINBASE_GENERATED_WALLET_ID= # Not your address but the wallet ID from ge COINBASE_GENERATED_WALLET_HEX_SEED= # Not your address but the wallet hex seed from generating a wallet through the plugin and calling export COINBASE_NOTIFICATION_URI= # For webhook plugin the uri you want to send the webhook to for dummy ones use https://webhook.site +# Coinbase AgentKit +COINBASE_AGENT_KIT_NETWORK= # defaults to 'base-sepolia' +CDP_API_KEY_NAME= +CDP_API_KEY_PRIVATE_KEY= + # Coinbase Charity Configuration IS_CHARITABLE=false # Set to true to enable charity donations CHARITY_ADDRESS_BASE=0x1234567890123456789012345678901234567890 diff --git a/.github/workflows/smoke-tests.yml b/.github/workflows/smoke-tests.yml index 2c088afbe6..00d1a5116b 100644 --- a/.github/workflows/smoke-tests.yml +++ b/.github/workflows/smoke-tests.yml @@ -10,17 +10,24 @@ on: jobs: smoke-tests: runs-on: ubuntu-latest + container: + image: node:23-bullseye steps: - uses: actions/checkout@v4 - - uses: pnpm/action-setup@v3 + - name: Cache pnpm + uses: actions/cache@v4 with: - version: 9.15.0 + path: | + ~/.pnpm-store + **/node_modules + key: ${{ runner.os }}-pnpm-${{ hashFiles('**/pnpm-lock.yaml') }} + restore-keys: ${{ runner.os }}-pnpm- - - uses: actions/setup-node@v4 + - name: Setup pnpm + uses: pnpm/action-setup@v3 with: - node-version: "23.3.0" - cache: "pnpm" + version: 9.15.0 - name: Run smoke tests run: pnpm run smokeTests diff --git a/.gitignore b/.gitignore index 7c6c92eb7b..a82e47b2b1 100644 --- a/.gitignore +++ b/.gitignore @@ -52,6 +52,8 @@ tsup.config.bundled_*.mjs .turbo .cursorrules .pnpm-store +instructions.md +wallet_data.txt coverage .eslintcache diff --git a/agent/package.json b/agent/package.json index 15815f3bd1..698efe3679 100644 --- a/agent/package.json +++ b/agent/package.json @@ -36,6 +36,7 @@ "@elizaos/core": "workspace:*", "@elizaos/plugin-0g": "workspace:*", "@elizaos/plugin-abstract": "workspace:*", + "@elizaos/plugin-agentkit": "workspace:*", "@elizaos/plugin-aptos": "workspace:*", "@elizaos/plugin-birdeye": "workspace:*", "@elizaos/plugin-coingecko": "workspace:*", diff --git a/client/src/lib/info.json b/client/src/lib/info.json index de0516e20d..18f2047dca 100644 --- a/client/src/lib/info.json +++ b/client/src/lib/info.json @@ -1 +1 @@ -{"version": "0.1.8+build.1"} +{"version": "0.1.9-alpha.1"} diff --git a/packages/plugin-agentkit/README.md b/packages/plugin-agentkit/README.md new file mode 100644 index 0000000000..dfd2499a9d --- /dev/null +++ b/packages/plugin-agentkit/README.md @@ -0,0 +1,123 @@ +# @elizaos/plugin-agentkit + +AgentKit plugin for Eliza that enables interaction with CDP AgentKit tools for NFT and token management. + +## Setup + +1. Install dependencies: + +```bash +pnpm install +``` + +2. Configure environment variables: + +```env +CDP_API_KEY_NAME=your_key_name +CDP_API_KEY_PRIVATE_KEY=your_private_key +``` + +3. Add the plugin to your character configuration: + +```json +{ + "plugins": ["@ai16z/plugin-agentkit"], + "settings": { + "secrets": { + "CDP_API_KEY_NAME": "your_key_name", + "CDP_API_KEY_PRIVATE_KEY": "your_private_key", + "networkId": "base-sepolia" + } + } +} +``` + +## Available Tools + +The plugin provides access to the following CDP AgentKit tools: + +- `GET_WALLET_DETAILS`: Get wallet information +- `DEPLOY_NFT`: Deploy a new NFT collection +- `DEPLOY_TOKEN`: Deploy a new token +- `GET_BALANCE`: Check token or NFT balance +- `MINT_NFT`: Mint NFTs from a collection +- `REGISTER_BASENAME`: Register a basename for NFTs +- `REQUEST_FAUCET_FUNDS`: Request testnet funds +- `TRADE`: Execute trades +- `TRANSFER`: Transfer tokens or NFTs +- `WOW_BUY_TOKEN`: Buy WOW tokens +- `WOW_SELL_TOKEN`: Sell WOW tokens +- `WOW_CREATE_TOKEN`: Create new WOW tokens + +## Usage Examples + +1. Get wallet details: + +``` +Can you show me my wallet details? +``` + +2. Deploy an NFT collection: + +``` +Deploy a new NFT collection called "Music NFTs" with symbol "MUSIC" +``` + +3. Create a token: + +``` +Create a new WOW token called "Artist Token" with symbol "ART" +``` + +4. Check balance: + +``` +What's my current balance? +``` + +## Development + +1. Build the plugin: + +```bash +pnpm build +``` + +2. Run in development mode: + +```bash +pnpm dev +``` + +## Dependencies + +- @elizaos/core +- @coinbase/cdp-agentkit-core +- @coinbase/cdp-langchain +- @langchain/core + +## Network Support + +The plugin currently supports the following networks: + +- Base Sepolia (default) +- Base Mainnet + +Configure the network using the `networkId` setting in your character configuration. + +## Troubleshooting + +1. If tools are not being triggered: + + - Verify CDP API key configuration + - Check network settings + - Ensure character configuration includes the plugin + +2. Common errors: + - "Cannot find package": Make sure dependencies are installed + - "API key not found": Check environment variables + - "Network error": Verify network configuration + +## License + +MIT diff --git a/packages/plugin-agentkit/package.json b/packages/plugin-agentkit/package.json new file mode 100644 index 0000000000..212617b58a --- /dev/null +++ b/packages/plugin-agentkit/package.json @@ -0,0 +1,18 @@ +{ + "name": "@elizaos/plugin-agentkit", + "version": "0.0.1", + "main": "dist/index.js", + "type": "module", + "types": "dist/index.d.ts", + "dependencies": { + "@elizaos/core": "workspace:*", + "@coinbase/cdp-agentkit-core": "^0.0.10", + "@coinbase/cdp-langchain": "^0.0.11", + "@langchain/core": "^0.3.27", + "tsup": "8.3.5" + }, + "scripts": { + "build": "tsup --format esm --dts", + "dev": "tsup --format esm --dts --watch" + } +} diff --git a/packages/plugin-agentkit/src/actions.ts b/packages/plugin-agentkit/src/actions.ts new file mode 100644 index 0000000000..5d1c721c93 --- /dev/null +++ b/packages/plugin-agentkit/src/actions.ts @@ -0,0 +1,178 @@ +import { + type Action, + generateText, + type HandlerCallback, + type IAgentRuntime, + type Memory, + ModelClass, + type State, + composeContext, + generateObject, +} from "@elizaos/core"; +import { CdpAgentkit } from "@coinbase/cdp-agentkit-core"; +import { CdpToolkit, type Tool } from "@coinbase/cdp-langchain"; + +type GetAgentKitActionsParams = { + getClient: () => Promise; + config?: { + networkId?: string; + }; +}; + +/** + * Get all AgentKit actions + */ +export async function getAgentKitActions({ + getClient, +}: GetAgentKitActionsParams): Promise { + const agentkit = await getClient(); + const cdpToolkit = new CdpToolkit(agentkit); + const tools = cdpToolkit.getTools(); + const actions = tools.map((tool: Tool) => ({ + name: tool.name.toUpperCase(), + description: tool.description, + similes: [], + validate: async () => true, + handler: async ( + runtime: IAgentRuntime, + message: Memory, + state: State | undefined, + options?: Record, + callback?: HandlerCallback + ): Promise => { + try { + const client = await getClient(); + let currentState = + state ?? (await runtime.composeState(message)); + currentState = + await runtime.updateRecentMessageState(currentState); + + const parameterContext = composeParameterContext( + tool, + currentState + ); + const parameters = await generateParameters( + runtime, + parameterContext, + tool + ); + + const result = await executeToolAction( + tool, + parameters, + client + ); + + const responseContext = composeResponseContext( + tool, + result, + currentState + ); + const response = await generateResponse( + runtime, + responseContext + ); + + callback?.({ text: response, content: result }); + return true; + } catch (error) { + const errorMessage = + error instanceof Error ? error.message : String(error); + callback?.({ + text: `Error executing action ${tool.name}: ${errorMessage}`, + content: { error: errorMessage }, + }); + return false; + } + }, + examples: [], + })); + return actions; +} + +async function executeToolAction( + tool: Tool, + parameters: any, + client: CdpAgentkit +): Promise { + const toolkit = new CdpToolkit(client); + const tools = toolkit.getTools(); + const selectedTool = tools.find((t) => t.name === tool.name); + + if (!selectedTool) { + throw new Error(`Tool ${tool.name} not found`); + } + + return await selectedTool.call(parameters); +} + +function composeParameterContext(tool: any, state: State): string { + const contextTemplate = `{{recentMessages}} + +Given the recent messages, extract the following information for the action "${tool.name}": +${tool.description} +`; + return composeContext({ state, template: contextTemplate }); +} + +async function generateParameters( + runtime: IAgentRuntime, + context: string, + tool: Tool +): Promise { + const { object } = await generateObject({ + runtime, + context, + modelClass: ModelClass.LARGE, + schema: tool.schema, + }); + + return object; +} + +function composeResponseContext( + tool: Tool, + result: unknown, + state: State +): string { + const responseTemplate = ` +# Action Examples +{{actionExamples}} + +# Knowledge +{{knowledge}} + +# Task: Generate dialog and actions for the character {{agentName}}. +About {{agentName}}: +{{bio}} +{{lore}} + +{{providers}} + +{{attachments}} + +# Capabilities +Note that {{agentName}} is capable of reading/seeing/hearing various forms of media, including images, videos, audio, plaintext and PDFs. Recent attachments have been included above under the "Attachments" section. + +The action "${tool.name}" was executed successfully. +Here is the result: +${JSON.stringify(result)} + +{{actions}} + +Respond to the message knowing that the action was successful and these were the previous messages: +{{recentMessages}} +`; + return composeContext({ state, template: responseTemplate }); +} + +async function generateResponse( + runtime: IAgentRuntime, + context: string +): Promise { + return generateText({ + runtime, + context, + modelClass: ModelClass.LARGE, + }); +} diff --git a/packages/plugin-agentkit/src/index.ts b/packages/plugin-agentkit/src/index.ts new file mode 100644 index 0000000000..3901617b32 --- /dev/null +++ b/packages/plugin-agentkit/src/index.ts @@ -0,0 +1,16 @@ +import type { Plugin } from "@elizaos/core"; +import { walletProvider, getClient } from "./provider"; +import { getAgentKitActions } from "./actions"; + +export const agentKitPlugin: Plugin = { + name: "[AgentKit] Integration", + description: "AgentKit integration plugin", + providers: [walletProvider], + evaluators: [], + services: [], + actions: await getAgentKitActions({ + getClient, + }), +}; + +export default agentKitPlugin; diff --git a/packages/plugin-agentkit/src/provider.ts b/packages/plugin-agentkit/src/provider.ts new file mode 100644 index 0000000000..685469b09e --- /dev/null +++ b/packages/plugin-agentkit/src/provider.ts @@ -0,0 +1,49 @@ +import { type Provider, type IAgentRuntime } from "@elizaos/core"; +import { CdpAgentkit } from "@coinbase/cdp-agentkit-core"; +import * as fs from "fs"; + +const WALLET_DATA_FILE = "wallet_data.txt"; + +export async function getClient( + networkId: string = "base-sepolia" +): Promise { + let walletDataStr: string | null = null; + + // Read existing wallet data if available + if (fs.existsSync(WALLET_DATA_FILE)) { + try { + walletDataStr = fs.readFileSync(WALLET_DATA_FILE, "utf8"); + } catch (error) { + console.error("Error reading wallet data:", error); + // Continue without wallet data + } + } + + // Configure CDP AgentKit + const config = { + cdpWalletData: walletDataStr || undefined, + networkId, + }; + + const agentkit = await CdpAgentkit.configureWithWallet(config); + // Save wallet data + const exportedWallet = await agentkit.exportWallet(); + fs.writeFileSync(WALLET_DATA_FILE, exportedWallet); + + return agentkit; +} + +export const walletProvider: Provider = { + async get(runtime: IAgentRuntime): Promise { + try { + const client = await getClient( + runtime.getSetting("COINBASE_AGENT_KIT_NETWORK") + ); + const address = (await (client as any).wallet.addresses)[0].id; + return `AgentKit Wallet Address: ${address}`; + } catch (error) { + console.error("Error in AgentKit provider:", error); + return null; + } + }, +}; diff --git a/packages/plugin-agentkit/tsconfig.json b/packages/plugin-agentkit/tsconfig.json new file mode 100644 index 0000000000..f642a90aee --- /dev/null +++ b/packages/plugin-agentkit/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../core/tsconfig.json", + "compilerOptions": { + "outDir": "dist", + "rootDir": "./src", + "declaration": true + }, + "include": ["src"] +} diff --git a/packages/plugin-agentkit/tsup.config.ts b/packages/plugin-agentkit/tsup.config.ts new file mode 100644 index 0000000000..a68ccd636a --- /dev/null +++ b/packages/plugin-agentkit/tsup.config.ts @@ -0,0 +1,21 @@ +import { defineConfig } from "tsup"; + +export default defineConfig({ + entry: ["src/index.ts"], + outDir: "dist", + sourcemap: true, + clean: true, + format: ["esm"], // Ensure you're targeting CommonJS + external: [ + "dotenv", // Externalize dotenv to prevent bundling + "fs", // Externalize fs to use Node.js built-in module + "path", // Externalize other built-ins if necessary + "@reflink/reflink", + "@node-llama-cpp", + "https", + "http", + "agentkeepalive", + "viem", + "@lifi/sdk", + ], +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7f80a26b77..c690b66e77 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -172,6 +172,9 @@ importers: '@elizaos/plugin-abstract': specifier: workspace:* version: link:../packages/plugin-abstract + '@elizaos/plugin-agentkit': + specifier: workspace:* + version: link:../packages/plugin-agentkit '@elizaos/plugin-akash': specifier: workspace:* version: link:../packages/plugin-akash @@ -1277,6 +1280,24 @@ importers: specifier: 7.1.0 version: 7.1.0 + packages/plugin-agentkit: + dependencies: + '@coinbase/cdp-agentkit-core': + specifier: ^0.0.10 + version: 0.0.10(bufferutil@4.0.9)(typescript@5.7.3)(utf-8-validate@6.0.5) + '@coinbase/cdp-langchain': + specifier: ^0.0.11 + version: 0.0.11(@coinbase/coinbase-sdk@0.13.0(bufferutil@4.0.9)(typescript@5.7.3)(utf-8-validate@6.0.5)(zod@3.23.8))(bufferutil@4.0.9)(openai@4.78.1(encoding@0.1.13)(zod@3.23.8))(typescript@5.7.3)(utf-8-validate@6.0.5) + '@elizaos/core': + specifier: workspace:* + version: link:../core + '@langchain/core': + specifier: ^0.3.27 + version: 0.3.30(openai@4.78.1(encoding@0.1.13)(zod@3.23.8)) + tsup: + specifier: 8.3.5 + version: 8.3.5(@swc/core@1.10.7(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.5.1)(tsx@4.19.2)(typescript@5.7.3)(yaml@2.7.0) + packages/plugin-akash: dependencies: '@akashnetwork/akash-api': @@ -4539,9 +4560,20 @@ packages: '@coinbase-samples/advanced-sdk-ts@file:packages/plugin-coinbase/advanced-sdk-ts': resolution: {directory: packages/plugin-coinbase/advanced-sdk-ts, type: directory} + '@coinbase/cdp-agentkit-core@0.0.10': + resolution: {integrity: sha512-EFTaCkCd1445B4jq3LxVJaqw+r4BrwyjTAOoEabKrv9BycCPgS7fPRLuuugmnJRbSEtlG90NbsRZ7B6YkQRJ/g==} + + '@coinbase/cdp-langchain@0.0.11': + resolution: {integrity: sha512-RqnViEuhPHa0uTWTA08R6G7JIco8s4hPiX/ChbeXC0q4h+0cGATC1bJxIW73NMJXEZfLPitM0871UZPdnDXxuw==} + peerDependencies: + '@coinbase/coinbase-sdk': ^0.13.0 + '@coinbase/coinbase-sdk@0.10.0': resolution: {integrity: sha512-sqLH7dE/0XSn5jHddjVrC1PR77sQUEytYcQAlH2d8STqRARcvddxVAByECUIL32MpbdJY7Wca3KfSa6qo811Mg==} + '@coinbase/coinbase-sdk@0.13.0': + resolution: {integrity: sha512-qYOcFwTANhiJvSTF2sn53Hkycj2UebOIzieNOkg42qWD606gCudCBuzV3PDrOQYVJBS/g10hyX10u5yPkIZ89w==} + '@coinbase/wallet-sdk@4.2.4': resolution: {integrity: sha512-wJ9QOXOhRdGermKAoJSr4JgGqZm/Um0m+ecywzEC9qSOu3TXuVcG3k0XXTXW11UBgjdoPRuf5kAwRX3T9BynFA==} @@ -25667,6 +25699,31 @@ snapshots: transitivePeerDependencies: - encoding + '@coinbase/cdp-agentkit-core@0.0.10(bufferutil@4.0.9)(typescript@5.7.3)(utf-8-validate@6.0.5)': + dependencies: + '@coinbase/coinbase-sdk': 0.13.0(bufferutil@4.0.9)(typescript@5.7.3)(utf-8-validate@6.0.5)(zod@3.23.8) + twitter-api-v2: 1.19.0 + viem: 2.21.58(bufferutil@4.0.9)(typescript@5.7.3)(utf-8-validate@6.0.5)(zod@3.23.8) + zod: 3.23.8 + transitivePeerDependencies: + - bufferutil + - debug + - typescript + - utf-8-validate + + '@coinbase/cdp-langchain@0.0.11(@coinbase/coinbase-sdk@0.13.0(bufferutil@4.0.9)(typescript@5.7.3)(utf-8-validate@6.0.5)(zod@3.23.8))(bufferutil@4.0.9)(openai@4.78.1(encoding@0.1.13)(zod@3.23.8))(typescript@5.7.3)(utf-8-validate@6.0.5)': + dependencies: + '@coinbase/cdp-agentkit-core': 0.0.10(bufferutil@4.0.9)(typescript@5.7.3)(utf-8-validate@6.0.5) + '@coinbase/coinbase-sdk': 0.13.0(bufferutil@4.0.9)(typescript@5.7.3)(utf-8-validate@6.0.5)(zod@3.23.8) + '@langchain/core': 0.3.30(openai@4.78.1(encoding@0.1.13)(zod@3.23.8)) + zod: 3.23.8 + transitivePeerDependencies: + - bufferutil + - debug + - openai + - typescript + - utf-8-validate + '@coinbase/coinbase-sdk@0.10.0(bufferutil@4.0.9)(typescript@5.6.3)(utf-8-validate@6.0.5)(zod@3.24.1)': dependencies: '@scure/bip32': 1.6.1 @@ -25689,6 +25746,28 @@ snapshots: - utf-8-validate - zod + '@coinbase/coinbase-sdk@0.13.0(bufferutil@4.0.9)(typescript@5.7.3)(utf-8-validate@6.0.5)(zod@3.23.8)': + dependencies: + '@scure/bip32': 1.6.1 + abitype: 1.0.8(typescript@5.7.3)(zod@3.23.8) + axios: 1.7.9(debug@4.4.0) + axios-mock-adapter: 1.22.0(axios@1.7.9) + axios-retry: 4.5.0(axios@1.7.9) + bip32: 4.0.0 + bip39: 3.1.0 + decimal.js: 10.4.3 + dotenv: 16.4.7 + ethers: 6.13.4(bufferutil@4.0.9)(utf-8-validate@6.0.5) + node-jose: 2.2.0 + secp256k1: 5.0.1 + viem: 2.21.58(bufferutil@4.0.9)(typescript@5.7.3)(utf-8-validate@6.0.5)(zod@3.23.8) + transitivePeerDependencies: + - bufferutil + - debug + - typescript + - utf-8-validate + - zod + '@coinbase/wallet-sdk@4.2.4': dependencies: '@noble/hashes': 1.6.1 @@ -29773,6 +29852,23 @@ snapshots: transitivePeerDependencies: - openai + '@langchain/core@0.3.30(openai@4.78.1(encoding@0.1.13)(zod@3.23.8))': + dependencies: + '@cfworker/json-schema': 4.1.0 + ansi-styles: 5.2.0 + camelcase: 6.3.0 + decamelize: 1.2.0 + js-tiktoken: 1.0.15 + langsmith: 0.2.15(openai@4.78.1(encoding@0.1.13)(zod@3.23.8)) + mustache: 4.2.0 + p-queue: 6.6.2 + p-retry: 4.6.2 + uuid: 10.0.0 + zod: 3.23.8 + zod-to-json-schema: 3.24.1(zod@3.23.8) + transitivePeerDependencies: + - openai + '@langchain/core@0.3.30(openai@4.78.1(encoding@0.1.13)(zod@3.24.1))': dependencies: '@cfworker/json-schema': 4.1.0 @@ -44381,6 +44477,17 @@ snapshots: optionalDependencies: openai: 4.73.0(encoding@0.1.13)(zod@3.23.8) + langsmith@0.2.15(openai@4.78.1(encoding@0.1.13)(zod@3.23.8)): + dependencies: + '@types/uuid': 10.0.0 + commander: 10.0.1 + p-queue: 6.6.2 + p-retry: 4.6.2 + semver: 7.6.3 + uuid: 10.0.0 + optionalDependencies: + openai: 4.78.1(encoding@0.1.13)(zod@3.23.8) + langsmith@0.2.15(openai@4.78.1(encoding@0.1.13)(zod@3.24.1)): dependencies: '@types/uuid': 10.0.0