adk
by yueranlu · v1.0.0
A guide to build AI bots with Botpress's Agent Development Kit (ADK)
Discussion
Sign in to join the discussion.
Scan Report
Scanners4/5 ran
clawguard-rules▸44 findings14ms
View logs
1[2026-02-12T22:42:52.384Z] Running @yourclaw/clawguard-rules pattern matcher2Scanning: /tmp/clawguard-scan-DpTogr/repo/skills/yueranlu/botpress-adk/SKILL.md3Content length: 56871 chars4Patterns matched: 445 [critical] MAL-001: Remote code execution via pipe-to-shell pattern6 [low] PI-041: Possible base64-encoded payload7 [low] PI-041: Possible base64-encoded payload8 [low] PI-041: Possible base64-encoded payload9 [low] PI-041: Possible base64-encoded payload10 [low] PI-041: Possible base64-encoded payload11 [low] PI-041: Possible base64-encoded payload12 [low] PI-041: Possible base64-encoded payload13 [low] PI-041: Possible base64-encoded payload14 [low] PI-041: Possible base64-encoded payload15 [low] PI-041: Possible base64-encoded payload16 [low] PI-041: Possible base64-encoded payload17 [low] PI-041: Possible base64-encoded payload18 [low] PI-041: Possible base64-encoded payload19 [low] PI-041: Possible base64-encoded payload20 [low] PI-041: Possible base64-encoded payload21 [low] PI-041: Possible base64-encoded payload22 [low] PI-041: Possible base64-encoded payload23 [low] PI-041: Possible base64-encoded payload24 [low] PI-041: Possible base64-encoded payload25 [low] PI-041: Possible base64-encoded payload26 [low] PI-041: Possible base64-encoded payload27 [low] PI-041: Possible base64-encoded payload28 [low] PI-041: Possible base64-encoded payload29 [low] PI-041: Possible base64-encoded payload30 [low] PI-041: Possible base64-encoded payload31 [low] PI-041: Possible base64-encoded payload32 [low] PI-041: Possible base64-encoded payload33 [low] PI-041: Possible base64-encoded payload34 [low] PI-041: Possible base64-encoded payload35 [low] PI-041: Possible base64-encoded payload36 [low] PI-041: Possible base64-encoded payload37 [low] PI-041: Possible base64-encoded payload38 [low] PI-041: Possible base64-encoded payload39 [low] PI-041: Possible base64-encoded payload40 [low] PI-041: Possible base64-encoded payload41 [low] PI-041: Possible base64-encoded payload42 [low] PI-041: Possible base64-encoded payload43 [low] PI-041: Possible base64-encoded payload44 [low] PI-041: Possible base64-encoded payload45 [low] PI-041: Possible base64-encoded payload46 [low] PI-041: Possible base64-encoded payload47 [low] PI-041: Possible base64-encoded payload48 [low] PI-041: Possible base64-encoded payload49✓ Completed in 14ms
gitleaks▸0 findings91874ms
View logs
1[2026-02-12T22:44:24.258Z] $ gitleaks detect --source /tmp/clawguard-scan-DpTogr/repo/skills/yueranlu/botpress-adk --report-format json --report-path /dev/stdout --no-git23⚠ stderr output:4○5 │╲6 │ ○7 ○ ░8 ░ gitleaks910[90m10:44PM[0m [31mFTL[0m [1mReport path is not writable: /dev/stdout[0m [36merror=[0m[31m[1m"open /dev/stdout: no such device or address"[0m[0m1112Process exited with code 113✓ Completed in 91874ms
semgrep▸0 findings150527ms
View logs
1[2026-02-12T22:45:22.916Z] $ semgrep scan --json --quiet --config auto /tmp/clawguard-scan-DpTogr/repo/skills/yueranlu/botpress-adk2{"version":"1.151.0","results":[],"errors":[],"paths":{"scanned":["/tmp/clawguard-scan-DpTogr/repo/skills/yueranlu/botpress-adk/README.md","/tmp/clawguard-scan-DpTogr/repo/skills/yueranlu/botpress-adk/SKILL.md","/tmp/clawguard-scan-DpTogr/repo/skills/yueranlu/botpress-adk/_meta.json"]},"time":{"rules":[],"rules_parse_time":13.623882055282593,"profiling_times":{"config_time":18.02668786048889,"core_time":19.322453498840332,"ignores_time":0.02347111701965332,"total_time":37.423030853271484},"parsing_time":{"total_time":0.0,"per_file_time":{"mean":0.0,"std_dev":0.0},"very_slow_stats":{"time_ratio":0.0,"count_ratio":0.0},"very_slow_files":[]},"scanning_time":{"total_time":1.0944490432739258,"per_file_time":{"mean":0.15634986332484654,"std_dev":0.06969707594804712},"very_slow_stats":{"time_ratio":0.0,"count_ratio":0.0},"very_slow_files":[]},"matching_time":{"total_time":0.0,"per_file_and_rule_time":{"mean":0.0,"std_dev":0.0},"very_slow_stats":{"time_ratio":0.0,"count_ratio":0.0},"very_slow_rules_on_files":[]},"tainting_time":{"total_time":0.0,"per_def_and_rule_time":{"mean":0.0,"std_dev":0.0},"very_slow_stats":{"time_ratio":0.0,"count_ratio":0.0},"very_slow_rules_on_defs":[]},"fixpoint_timeouts":[],"prefiltering":{"project_level_time":0.0,"file_level_time":0.0,"rules_with_project_prefilters_ratio":0.0,"rules_with_file_prefilters_ratio":0.9797297297297297,"rules_selected_ratio":0.04054054054054054,"rules_matched_ratio":0.04054054054054054},"targets":[],"total_bytes":0,"max_memory_bytes":1231881024},"engine_requested":"OSS","skipped_rules":[],"profiling_results":[]}34Process exited with code 05✓ Completed in 150527ms
mcp-scan▸1 finding104869ms
View logs
1[2026-02-12T22:44:37.261Z] $ mcp-scan --skills /tmp/clawguard-scan-DpTogr/repo/skills/yueranlu/botpress-adk --json2{3 "/tmp/clawguard-scan-DpTogr/repo/skills/yueranlu": {4 "client": "not-available",5 "path": "/tmp/clawguard-scan-DpTogr/repo/skills/yueranlu",6 "servers": [7 {8 "name": "botpress-adk",9 "server": {10 "path": "/tmp/clawguard-scan-DpTogr/repo/skills/yueranlu/botpress-adk",11 "type": "skill"12 },13 "signature": {14 "metadata": {15 "meta": null,16 "protocolVersion": "built-in",17 "capabilities": {18 "experimental": null,19 "logging": null,20 "prompts": null,21 "resources": null,22 "tools": {23 "listChanged": false24 },25 "completions": null,26 "tasks": null27 },28 "serverInfo": {29 "name": "adk",30 "title": null,31 "version": "skills",32 "websiteUrl": null,33 "icons": null34 },35 "instructions": "A guide to build AI bots with Botpress's Agent Development Kit (ADK)",36 "prompts": {37 "listChanged": false38 },39 "resources": {40 "subscribe": null,41 "listChanged": false42 }43 },44 "prompts": [45 {46 "name": "SKILL.md",47 "title": null,48 "description": "\n\n# Botpress ADK Development Guide\n\nA comprehensive guide for building AI bots with the Botpress Agent Development Kit (ADK).\n\n## When to Use\n\n- User asks to build a Botpress bot or chatbot\n- User mentions ADK, Agent Development Kit, or Botpress\n- User wants to create actions, tools, workflows, conversations, tables, triggers, or knowledge bases\n- User needs help with `adk` CLI commands (init, dev, deploy, link)\n- User has ADK-related errors or needs troubleshooting\n- User asks about bot configuration, state management, or integrations\n\n## Quick Reference\n\nThe ADK is a **convention-based TypeScript framework** where **file structure maps directly to bot behavior**.\n\n**Your role:** Guide users through the entire bot development lifecycle - from project setup to deployment. Use the patterns and code examples in this skill to write correct, working ADK code.\n\n**Key principle:** In ADK, **where you put files matters**. Each component type has a specific `src/` subdirectory, and files are auto-discovered based on location.\n\n## How to Use This Skill\n\n**This skill is your primary reference for building Botpress bots.** When a user asks you to build something with the ADK:\n\n1. **Identify what they need** - Is it a new bot, a feature (action, tool, workflow), data storage (table), or event handling (trigger)?\n2. **Check the correct directory** - Each component type goes in a specific `src/` subdirectory\n3. **Use the patterns below** - Follow the code examples exactly, they represent the correct ADK conventions\n4. **Run `adk --help`** - For CLI commands not covered here, or `adk <command> --help` for specific help\n\n**Decision Guide - What Component to Create:**\n\n| User Wants To... | Create This | Location |\n|------------------|-------------|----------|\n| Handle user messages | Conversation | `src/conversations/` |\n| Add a function the AI can call | Tool | `src/tools/` |\n| Add reusable business logic | Action | `src/actions/` |\n| Run background/scheduled tasks | Workflow | `src/workflows/` |\n| Store structured data | Table | `src/tables/` |\n| React to events (user created, etc.) | Trigger | `src/triggers/` |\n| Give AI access to docs/data | Knowledge Base | `src/knowledge/` |\n| Connect external service (Slack, etc.) | Integration | `adk add <name>` |\n\n**If the information in this skill isn't enough**, fetch the corresponding GitHub reference file (links provided in each section) for more detailed specifications.\n\n## Important: ADK is AI-Native\n\nThe ADK does **NOT** use traditional chatbot patterns. Don't create intents, entities, or dialog flows.\n\n**Instead of:**\n- Defining intents (`greet`, `orderPizza`, `checkStatus`)\n- Training entity extraction (`@pizzaSize`, `@toppings`)\n- Manually routing to intent handlers\n\n**ADK uses:**\n- `execute()` - The AI understands user intent naturally from instructions\n- Tools - AI autonomously decides when to call your functions\n- `zai.extract()` - Schema-based structured data extraction\n- Knowledge bases - RAG for grounding responses in your docs\n\n**Docs:** https://www.botpress.com/docs/adk/\n**GitHub:** https://github.com/botpress/skills/tree/master/skills/adk\n\n---\n\n## Prerequisites & Installation\n\nBefore using the ADK, ensure the user has:\n\n- **Botpress Account** - Create at https://app.botpress.cloud\n- **Node.js v22.0.0+** - Check with `node --version`\n- **Package Manager** - bun (recommended), pnpm, yarn, or npm\n\n**Install the ADK CLI:**\n\nmacOS & Linux:\n```bash\ncurl -fsSL https://github.com/botpress/adk/releases/latest/download/install.sh | bash\n```\n\nWindows (PowerShell):\n```powershell\npowershell -c \"irm https://github.com/botpress/adk/releases/latest/download/install.ps1 | iex\"\n```\n\n**Verify installation:**\n```bash\nadk --version\n```\n\nIf installation fails, check https://github.com/botpress/adk/releases for manual download options.\n\n**Docs:** https://www.botpress.com/docs/adk/quickstart\n**GitHub:** https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/cli.md\n\n---\n\n## Quick Start\n\nOnce the ADK CLI is installed, create a new bot:\n\n```bash\nadk init my-bot # Create project (choose \"Hello World\" template for beginners)\ncd my-bot\nnpm install # Or bun/pnpm/yarn\nadk login # Authenticate with Botpress Cloud\nadk add chat # Add the chat integration for testing\nadk dev # Start dev server with hot reload\nadk chat # Test in CLI (run in separate terminal)\nadk deploy # Deploy to production when ready\n```\n\nThe visual console at **http://localhost:3001/** lets you configure integrations and test the bot.\n\n**Docs:** https://www.botpress.com/docs/adk/quickstart\n**GitHub:** https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/cli.md\n\n---\n\n## Linking and Deploying Your Bot\n\n**IMPORTANT:** Your bot must be linked to Botpress Cloud and deployed for it to work. The ADK runs locally during development but the bot itself lives in Botpress Cloud.\n\n### The Correct Order: Link \u2192 Dev \u2192 Deploy\n\nFollow this order to get your bot working:\n\n```bash\n# 1. LINK - Connect your project to Botpress Cloud (creates agent.json)\nadk link\n\n# 2. DEV - Start the development server (hot reload, testing)\nadk dev\n\n# 3. DEPLOY - Push to production when ready\nadk deploy\n```\n\n**Step-by-step:**\n\n1. **`adk link`** - Links your local project to a bot in Botpress Cloud. This creates `agent.json` with your workspace and bot IDs. Run this first before anything else.\n\n2. **`adk dev`** - Starts the local development server with hot reloading. Opens the dev console at http://localhost:3001 where you can configure integrations and test your bot. Use `adk chat` in a separate terminal to test.\n\n3. **`adk deploy`** - Deploys your bot to production. Run this when you're ready for your bot to be live and accessible through production channels (Slack, WhatsApp, webchat, etc.).\n\n### Troubleshooting Errors\n\n**If you encounter errors when running `adk dev` or `adk deploy`:**\n\n1. **Check the logs** - Look at the terminal output or the logs panel in the dev console at http://localhost:3001\n2. **Copy the error message** - Select and copy the full error message from the logs\n3. **Ask for help** - Paste the error back to the AI assistant and ask it to help fix the issue\n\nCommon error scenarios:\n- **Integration configuration errors:** Usually means an integration needs to be configured in the UI at localhost:3001\n- **Type errors:** Often caused by incorrect imports or schema mismatches\n- **Deployment failures:** May indicate missing environment variables or invalid configuration\n\n**Example workflow for fixing errors:**\n```\n1. Run `adk dev` or `adk deploy`\n2. See error in terminal/logs\n3. Copy the error message\n4. Tell the AI: \"I got this error when running adk dev: [paste error]\"\n5. The AI will help diagnose and fix the issue\n```\n\n**Docs:** https://www.botpress.com/docs/adk/quickstart\n**GitHub:** https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/cli.md\n\n---\n\n## Project Structure\n\n**Critical rule:** File location determines behavior. Place components in the correct `src/` subdirectory or they won't be discovered.\n\n```\nmy-bot/\n\u251c\u2500\u2500 agent.config.ts # Bot configuration: name, models, state schemas, integrations\n\u251c\u2500\u2500 agent.json # Workspace/bot IDs (auto-generated by adk link/dev, add to .gitignore)\n\u251c\u2500\u2500 package.json # Node.js dependencies and scripts (dev, build, deploy)\n\u251c\u2500\u2500 tsconfig.json # TypeScript configuration\n\u251c\u2500\u2500 .env # API keys and secrets (never commit!)\n\u251c\u2500\u2500 .gitignore # Should include: agent.json, .env, node_modules/, .botpress/\n\u251c\u2500\u2500 src/\n\u2502 \u251c\u2500\u2500 conversations/ # Handle incoming messages \u2192 use execute() for AI responses\n\u2502 \u251c\u2500\u2500 workflows/ # Background processes \u2192 use step() for resumable operations\n\u2502 \u251c\u2500\u2500 actions/ # Reusable functions \u2192 call from anywhere with actions.name()\n\u2502 \u251c\u2500\u2500 tools/ # AI-callable functions \u2192 AI decides when to invoke these\n\u2502 \u251c\u2500\u2500 tables/ # Data storage \u2192 auto-synced to cloud, supports semantic search\n\u2502 \u251c\u2500\u2500 triggers/ # Event handlers \u2192 react to user.created, integration events, etc.\n\u2502 \u2514\u2500\u2500 knowledge/ # RAG sources \u2192 index docs, websites, or tables for AI context\n\u2514\u2500\u2500 .botpress/ # Auto-generated types (never edit manually)\n```\n\n**Key Configuration Files:**\n\n- **agent.config.ts** - Primary configuration defining bot metadata, AI models, state schemas, and integrations (you edit this)\n- **agent.json** - Links agent to workspace/bot IDs. Auto-generated by `adk link` or `adk dev`. **Add to .gitignore** - contains environment-specific IDs that differ per developer\n- **package.json** - Node.js config with `@botpress/runtime` dependency and scripts for `dev`, `build`, `deploy`\n- **tsconfig.json** - TypeScript configuration for the project\n- **.env** - Environment variables for API keys and secrets (never commit!)\n- **.gitignore** - Should include: `agent.json`, `.env`, `node_modules/`, `.botpress/`\n\n**Docs:** https://www.botpress.com/docs/adk/project-structure\n**GitHub:** https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/agent-config.md\n\n---\n\n## Agent Configuration\n\nThe `agent.config.ts` file defines your bot's identity, AI models, state schemas, and integrations. Always start here when setting up a new bot.\n\n```typescript\nimport { defineConfig, z } from \"@botpress/runtime\";\n\nexport default defineConfig({\n name: \"my-support-bot\",\n description: \"AI customer support assistant\",\n\n // AI models for different operations\n defaultModels: {\n autonomous: \"openai:gpt-4o\", // Used by execute() for conversations\n zai: \"openai:gpt-4o-mini\" // Used by zai operations (cheaper, faster)\n },\n\n // Global bot state - shared across all conversations and users\n bot: {\n state: z.object({\n maintenanceMode: z.boolean().default(false),\n totalConversations: z.number().default(0)\n })\n },\n\n // Per-user state - persists across all conversations for each user\n user: {\n state: z.object({\n name: z.string().optional(),\n tier: z.enum([\"free\", \"pro\"]).default(\"free\"),\n preferredLanguage: z.enum([\"en\", \"es\", \"fr\"]).default(\"en\")\n }),\n tags: {\n source: z.string(),\n region: z.string().optional()\n }\n },\n\n // Per-conversation state\n conversation: {\n state: z.object({\n context: z.string().optional()\n }),\n tags: {\n category: z.enum([\"support\", \"sales\", \"general\"]),\n priority: z.enum([\"low\", \"medium\", \"high\"]).optional()\n }\n },\n\n // Integrations your bot uses (ADK 1.9+ format)\n dependencies: {\n integrations: {\n chat: { version: \"chat@0.7.3\", enabled: true },\n slack: { version: \"slack@2.5.5\", enabled: true }\n }\n }\n});\n```\n\n**Available models:**\n- OpenAI: `openai:gpt-4o`, `openai:gpt-4o-mini`, `openai:gpt-4-turbo`\n- Anthropic: `anthropic:claude-3-5-sonnet`, `anthropic:claude-3-opus`\n- Google: `google:gemini-1.5-pro`, `google:gemini-1.5-flash`\n\n**Docs:** https://www.botpress.com/docs/adk/project-structure\n**GitHub:** https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/agent-config.md\n\n---\n\n## Core Concepts\n\n### 1. Actions - Reusable Business Logic\n\n**When to create an Action:**\n- You need reusable logic that will be called from multiple places (workflows, conversations, triggers)\n- You're wrapping an external API or database operation\n- You want testable, composable business logic\n- You need to call integration APIs (Slack, Linear, etc.) with custom logic\n\n**When NOT to use an Action (use a Tool instead):**\n- You want the AI to decide when to call it autonomously\n- The function should be available during `execute()`\n\nActions are **not** directly callable by the AI - convert them to tools with `.asTool()` if the AI needs to use them.\n\n**Location:** `src/actions/*.ts`\n\n```typescript\nimport { Action, z } from \"@botpress/runtime\";\n\nexport const fetchUser = new Action({\n name: \"fetchUser\",\n description: \"Retrieves user details from the database\",\n\n // Define input/output with Zod schemas for type safety\n input: z.object({ userId: z.string() }),\n output: z.object({ name: z.string(), email: z.string() }),\n\n // IMPORTANT: Handler receives { input, client } - destructure input INSIDE the handler\n async handler({ input, client }) {\n const { user } = await client.getUser({ id: input.userId });\n return { name: user.name, email: user.tags.email };\n }\n});\n```\n\n**Calling actions:**\n```typescript\nimport { actions } from \"@botpress/runtime\";\nconst userData = await actions.fetchUser({ userId: \"123\" });\n\n// To make an action callable by the AI, convert it to a tool:\ntools: [actions.fetchUser.asTool()]\n```\n\n**Key Rules:**\n- Handler receives `{ input, client }` - must destructure `input` inside the handler\n- Cannot destructure input fields directly in parameters\n- Can call other actions, integration actions, access state\n- Can be converted to tools with `.asTool()`\n\n**Docs:** https://www.botpress.com/docs/adk/concepts/actions\n**GitHub:** https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/actions.md\n\n---\n\n### 2. Tools - AI-Callable Functions\n\n**When to create a Tool:**\n- You want the AI to autonomously decide when to use this function\n- The function retrieves information the AI needs (search, lookup, fetch)\n- The function performs actions on behalf of the user (create ticket, send message)\n- You're building capabilities the AI should have during conversations\n\n**The AI decides when to use tools based on:**\n1. The tool's `description` - Make this clear and specific about WHEN to use it\n2. The input schema's `.describe()` fields - Help AI understand what parameters mean\n3. The conversation context and user's intent\n\n**Key difference from Actions:** Tools can destructure input directly; Actions cannot.\n\n**Location:** `src/tools/*.ts`\n\n```typescript\nimport { Autonomous, z } from \"@botpress/runtime\";\n\nexport const searchProducts = new Autonomous.Tool({\n name: \"searchProducts\",\n // This description is critical - it tells the AI when to use this tool\n description: \"Search the product catalog. Use when user asks about products, availability, pricing, or wants to browse items.\",\n\n input: z.object({\n query: z.string().describe(\"Search keywords\"),\n category: z.string().optional().describe(\"Filter by category\")\n }),\n output: z.object({\n products: z.array(z.object({ id: z.string(), name: z.string(), price: z.number() }))\n }),\n\n // Unlike actions, tools CAN destructure input directly in the handler\n handler: async ({ query, category }) => {\n // Your search logic here\n return { products: [] };\n }\n});\n```\n\n**Using ThinkSignal:** When a tool can't complete but you want to give the AI context:\n```typescript\nimport { Autonomous } from \"@botpress/runtime\";\n\n// Inside handler - AI will see this message and can respond appropriately\nthrow new Autonomous.ThinkSignal(\n \"No results found\",\n \"No products found matching that query. Ask user to try different search terms.\"\n);\n```\n\n**Advanced Tool Properties:**\n```typescript\nexport const myTool = new Autonomous.Tool({\n name: \"myTool\",\n description: \"Tool description\",\n input: z.object({...}),\n output: z.object({...}),\n aliases: [\"searchDocs\", \"findDocs\"], // Alternative names\n handler: async (input, ctx) => {\n console.log(`Call ID: ${ctx.callId}`); // Unique call identifier\n // ...\n },\n retry: async ({ attempt, error }) => {\n if (attempt < 3 && error?.code === 'RATE_LIMIT') {\n await new Promise(r => setTimeout(r, 1000 * attempt));\n return true; // Retry\n }\n return false; // Don't retry\n }\n});\n```\n\n**Docs:** https://www.botpress.com/docs/adk/concepts/tools\n**GitHub:** https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/tools.md\n\n---\n\n### 3. Conversations - Message Handlers\n\n**When to create a Conversation:**\n- Every bot needs at least one conversation handler to respond to users\n- Create separate handlers for different channels if they need different behavior\n- Use `channel: \"*\"` to handle all channels with one handler\n\n**Key decisions when building a conversation:**\n1. **Which channels?** - Specify `\"*\"` for all, or specific channels like `\"slack.dm\"`\n2. **What tools does the AI need?** - Pass them to `execute({ tools: [...] })`\n3. **What knowledge should ground responses?** - Pass to `execute({ knowledge: [...] })`\n4. **What instructions guide the AI?** - Define personality, rules, and context\n\n**The `execute()` function is the heart of ADK** - it runs autonomous AI logic with your tools and knowledge. Most conversation handlers will call `execute()`.\n\n**Location:** `src/conversations/*.ts`\n\n```typescript\nimport { Conversation, z } from \"@botpress/runtime\";\n\nexport const Chat = new Conversation({\n // Which channels this handler responds to\n channel: \"chat.channel\", // Or \"*\" for all, or [\"slack.dm\", \"webchat.channel\"]\n\n // Per-conversation state (optional)\n state: z.object({\n messageCount: z.number().default(0)\n }),\n\n async handler({ message, state, conversation, execute, user }) {\n state.messageCount += 1;\n\n // Handle commands\n if (message?.payload?.text?.startsWith(\"/help\")) {\n await conversation.send({\n type: \"text\",\n payload: { text: \"Available commands: /help, /status\" }\n });\n return;\n }\n\n // Let the AI handle the response with your tools and knowledge\n await execute({\n // Instructions guide the AI's behavior and personality\n instructions: `You are a helpful customer support agent for Acme Corp.\n User's name: ${user.state.name || \"there\"}\n User's tier: ${user.state.tier}\n Be friendly, concise, and always offer to help further.`,\n\n // Tools the AI can use during this conversation\n tools: [searchProducts, actions.createTicket.asTool()],\n\n // Knowledge bases for RAG - AI will search these to ground responses\n knowledge: [DocsKnowledgeBase],\n\n model: \"openai:gpt-4o\",\n temperature: 0.7,\n iterations: 10 // Max tool call iterations\n });\n }\n});\n```\n\n**Handler Context:**\n- `message` - User's message data\n- `execute` - Run autonomous AI logic\n- `conversation` - Conversation instance methods (send, startTyping, stopTyping)\n- `state` - Mutable state (bot, user, conversation)\n- `client` - Botpress API client\n- `type` - Event classification (message, workflow_request)\n\n**Execute Function Options:**\n```typescript\nawait execute({\n instructions: string | async function, // Required\n tools: Tool[], // AI-callable tools\n knowledge: Knowledge[], // Knowledge bases for RAG\n exits: Exit[], // Structured exit handlers\n model: string, // AI model to use\n temperature: number, // 0-1, default 0.7\n iterations: number, // Max tool calls, default 10\n hooks: {\n onBeforeTool: async ({ tool, input }) => { ... },\n onAfterTool: async ({ tool, output }) => { ... },\n onTrace: async (trace) => { ... }\n }\n});\n```\n\n**Common channels:** `chat.channel`, `webchat.channel`, `slack.dm`, `slack.channel`, `discord.channel`, `whatsapp.channel`, `\"*\"` (all)\n\n**Docs:** https://www.botpress.com/docs/adk/concepts/conversations\n**GitHub:** https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/conversations.md\n\n---\n\n### 4. Workflows - Background & Multi-Step Processes\n\n**When to create a Workflow:**\n- Operations that take longer than 2 minutes (the default timeout)\n- Multi-step processes that need to survive crashes/restarts\n- Scheduled/recurring tasks (daily reports, periodic syncs)\n- Background processing (order fulfillment, data migration)\n- Operations that need to wait for external events or user input\n\n**When NOT to use a Workflow (handle in conversation instead):**\n- Quick operations that complete immediately\n- Simple request-response patterns\n- Operations that don't need persistence\n\n**Key workflow concepts:**\n- **Steps are checkpoints** - If workflow crashes, it resumes from last completed step\n- **State persists** - Store progress in `state` to track across steps\n- **Always pass conversationId** - If the workflow needs to message users back\n\n**Location:** `src/workflows/*.ts`\n\n```typescript\nimport { Workflow, z } from \"@botpress/runtime\";\n\nexport const ProcessOrderWorkflow = new Workflow({\n name: \"processOrder\",\n description: \"Processes customer orders\",\n timeout: \"6h\", // Max duration\n schedule: \"0 9 * * *\", // Optional: run daily at 9am (cron syntax)\n\n input: z.object({\n orderId: z.string(),\n conversationId: z.string() // Include this to message the user back!\n }),\n\n state: z.object({\n currentStep: z.number().default(0),\n processedItems: z.array(z.string()).default([])\n }),\n\n output: z.object({\n success: z.boolean(),\n itemsProcessed: z.number()\n }),\n\n async handler({ input, state, step, client, execute }) {\n // State is passed as parameter, auto-tracked\n state.currentStep = 1;\n\n // IMPORTANT: Each step needs a unique, stable name (no dynamic names!)\n const orderData = await step(\"fetch-order\", async () => {\n return await fetchOrderData(input.orderId);\n });\n\n // Steps can have retry logic\n await step(\"process-payment\", async () => {\n return await processPayment(orderData);\n }, { maxAttempts: 3 });\n\n // To message the user from a workflow, use client.createMessage (NOT conversation.send)\n await step(\"notify-user\", async () => {\n await client.createMessage({\n conversationId: input.conversationId,\n type: \"text\",\n payload: { text: \"Your order has been processed!\" }\n });\n });\n\n return {\n success: true,\n itemsProcessed: state.processedItems.length\n };\n }\n});\n\n// Start a workflow from a conversation or trigger\nawait ProcessOrderWorkflow.start({\n orderId: \"123\",\n conversationId: conversation.id // Always pass this if you need to message back\n});\n\n// Get or create with deduplication\nconst instance = await ProcessOrderWorkflow.getOrCreate({\n key: `order-${orderId}`, // Prevents duplicate workflows\n input: { orderId, conversationId }\n});\n```\n\n**Step Methods:**\n\n| Method | Purpose |\n|--------|---------|\n| `step(name, fn)` | Basic execution with caching |\n| `step.sleep(name, ms)` | Pause for milliseconds |\n| `step.sleepUntil(name, date)` | Pause until specific date |\n| `step.listen()` | Wait for external events |\n| `step.progress(msg)` | Update progress message |\n| `step.request(name, prompt)` | Request user input (blocking) |\n| `step.executeWorkflow()` | Start and await another workflow |\n| `step.waitForWorkflow(id)` | Wait for existing workflow |\n| `step.map(items, fn)` | Process array with concurrency |\n| `step.forEach(items, fn)` | Execute on items without results |\n| `step.batch(items, fn)` | Process in groups |\n| `step.fail(reason)` | Mark workflow as failed |\n| `step.abort()` | Stop immediately without failure |\n\n**Critical Rules:**\n- Step names must be **unique** and **stable** (avoid dynamic naming in loops)\n- State is passed as a parameter, not accessed via `this.state`\n- Always pass `conversationId` for workflows that need to message users\n- Default timeout is 2 minutes - use steps for longer processes\n\n**Docs:** https://www.botpress.com/docs/adk/concepts/workflows/overview\n**GitHub:** https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/workflows.md\n\n---\n\n### 5. Tables - Data Storage\n\n**When to create a Table:**\n- You need to persist structured data (users, orders, tickets, logs)\n- You want to query/filter data by fields\n- You need semantic search on text content (set `searchable: true`)\n- You're storing data that should survive bot restarts\n\n**When NOT to use a Table (use State instead):**\n- Simple key-value data per user/conversation \u2192 use `user.state` or `conversation.state`\n- Temporary data that doesn't need persistence\n- Small amounts of data that fit in state\n\n**Tables vs Knowledge Bases:**\n- **Tables** = Structured data you CRUD (create, read, update, delete)\n- **Knowledge Bases** = Documents/content for AI to search and reference\n\n**Location:** `src/tables/*.ts`\n\n**CRITICAL RULES (violations will cause errors):**\n- Do NOT define an `id` column - it's created automatically as a number\n- Table names MUST end with \"Table\" (e.g., `OrdersTable`, not `Orders`)\n\n```typescript\nimport { Table, z } from \"@botpress/runtime\";\n\nexport const OrdersTable = new Table({\n name: \"OrdersTable\", // Must end with \"Table\"\n description: \"Stores order information\",\n columns: {\n // NO id column - it's automatic!\n orderId: z.string(),\n userId: z.string(),\n status: z.enum([\"pending\", \"completed\", \"cancelled\"]),\n total: z.number(),\n createdAt: z.date(),\n // Enable semantic search on a column:\n notes: {\n schema: z.string(),\n searchable: true\n }\n }\n});\n```\n\n**CRUD operations:**\n```typescript\n// Create - id is auto-assigned\nawait OrdersTable.createRows({\n rows: [{ orderId: \"ord-123\", userId: \"user-456\", status: \"pending\", total: 99.99, createdAt: new Date() }]\n});\n\n// Read with filters\nconst { rows } = await OrdersTable.findRows({\n filter: { userId: \"user-456\", status: \"pending\" },\n orderBy: \"createdAt\",\n orderDirection: \"desc\",\n limit: 10\n});\n\n// Get single row by id\nconst row = await OrdersTable.getRow({ id: 123 });\n\n// Semantic search (on searchable columns)\nconst { rows } = await OrdersTable.findRows({\n search: \"delivery issue\",\n limit: 5\n});\n\n// Update - must include the id\nawait OrdersTable.updateRows({\n rows: [{ id: 1, status: \"completed\" }]\n});\n\n// Upsert - insert or update based on key column\nawait OrdersTable.upsertRows({\n rows: [{ orderId: \"ord-123\", status: \"shipped\" }],\n keyColumn: \"orderId\"\n});\n\n// Delete by filter\nawait OrdersTable.deleteRows({ status: \"cancelled\" });\n\n// Delete by IDs\nawait OrdersTable.deleteRowIds([123, 456]);\n```\n\n**Advanced: Computed Columns:**\n```typescript\ncolumns: {\n basePrice: z.number(),\n taxRate: z.number(),\n fullPrice: {\n computed: true,\n schema: z.number(),\n dependencies: [\"basePrice\", \"taxRate\"],\n value: async (row) => row.basePrice * (1 + row.taxRate)\n }\n}\n```\n\n**Docs:** https://www.botpress.com/docs/adk/concepts/tables\n**GitHub:** https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/tables.md\n\n---\n\n### 6. Knowledge Bases - RAG for AI Context\n\n**When to create a Knowledge Base:**\n- You want the AI to answer questions based on your documentation\n- You have FAQs, policies, or product info the AI should reference\n- You want AI responses grounded in specific content (not hallucinated)\n- You're building a support bot that needs access to help articles\n\n**How RAG works in ADK:**\n1. You define knowledge sources (websites, files, tables)\n2. Content is indexed and embedded for semantic search\n3. During `execute()`, the AI automatically searches relevant knowledge\n4. AI uses retrieved content to generate grounded responses\n\n**Choosing a DataSource type:**\n- **Website** - Index public documentation, help sites, blogs\n- **Directory** - Index local markdown/text files (dev only!)\n- **Table** - Index structured data from your tables\n\n**Location:** `src/knowledge/*.ts`\n\n```typescript\nimport { Knowledge, DataSource } from \"@botpress/runtime\";\n\n// Website source - index via sitemap\nconst websiteSource = DataSource.Website.fromSitemap(\n \"https://docs.example.com/sitemap.xml\",\n {\n id: \"website-docs\",\n maxPages: 500,\n maxDepth: 10,\n filter: (ctx) => ctx.url.includes(\"/docs/\") // Only index /docs/ pages\n }\n);\n\n// Local files (development only - won't work in production)\nconst localSource = DataSource.Directory.fromPath(\"src/knowledge/docs\", {\n id: \"local-docs\",\n filter: (path) => path.endsWith(\".md\")\n});\n\n// Table-based knowledge\nconst tableSource = DataSource.Table.fromTable(FAQTable, {\n id: \"faq-table\",\n transform: ({ row }) => `Question: ${row.question}\\nAnswer: ${row.answer}`,\n filter: ({ row }) => row.published === true\n});\n\nexport const DocsKB = new Knowledge({\n name: \"docsKB\",\n description: \"Product documentation and help articles\",\n sources: [websiteSource, localSource, tableSource]\n});\n\n// Use in conversations - AI will search this knowledge base\nawait execute({\n instructions: \"Answer based on the documentation\",\n knowledge: [DocsKB]\n});\n\n// Manually refresh knowledge base\nawait DocsKB.refresh(); // Smart refresh (only changed content)\nawait DocsKB.refresh({ force: true }); // Force full re-index\nawait DocsKB.refreshSource(\"website-docs\", { force: true }); // Refresh specific source\n```\n\n**Website Source Methods:**\n- `fromSitemap(url, options)` - Parse XML sitemap\n- `fromWebsite(baseUrl, options)` - Crawl from base URL (requires Browser integration)\n- `fromLlmsTxt(url, options)` - Parse llms.txt file\n- `fromUrls(urls, options)` - Index specific URLs\n\n**Docs:** https://www.botpress.com/docs/adk/concepts/knowledge\n**GitHub:** https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/knowledge-bases.md\n\n---\n\n### 7. Triggers - Event-Driven Automation\n\n**When to create a Trigger:**\n- You need to react to events automatically (user signs up, issue created, etc.)\n- You want to start workflows when specific events occur\n- You need to sync data when external systems change\n- You want to send notifications based on events\n\n**Common trigger patterns:**\n- **User onboarding** - Trigger on `user.created` \u2192 start onboarding workflow\n- **Integration sync** - Trigger on `linear:issueCreated` \u2192 create record in table\n- **Notifications** - Trigger on `workflow.completed` \u2192 send Slack message\n\n**Finding available events:**\n- Bot events: `user.created`, `conversation.started`, `workflow.completed`, etc.\n- Integration events: Run `adk info <integration> --events` to see available events\n\n**Location:** `src/triggers/*.ts`\n\n```typescript\nimport { Trigger } from \"@botpress/runtime\";\n\nexport default new Trigger({\n name: \"onNewUser\",\n description: \"Start onboarding when user created\",\n events: [\"user.created\"], // Can listen to multiple events\n\n handler: async ({ event, client, actions }) => {\n const { userId, email } = event.payload;\n\n // Start an onboarding workflow\n await OnboardingWorkflow.start({\n userId,\n email\n });\n }\n});\n\n// Integration events use format: integration:eventName\nexport const LinearTrigger = new Trigger({\n name: \"onLinearIssue\",\n description: \"Handle Linear issue events\",\n events: [\"linear:issueCreated\", \"linear:issueUpdated\"],\n\n handler: async ({ event, actions }) => {\n if (event.type === \"linear:issueCreated\") {\n await actions.slack.sendMessage({\n channel: \"#notifications\",\n text: `New issue: ${event.payload.title}`\n });\n }\n }\n});\n```\n\n**Common Bot Events:**\n- `user.created`, `user.updated`, `user.deleted`\n- `conversation.started`, `conversation.ended`, `message.created`\n- `workflow.started`, `workflow.completed`, `workflow.failed`\n- `bot.started`, `bot.stopped`\n\n**Common Integration Events:**\n- Slack: `slack:reactionAdded`, `slack:memberJoinedChannel`\n- Linear: `linear:issueCreated`, `linear:issueUpdated`\n- GitHub: `github:issueOpened`, `github:pullRequestOpened`\n- Intercom: `intercom:conversationEvent`, `intercom:contactEvent`\n\n**Find integration events:** Run `adk info <integration> --events`\n\n**Docs:** https://www.botpress.com/docs/adk/concepts/triggers\n**GitHub:** https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/triggers.md\n\n---\n\n## Sending Messages\n\n**CRITICAL: The method depends on WHERE you're sending from:**\n\n| Context | Method | Why |\n|---------|--------|-----|\n| In Conversations | `conversation.send()` | Has conversation context |\n| In Workflows/Actions | `client.createMessage()` | Needs explicit `conversationId` |\n\n**Common mistake:** Using `client.createMessage()` in conversations. Always use `conversation.send()` instead.\n\nThe method depends on where you're sending from:\n\n**In conversations** - Use `conversation.send()`:\n```typescript\nawait conversation.send({ type: \"text\", payload: { text: \"Hello!\" } });\nawait conversation.send({ type: \"image\", payload: { imageUrl: \"https://...\" } });\nawait conversation.send({\n type: \"choice\",\n payload: {\n text: \"Pick one:\",\n choices: [\n { title: \"Option A\", value: \"a\" },\n { title: \"Option B\", value: \"b\" }\n ]\n }\n});\n```\n\n**In workflows or actions** - Use `client.createMessage()` with `conversationId`:\n```typescript\nawait client.createMessage({\n conversationId: input.conversationId, // Must have this!\n type: \"text\",\n payload: { text: \"Workflow complete!\" }\n});\n```\n\n**All Message Types:**\n```typescript\n// Text\n{ type: \"text\", payload: { text: \"Hello!\" } }\n\n// Markdown\n{ type: \"markdown\", payload: { text: \"# Heading\\n**Bold**\" } }\n\n// Image\n{ type: \"image\", payload: { imageUrl: \"https://...\" } }\n\n// Audio\n{ type: \"audio\", payload: { audioUrl: \"https://...\" } }\n\n// Video\n{ type: \"video\", payload: { videoUrl: \"https://...\" } }\n\n// File\n{ type: \"file\", payload: { fileUrl: \"https://...\", title: \"Document.pdf\" } }\n\n// Location\n{ type: \"location\", payload: { latitude: 40.7128, longitude: -74.0060, address: \"New York, NY\" } }\n\n// Card\n{ type: \"card\", payload: {\n title: \"Product Name\",\n subtitle: \"Description\",\n imageUrl: \"https://...\",\n actions: [\n { action: \"url\", label: \"View\", value: \"https://...\" },\n { action: \"postback\", label: \"Buy\", value: \"buy_123\" }\n ]\n}}\n\n// Carousel\n{ type: \"carousel\", payload: {\n items: [\n { title: \"Item 1\", subtitle: \"...\", imageUrl: \"...\", actions: [...] },\n { title: \"Item 2\", subtitle: \"...\", imageUrl: \"...\", actions: [...] }\n ]\n}}\n\n// Choice (Quick Replies)\n{ type: \"choice\", payload: {\n text: \"Select an option:\",\n choices: [\n { title: \"Option 1\", value: \"opt1\" },\n { title: \"Option 2\", value: \"opt2\" }\n ]\n}}\n\n// Dropdown\n{ type: \"dropdown\", payload: {\n text: \"Select country:\",\n options: [\n { label: \"United States\", value: \"us\" },\n { label: \"Canada\", value: \"ca\" }\n ]\n}}\n```\n\n**GitHub:** https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/messages.md\n\n---\n\n## Zai - LLM Utility Operations\n\n**When to use Zai vs execute():**\n- **Use `zai`** for specific, structured AI operations (extract data, classify, summarize)\n- **Use `execute()`** for autonomous, multi-turn AI conversations with tools\n\n**Zai is perfect for:**\n- Extracting structured data from user messages (`zai.extract`)\n- Classifying/labeling content (`zai.check`, `zai.label`)\n- Summarizing long content (`zai.summarize`)\n- Answering questions from documents (`zai.answer`)\n- Sorting/filtering/grouping data intelligently (`zai.sort`, `zai.filter`, `zai.group`)\n\n**Zai operations are optimized for speed and cost** - they use the `zai` model configured in `agent.config.ts` (typically a faster/cheaper model).\n\n```typescript\nimport { adk, z } from \"@botpress/runtime\";\n\n// Extract structured data from text\nconst contact = await adk.zai.extract(\n \"Contact John at john@example.com, phone 555-0100\",\n z.object({\n name: z.string(),\n email: z.string(),\n phone: z.string()\n })\n);\n// Returns: { name: \"John\", email: \"john@example.com\", phone: \"555-0100\" }\n\n// Check if text matches a condition (returns boolean)\nconst isSpam = await adk.zai.check(messageText, \"is spam or promotional\");\n\n// Label text with multiple criteria\nconst labels = await adk.zai.label(customerEmail, {\n spam: \"is spam\",\n urgent: \"needs immediate response\",\n complaint: \"expresses dissatisfaction\"\n});\n// Returns: { spam: false, urgent: true, complaint: true }\n\n// Summarize content\nconst summary = await adk.zai.summarize(longDocument, {\n length: 200,\n bulletPoints: true\n});\n\n// Answer questions from documents (with citations)\nconst result = await adk.zai.answer(docs, \"What is the refund policy?\");\nif (result.type === \"answer\") {\n console.log(result.answer);\n console.log(result.citations);\n}\n// Response types: \"answer\", \"ambiguous\", \"out_of_topic\", \"invalid_question\", \"missing_knowledge\"\n\n// Rate items on 1-5 scale\nconst scores = await adk.zai.rate(products, \"quality score\");\n\n// Sort by criteria\nconst sorted = await adk.zai.sort(tickets, \"by urgency, most urgent first\");\n\n// Group items semantically\nconst groups = await adk.zai.group(emails, {\n instructions: \"categorize by topic\"\n});\n\n// Rewrite text\nconst professional = await adk.zai.rewrite(\"hey wassup\", \"make it professional and friendly\");\n\n// Filter arrays\nconst activeUsers = await adk.zai.filter(users, \"have been active this month\");\n\n// Generate text\nconst blogPost = await adk.zai.text(\"Write about AI in healthcare\", {\n length: 1000,\n temperature: 0.7\n});\n\n// Patch code files\nconst patched = await adk.zai.patch(files, \"add JSDoc comments to all functions\");\n```\n\n**Zai Configuration:**\n```typescript\n// Create configured instance\nconst preciseZai = adk.zai.with({\n modelId: \"best\", // \"best\" | \"fast\" | custom model ID\n temperature: 0.1\n});\n\n// Enable active learning\nconst learningZai = adk.zai.learn(\"sentiment-analysis\");\n```\n\n**Docs:** https://www.botpress.com/docs/adk/zai/overview\n**GitHub:** https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/zai-complete-guide.md\n\n---\n\n## Integrations\n\n**When to add an Integration:**\n- You need to connect to an external service (Slack, Linear, GitHub, etc.)\n- You want to receive messages from a channel (webchat, WhatsApp, Discord)\n- You need to call external APIs with pre-built actions\n- You want to react to events from external systems\n\n**Integration workflow:**\n1. **Search** - Find integrations with `adk search <name>`\n2. **Add** - Install with `adk add <name>@<version>`\n3. **Configure** - Set up credentials in the UI at `http://localhost:3001/`\n4. **Use** - Call actions via `actions.<integration>.<action>()`\n\n**Making integration actions available to AI:**\n```typescript\n// Convert any integration action to an AI-callable tool\ntools: [actions.slack.sendMessage.asTool()]\n```\n\n**CLI commands:**\n```bash\nadk search slack # Find integrations\nadk add slack@latest # Add to project\nadk add slack --alias my-slack # Add with custom alias\nadk info slack --events # See available events\nadk list # List installed integrations\nadk upgrade slack # Update to latest\nadk remove slack # Remove integration\n```\n\n**Using integration actions:**\n```typescript\nimport { actions } from \"@botpress/runtime\";\n\n// Slack\nawait actions.slack.sendMessage({ channel: \"#general\", text: \"Hello!\" });\nawait actions.slack.addReaction({ channel: \"C123\", timestamp: \"123\", name: \"thumbsup\" });\n\n// Linear\nawait actions.linear.issueCreate({ teamId: \"123\", title: \"Bug report\", description: \"Details\" });\nconst { items } = await actions.linear.issueList({\n first: 10,\n filter: { state: { name: { eq: \"In Progress\" } } }\n});\n\n// GitHub\nawait actions.github.createIssue({ owner: \"org\", repo: \"repo\", title: \"Issue\" });\n\n// Browser (web scraping)\nconst results = await actions.browser.webSearch({ query: \"Botpress docs\", maxResults: 5 });\n\n// Make integration actions available to AI as tools\nawait execute({ tools: [actions.slack.sendMessage.asTool()] });\n```\n\n**Docs:** https://www.botpress.com/docs/adk/managing-integrations\n**GitHub:** https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/integration-actions.md\n\n---\n\n## State Management\n\n**Understanding the state hierarchy - choose the right level:**\n\n| State Level | Scope | Use For |\n|-------------|-------|---------|\n| `bot.state` | Global, all users | Feature flags, counters, maintenance mode |\n| `user.state` | Per user, all their conversations | User preferences, profile, tier |\n| `conversation.state` | Per conversation | Context, message count, active workflow |\n| `workflow.state` | Per workflow instance | Progress tracking, intermediate results |\n\n**State is automatically persisted** - just modify it and it saves.\n\nAccess and modify state from anywhere in your bot:\n\n```typescript\nimport { bot, user, conversation } from \"@botpress/runtime\";\n\n// Bot state - global, shared across all users\nbot.state.maintenanceMode = true;\nbot.state.totalConversations += 1;\n\n// User state - per user, persists across conversations\nuser.state.name = \"Alice\";\nuser.state.tier = \"pro\";\nuser.state.preferredLanguage = \"es\";\n\n// In handlers, state is passed as a parameter\nasync handler({ state }) {\n state.messageCount += 1; // Auto-persisted\n}\n\n// Tags - simple string key-value pairs for categorization\nuser.tags.source = \"website\";\nuser.tags.region = \"north-america\";\nconversation.tags.category = \"support\";\nconversation.tags.priority = \"high\";\n```\n\n**State Types:**\n- **Bot State** - Global, shared across all users and conversations\n- **User State** - Per-user, persists across all their conversations\n- **Conversation State** - Per-conversation, isolated between conversations\n- **Workflow State** - Per-workflow instance, persists across steps\n\n**Tags vs State:**\n- Use **Tags** for: categorization, simple strings, filtering/querying\n- Use **State** for: complex objects, arrays, nested data, business logic\n\n**GitHub:** https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/tags.md\n\n---\n\n## Context API\n\nAccess runtime services in any handler:\n\n```typescript\nimport { context } from \"@botpress/runtime\";\n\n// Always available\nconst client = context.get(\"client\"); // Botpress API client\nconst citations = context.get(\"citations\"); // Citation manager\nconst cognitive = context.get(\"cognitive\"); // LLM client\nconst logger = context.get(\"logger\"); // Structured logger\nconst botId = context.get(\"botId\"); // Current bot ID\nconst configuration = context.get(\"configuration\"); // Bot config\n\n// Conditionally available (use { optional: true })\nconst user = context.get(\"user\", { optional: true });\nconst conversation = context.get(\"conversation\", { optional: true });\nconst message = context.get(\"message\", { optional: true });\nconst workflow = context.get(\"workflow\", { optional: true });\nconst chat = context.get(\"chat\", { optional: true }); // Conversation transcript\n\nif (user) {\n console.log(`User: ${user.id}`);\n}\n```\n\n**GitHub:** https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/context-api.md\n\n---\n\n## CLI Quick Reference\n\n```bash\n# Project Lifecycle\nadk init <name> # Create new project\nadk login # Authenticate with Botpress\nadk dev # Start dev server (hot reload)\nadk dev --port 3000 # Custom port\nadk chat # Test in CLI\nadk build # Build for production\nadk deploy # Deploy to Botpress Cloud\nadk deploy --env production # Deploy to specific environment\n\n# Integration Management\nadk add <integration> # Add integration\nadk add slack@2.5.5 # Add specific version\nadk add slack --alias my-slack # Add with alias\nadk remove <integration> # Remove integration\nadk search <query> # Search integrations\nadk list # List installed integrations\nadk list --available # List all available\nadk info <name> # Integration details\nadk info <name> --events # Show available events\nadk upgrade <name> # Update integration\nadk upgrade # Interactive upgrade all\n\n# Knowledge & Assets\nadk kb sync --dev # Sync knowledge bases\nadk kb sync --prod --force # Force re-sync production\nadk assets sync # Sync static files\n\n# Advanced\nadk run <script.ts> # Run TypeScript script\nadk mcp # Start MCP server\nadk link --workspace ws_123 --bot bot_456 # Link to existing bot\n\n# Utilities\nadk self-upgrade # Update CLI\nadk telemetry --disable # Disable telemetry\nadk --help # Full CLI help\nadk <command> --help # Help for specific command\n```\n\n**Docs:** https://www.botpress.com/docs/adk/cli-reference\n**GitHub:** https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/cli.md\n\n---\n\n## Autonomous Execution with `execute()`\n\n**The `execute()` function is the core of ADK's AI capabilities.** It runs an autonomous AI agent that can:\n- Understand user intent from natural language\n- Decide which tools to call and when\n- Search knowledge bases for relevant information\n- Generate contextual responses\n- Loop through multiple tool calls until the task is complete\n\n**When to use execute():**\n- In conversation handlers to generate AI responses\n- In workflows when you need AI decision-making\n- Anywhere you want autonomous, multi-step AI behavior\n\n**Key parameters to configure:**\n- `instructions` - Tell the AI who it is and how to behave\n- `tools` - Give the AI capabilities (search, create, update, etc.)\n- `knowledge` - Ground the AI in your documentation\n- `exits` - Define structured output schemas for specific outcomes\n\nThe `execute()` function enables autonomous AI agent behavior:\n\n```typescript\nimport { Autonomous, z } from \"@botpress/runtime\";\n\n// Define custom tool\nconst searchTool = new Autonomous.Tool({\n name: \"search\",\n description: \"Search documentation\",\n input: z.object({ query: z.string() }),\n output: z.string(),\n handler: async ({ query }) => {\n // Search implementation\n return \"results...\";\n }\n});\n\n// Define exit (structured response)\nconst AnswerExit = new Autonomous.Exit({\n name: \"Answer\",\n description: \"Provide final answer to the user\",\n schema: z.object({\n answer: z.string(),\n confidence: z.number(),\n sources: z.array(z.string())\n })\n});\n\n// Execute AI with tools, knowledge, and exits\nconst result = await execute({\n instructions: \"Help the user with their request. Be helpful and concise.\",\n\n // Add tools\n tools: [\n searchTool,\n actions.linear.issueCreate.asTool()\n ],\n\n // Add knowledge bases\n knowledge: [DocsKnowledgeBase, FAQKnowledgeBase],\n\n // Define exits for structured outputs\n exits: [AnswerExit],\n\n // Model configuration\n model: \"openai:gpt-4o\",\n temperature: 0.7,\n iterations: 10, // Max tool call iterations\n\n // Hooks for monitoring\n hooks: {\n onBeforeTool: async ({ tool, input }) => {\n console.log(`Calling ${tool.name}`, input);\n return { input: { ...input, enhanced: true } }; // Modify input\n },\n onAfterTool: async ({ tool, output }) => {\n console.log(`Result:`, output);\n }\n }\n});\n\n// Handle structured exit\nif (result.is(AnswerExit)) {\n console.log(result.output.answer);\n console.log(result.output.sources);\n}\n```\n\n---\n\n## Troubleshooting\n\n| Error | Cause | Solution |\n|-------|-------|----------|\n| \"Cannot destructure property\" in Actions | Destructuring input directly in handler params | Use `async handler({ input, client })` then `const { field } = input` inside |\n| Table creation fails | Invalid table name or `id` defined | Remove `id` column, ensure name ends with \"Table\" |\n| Integration action not found | Integration not installed or configured | Run `adk list`, add with `adk add`, configure in UI at localhost:3001 |\n| Knowledge base not updating | KB not synced | Run `adk kb sync --dev` or `adk kb sync --force` |\n| Workflow not resuming | Dynamic step names | Use stable, unique step names (no `step(\\`item-${i}\\`)`) |\n| Types out of date | Generated types stale | Run `adk dev` or `adk build` to regenerate |\n| Can't message user from workflow | Missing conversationId | Pass `conversationId` when starting workflow, use `client.createMessage()` |\n| \"user is not defined\" | Accessing conversation context outside conversation | Use `context.get(\"user\", { optional: true })` |\n| State changes not persisting | Creating new objects instead of modifying | Modify state directly: `state.user.name = \"Alice\"` |\n| Tool not being used by AI | Poor description | Improve tool description, add detailed `.describe()` to inputs |\n\n**For more help:** Run `adk --help` or check:\n- **Docs:** https://www.botpress.com/docs/adk/\n- **GitHub:** https://github.com/botpress/skills/tree/master/skills/adk/references\n\n---\n\n## Common Patterns & Best Practices\n\n### 1. Always Pass conversationId for Workflows\n```typescript\n// In conversation - starting a workflow that needs to message back\nawait MyWorkflow.start({\n conversationId: conversation.id, // Always include this!\n data: \"...\"\n});\n\n// In workflow - messaging back to user\nawait client.createMessage({\n conversationId: input.conversationId,\n type: \"text\",\n payload: { text: \"Processing complete!\" }\n});\n```\n\n### 2. Use Environment Variables for Secrets\n```typescript\n// In .env (never commit!)\nAPI_KEY=sk-...\nSLACK_TOKEN=xoxb-...\n\n// In code\nconfig: { apiKey: process.env.API_KEY }\n```\n\n### 3. Keep Step Names Stable in Workflows\n```typescript\n// GOOD - Single step for batch\nawait step(\"process-all-items\", async () => {\n for (const item of items) {\n await processItem(item);\n }\n});\n\n// BAD - Dynamic names break resume\nfor (let i = 0; i < items.length; i++) {\n await step(`process-${i}`, async () => { ... }); // Don't do this!\n}\n```\n\n### 4. Error Handling in Actions/Tools\n```typescript\nexport default new Action({\n handler: async ({ input }) => {\n try {\n // Action logic\n return { success: true };\n } catch (error) {\n console.error(\"Action failed:\", error);\n throw new Error(`Failed to process: ${error.message}`);\n }\n }\n});\n```\n\n### 5. ThinkSignal for Tool Edge Cases\n```typescript\nhandler: async ({ query }) => {\n const results = await search(query);\n\n if (!results.length) {\n throw new Autonomous.ThinkSignal(\n \"No results\",\n \"No results found. Ask the user to try different search terms.\"\n );\n }\n\n return results;\n}\n```\n\n### 6. Multi-Channel Handling\n```typescript\nexport default new Conversation({\n channels: [\"slack.channel\", \"webchat.channel\"],\n handler: async ({ conversation }) => {\n const channel = conversation.channel;\n\n if (channel === \"slack.channel\") {\n // Slack-specific handling (threading, mentions, etc.)\n } else if (channel === \"webchat.channel\") {\n // Webchat-specific handling\n }\n }\n});\n```\n\n---\n\n## Complete Reference Documentation\n\n### Official Botpress ADK Documentation\n**Base URL:** https://www.botpress.com/docs/adk/\n\n| Topic | URL |\n|-------|-----|\n| Introduction | https://www.botpress.com/docs/adk/introduction |\n| Quickstart | https://www.botpress.com/docs/adk/quickstart |\n| Project Structure | https://www.botpress.com/docs/adk/project-structure |\n| Actions | https://www.botpress.com/docs/adk/concepts/actions |\n| Tools | https://www.botpress.com/docs/adk/concepts/tools |\n| Conversations | https://www.botpress.com/docs/adk/concepts/conversations |\n| Workflows Overview | https://www.botpress.com/docs/adk/concepts/workflows/overview |\n| Workflow Steps | https://www.botpress.com/docs/adk/concepts/workflows/steps |\n| Tables | https://www.botpress.com/docs/adk/concepts/tables |\n| Triggers | https://www.botpress.com/docs/adk/concepts/triggers |\n| Knowledge Bases | https://www.botpress.com/docs/adk/concepts/knowledge |\n| Managing Integrations | https://www.botpress.com/docs/adk/managing-integrations |\n| Zai Overview | https://www.botpress.com/docs/adk/zai/overview |\n| Zai Reference | https://www.botpress.com/docs/adk/zai/reference |\n| CLI Reference | https://www.botpress.com/docs/adk/cli-reference |\n\n### GitHub Repository References (AI-Optimized)\n**Base URL:** https://github.com/botpress/skills/tree/master/skills/adk/references\n\nFor detailed specifications beyond this guide, fetch the corresponding reference file:\n\n| Topic | Reference File |\n|-------|----------------|\n| Actions | https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/actions.md |\n| Tools | https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/tools.md |\n| Workflows | https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/workflows.md |\n| Conversations | https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/conversations.md |\n| Tables | https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/tables.md |\n| Triggers | https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/triggers.md |\n| Knowledge Bases | https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/knowledge-bases.md |\n| Messages | https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/messages.md |\n| Agent Config | https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/agent-config.md |\n| CLI | https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/cli.md |\n| Integration Actions | https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/integration-actions.md |\n| Model Configuration | https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/model-configuration.md |\n| Context API | https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/context-api.md |\n| Tags | https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/tags.md |\n| Files | https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/files.md |\n| Zai Complete Guide | https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/zai-complete-guide.md |\n| Zai Agent Reference | https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/zai-agent-reference.md |\n| MCP Server | https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/mcp-server.md |\n\n---\n\n## Common Scenarios - What to Build\n\n**\"I want to build a support bot that answers questions from our docs\"**\n1. Create a Knowledge Base with your documentation as a source\n2. Create a Conversation handler that uses `execute()` with that knowledge\n3. Add the `chat` integration for testing\n\n**\"I want the bot to create tickets in Linear when users report issues\"**\n1. Add the Linear integration: `adk add linear`\n2. Create a Tool that calls `actions.linear.issueCreate()`\n3. Pass the tool to `execute()` in your conversation\n\n**\"I need to run a daily sync job\"**\n1. Create a Workflow with `schedule: \"0 9 * * *\"` (cron syntax)\n2. Implement the sync logic in steps\n3. The workflow will run automatically at the scheduled time\n\n**\"I want to store user preferences\"**\n1. Define the schema in `agent.config.ts` under `user.state`\n2. Access/modify via `user.state.preferenceField = value`\n3. State persists automatically\n\n**\"I need to react when a new user signs up\"**\n1. Create a Trigger listening to `user.created` event\n2. In the handler, start an onboarding workflow or send a welcome message\n\n**\"I want to store order data and search it\"**\n1. Create a Table with your schema (remember: no `id` field, name ends with \"Table\")\n2. Use `searchable: true` on text columns you want to search\n3. Use CRUD methods: `createRows`, `findRows`, `updateRows`, `deleteRows`\n\n---\n\n## Summary\n\nThis skill provides comprehensive guidance for building Botpress bots using the ADK:\n\n- **Setup & Initialization** - ADK installation and project creation\n- **Project Structure** - Conventions, files, and organization\n- **Core Concepts** - Actions, Tools, Workflows, Conversations, Tables, Knowledge, Triggers\n- **State Management** - Bot, user, conversation, and workflow state\n- **Integration Management** - Adding and configuring integrations\n- **Zai (AI Operations)** - Extract, check, label, summarize, answer, sort, group, rewrite, filter\n- **CLI Reference** - Complete command guide\n- **Testing & Deployment** - Local testing and cloud deployment\n- **Common Patterns** - Best practices and troubleshooting\n\n**Core Principle:** The ADK is a convention-based framework where file location determines behavior. Place components in the correct `src/` subdirectory and they automatically become bot capabilities.\n\n**When to use this skill:**\n- User wants to create a new Botpress bot\n- User asks how to add actions, tools, workflows, conversations, tables, knowledge bases, or triggers\n- User needs help with integrations (Slack, Linear, GitHub, etc.)\n- User wants to understand ADK patterns and best practices\n- User has errors or needs troubleshooting\n- User asks about CLI commands, configuration, or deployment\n\n**Official Documentation:** https://www.botpress.com/docs/adk/\n**GitHub Repository:** https://github.com/botpress/adk\n**Skills Repository:** https://github.com/botpress/skills\n",49 "arguments": [],50 "icons": null,51 "meta": null52 },53 {54 "name": "README.md",55 "title": null,56 "description": "# Botpress ADK Skill\n\nA comprehensive skill for building AI bots with the Botpress Agent Development Kit (ADK).\n\n## What's Inside\n\n- `SKILL.md` - Complete ADK development guide with code examples and patterns\n\n## Quick Summary\n\nThis skill provides guidance for the entire Botpress ADK development lifecycle:\n\n- **Project Setup** - `adk init`, `adk login`, `adk link`\n- **Development** - `adk dev`, `adk chat`, hot reloading\n- **Core Concepts** - Actions, Tools, Workflows, Conversations, Tables, Triggers, Knowledge Bases\n- **Deployment** - `adk deploy`, production configuration\n- **Troubleshooting** - Common errors and fixes\n\n## Usage\n\nLoad this skill when working with Botpress ADK projects. It will help with:\n\n- Creating new bots from scratch\n- Adding features (actions, tools, workflows, etc.)\n- Configuring integrations (Slack, Linear, GitHub, etc.)\n- Debugging deployment issues\n- Understanding ADK conventions and patterns\n\n## Requirements\n\n- Node.js v22.0.0+\n- Botpress Account (https://app.botpress.cloud)\n- ADK CLI installed\n\n## Links\n\n- [Botpress ADK Documentation](https://www.botpress.com/docs/adk/)\n- [ADK GitHub Repository](https://github.com/botpress/adk)\n- [Botpress Skills Repository](https://github.com/botpress/skills)\n\n## License\n\nMIT License\n",57 "arguments": null,58 "icons": null,59 "meta": null60 }61 ],62 "resources": [63 {64 "name": "_meta.json",65 "title": null,66 "uri": "skill://_meta.json",67 "description": "{\n \"owner\": \"yueranlu\",\n \"slug\": \"botpress-adk\",\n \"displayName\": \"Create a Botpress AI agent with the ADK using OpenClaw\",\n \"latest\": {\n \"version\": \"1.0.1\",\n \"publishedAt\": 1770066884691,\n \"commit\": \"https://github.com/clawdbot/skills/commit/ea08470057cf35c0449adfb6a51727e6563687b3\"\n },\n \"history\": [\n {\n \"version\": \"1.0.0\",\n \"publishedAt\": 1769807188387,\n \"commit\": \"https://github.com/clawdbot/skills/commit/8eb9ea0e7efe0cb691bb5959c798073cc28af398\"\n }\n ]\n}\n",68 "mimeType": null,69 "size": null,70 "icons": null,71 "annotations": null,72 "meta": null73 }74 ],75 "resource_templates": [],76 "tools": []77 },78 "error": null79 }80 ],81 "issues": [82 {83 "code": "W004",84 "message": "The MCP server is not in our registry.",85 "reference": [86 0,87 null88 ],89 "extra_data": null90 }91 ],92 "labels": [93 [94 {95 "is_public_sink": 0,96 "destructive": 0,97 "untrusted_content": 0,98 "private_data": 099 },100 {101 "is_public_sink": 0,102 "destructive": 0,103 "untrusted_content": 0,104 "private_data": 0105 },106 {107 "is_public_sink": 0,108 "destructive": 0,109 "untrusted_content": 0,110 "private_data": 0111 }112 ]113 ],114 "error": null115 }116}117118Process exited with code 0119✓ Completed in 104869ms
npm-audit▸No package.json found — skipping npm audit
View logs
1No package.json found at /tmp/clawguard-scan-DpTogr/repo/skills/yueranlu/botpress-adk/package.json2Skipping npm audit.
Files analyzed
Rules coverage147 patterns
Security Findings
Remote code execution via pipe-to-shell pattern
/tmp/clawguard-scan-DpTogr/repo/skills/yueranlu/botpress-adk/SKILL.md:87
Evidence: curl -fsSL https://github.com/botpress/adk/releases/latest/download/install.sh | bash
Possible base64-encoded payload
/tmp/clawguard-scan-DpTogr/repo/skills/yueranlu/botpress-adk/SKILL.md:71
Evidence: com/botpress/skills/tree/master/skills/adk
Possible base64-encoded payload
/tmp/clawguard-scan-DpTogr/repo/skills/yueranlu/botpress-adk/SKILL.md:87
Evidence: com/botpress/adk/releases/latest/download/install
Possible base64-encoded payload
/tmp/clawguard-scan-DpTogr/repo/skills/yueranlu/botpress-adk/SKILL.md:92
Evidence: com/botpress/adk/releases/latest/download/install
Possible base64-encoded payload
/tmp/clawguard-scan-DpTogr/repo/skills/yueranlu/botpress-adk/SKILL.md:103
Evidence: com/botpress/skills/master/skills/adk/references/cli
Possible base64-encoded payload
/tmp/clawguard-scan-DpTogr/repo/skills/yueranlu/botpress-adk/SKILL.md:125
Evidence: com/botpress/skills/master/skills/adk/references/cli
Possible base64-encoded payload
/tmp/clawguard-scan-DpTogr/repo/skills/yueranlu/botpress-adk/SKILL.md:179
Evidence: com/botpress/skills/master/skills/adk/references/cli
Possible base64-encoded payload
/tmp/clawguard-scan-DpTogr/repo/skills/yueranlu/botpress-adk/SKILL.md:216
Evidence: com/botpress/skills/master/skills/adk/references/agent
Possible base64-encoded payload
/tmp/clawguard-scan-DpTogr/repo/skills/yueranlu/botpress-adk/SKILL.md:285
Evidence: com/botpress/skills/master/skills/adk/references/agent
Possible base64-encoded payload
/tmp/clawguard-scan-DpTogr/repo/skills/yueranlu/botpress-adk/SKILL.md:342
Evidence: com/botpress/skills/master/skills/adk/references/actions
Possible base64-encoded payload
/tmp/clawguard-scan-DpTogr/repo/skills/yueranlu/botpress-adk/SKILL.md:421
Evidence: com/botpress/skills/master/skills/adk/references/tools
Possible base64-encoded payload
/tmp/clawguard-scan-DpTogr/repo/skills/yueranlu/botpress-adk/SKILL.md:517
Evidence: com/botpress/skills/master/skills/adk/references/conversations
Possible base64-encoded payload
/tmp/clawguard-scan-DpTogr/repo/skills/yueranlu/botpress-adk/SKILL.md:633
Evidence: com/docs/adk/concepts/workflows/overview
Possible base64-encoded payload
/tmp/clawguard-scan-DpTogr/repo/skills/yueranlu/botpress-adk/SKILL.md:634
Evidence: com/botpress/skills/master/skills/adk/references/workflows
Possible base64-encoded payload
/tmp/clawguard-scan-DpTogr/repo/skills/yueranlu/botpress-adk/SKILL.md:740
Evidence: com/botpress/skills/master/skills/adk/references/tables
Possible base64-encoded payload
/tmp/clawguard-scan-DpTogr/repo/skills/yueranlu/botpress-adk/SKILL.md:817
Evidence: com/botpress/skills/master/skills/adk/references/knowledge
Possible base64-encoded payload
/tmp/clawguard-scan-DpTogr/repo/skills/yueranlu/botpress-adk/SKILL.md:891
Evidence: com/botpress/skills/master/skills/adk/references/triggers
Possible base64-encoded payload
/tmp/clawguard-scan-DpTogr/repo/skills/yueranlu/botpress-adk/SKILL.md:994
Evidence: com/botpress/skills/master/skills/adk/references/messages
Possible base64-encoded payload
/tmp/clawguard-scan-DpTogr/repo/skills/yueranlu/botpress-adk/SKILL.md:1092
Evidence: com/botpress/skills/master/skills/adk/references/zai
Possible base64-encoded payload
/tmp/clawguard-scan-DpTogr/repo/skills/yueranlu/botpress-adk/SKILL.md:1153
Evidence: com/botpress/skills/master/skills/adk/references/integration
Possible base64-encoded payload
/tmp/clawguard-scan-DpTogr/repo/skills/yueranlu/botpress-adk/SKILL.md:1206
Evidence: com/botpress/skills/master/skills/adk/references/tags
Possible base64-encoded payload
/tmp/clawguard-scan-DpTogr/repo/skills/yueranlu/botpress-adk/SKILL.md:1237
Evidence: com/botpress/skills/master/skills/adk/references/context
Possible base64-encoded payload
/tmp/clawguard-scan-DpTogr/repo/skills/yueranlu/botpress-adk/SKILL.md:1285
Evidence: com/botpress/skills/master/skills/adk/references/cli
Possible base64-encoded payload
/tmp/clawguard-scan-DpTogr/repo/skills/yueranlu/botpress-adk/SKILL.md:1396
Evidence: com/botpress/skills/tree/master/skills/adk/references
Possible base64-encoded payload
/tmp/clawguard-scan-DpTogr/repo/skills/yueranlu/botpress-adk/SKILL.md:1505
Evidence: com/docs/adk/concepts/workflows/overview
Possible base64-encoded payload
/tmp/clawguard-scan-DpTogr/repo/skills/yueranlu/botpress-adk/SKILL.md:1516
Evidence: com/botpress/skills/tree/master/skills/adk/references
Possible base64-encoded payload
/tmp/clawguard-scan-DpTogr/repo/skills/yueranlu/botpress-adk/SKILL.md:1522
Evidence: com/botpress/skills/master/skills/adk/references/actions
Possible base64-encoded payload
/tmp/clawguard-scan-DpTogr/repo/skills/yueranlu/botpress-adk/SKILL.md:1523
Evidence: com/botpress/skills/master/skills/adk/references/tools
Possible base64-encoded payload
/tmp/clawguard-scan-DpTogr/repo/skills/yueranlu/botpress-adk/SKILL.md:1524
Evidence: com/botpress/skills/master/skills/adk/references/workflows
Possible base64-encoded payload
/tmp/clawguard-scan-DpTogr/repo/skills/yueranlu/botpress-adk/SKILL.md:1525
Evidence: com/botpress/skills/master/skills/adk/references/conversations
Possible base64-encoded payload
/tmp/clawguard-scan-DpTogr/repo/skills/yueranlu/botpress-adk/SKILL.md:1526
Evidence: com/botpress/skills/master/skills/adk/references/tables
Possible base64-encoded payload
/tmp/clawguard-scan-DpTogr/repo/skills/yueranlu/botpress-adk/SKILL.md:1527
Evidence: com/botpress/skills/master/skills/adk/references/triggers
Possible base64-encoded payload
/tmp/clawguard-scan-DpTogr/repo/skills/yueranlu/botpress-adk/SKILL.md:1528
Evidence: com/botpress/skills/master/skills/adk/references/knowledge
Possible base64-encoded payload
/tmp/clawguard-scan-DpTogr/repo/skills/yueranlu/botpress-adk/SKILL.md:1529
Evidence: com/botpress/skills/master/skills/adk/references/messages
Possible base64-encoded payload
/tmp/clawguard-scan-DpTogr/repo/skills/yueranlu/botpress-adk/SKILL.md:1530
Evidence: com/botpress/skills/master/skills/adk/references/agent
Possible base64-encoded payload
/tmp/clawguard-scan-DpTogr/repo/skills/yueranlu/botpress-adk/SKILL.md:1531
Evidence: com/botpress/skills/master/skills/adk/references/cli
Possible base64-encoded payload
/tmp/clawguard-scan-DpTogr/repo/skills/yueranlu/botpress-adk/SKILL.md:1532
Evidence: com/botpress/skills/master/skills/adk/references/integration
Possible base64-encoded payload
/tmp/clawguard-scan-DpTogr/repo/skills/yueranlu/botpress-adk/SKILL.md:1533
Evidence: com/botpress/skills/master/skills/adk/references/model
Possible base64-encoded payload
/tmp/clawguard-scan-DpTogr/repo/skills/yueranlu/botpress-adk/SKILL.md:1534
Evidence: com/botpress/skills/master/skills/adk/references/context
Possible base64-encoded payload
/tmp/clawguard-scan-DpTogr/repo/skills/yueranlu/botpress-adk/SKILL.md:1535
Evidence: com/botpress/skills/master/skills/adk/references/tags
Possible base64-encoded payload
/tmp/clawguard-scan-DpTogr/repo/skills/yueranlu/botpress-adk/SKILL.md:1536
Evidence: com/botpress/skills/master/skills/adk/references/files
Possible base64-encoded payload
/tmp/clawguard-scan-DpTogr/repo/skills/yueranlu/botpress-adk/SKILL.md:1537
Evidence: com/botpress/skills/master/skills/adk/references/zai
Possible base64-encoded payload
/tmp/clawguard-scan-DpTogr/repo/skills/yueranlu/botpress-adk/SKILL.md:1538
Evidence: com/botpress/skills/master/skills/adk/references/zai
Possible base64-encoded payload
/tmp/clawguard-scan-DpTogr/repo/skills/yueranlu/botpress-adk/SKILL.md:1539
Evidence: com/botpress/skills/master/skills/adk/references/mcp
The MCP server is not in our registry.
Scan History1 scan
▸Failedv1.0.0Critical45 findings2/12/2026
Scanners4/5 ran
clawguard-rules▸44 findings14ms
View logs
1[2026-02-12T22:42:52.384Z] Running @yourclaw/clawguard-rules pattern matcher2Scanning: /tmp/clawguard-scan-DpTogr/repo/skills/yueranlu/botpress-adk/SKILL.md3Content length: 56871 chars4Patterns matched: 445 [critical] MAL-001: Remote code execution via pipe-to-shell pattern6 [low] PI-041: Possible base64-encoded payload7 [low] PI-041: Possible base64-encoded payload8 [low] PI-041: Possible base64-encoded payload9 [low] PI-041: Possible base64-encoded payload10 [low] PI-041: Possible base64-encoded payload11 [low] PI-041: Possible base64-encoded payload12 [low] PI-041: Possible base64-encoded payload13 [low] PI-041: Possible base64-encoded payload14 [low] PI-041: Possible base64-encoded payload15 [low] PI-041: Possible base64-encoded payload16 [low] PI-041: Possible base64-encoded payload17 [low] PI-041: Possible base64-encoded payload18 [low] PI-041: Possible base64-encoded payload19 [low] PI-041: Possible base64-encoded payload20 [low] PI-041: Possible base64-encoded payload21 [low] PI-041: Possible base64-encoded payload22 [low] PI-041: Possible base64-encoded payload23 [low] PI-041: Possible base64-encoded payload24 [low] PI-041: Possible base64-encoded payload25 [low] PI-041: Possible base64-encoded payload26 [low] PI-041: Possible base64-encoded payload27 [low] PI-041: Possible base64-encoded payload28 [low] PI-041: Possible base64-encoded payload29 [low] PI-041: Possible base64-encoded payload30 [low] PI-041: Possible base64-encoded payload31 [low] PI-041: Possible base64-encoded payload32 [low] PI-041: Possible base64-encoded payload33 [low] PI-041: Possible base64-encoded payload34 [low] PI-041: Possible base64-encoded payload35 [low] PI-041: Possible base64-encoded payload36 [low] PI-041: Possible base64-encoded payload37 [low] PI-041: Possible base64-encoded payload38 [low] PI-041: Possible base64-encoded payload39 [low] PI-041: Possible base64-encoded payload40 [low] PI-041: Possible base64-encoded payload41 [low] PI-041: Possible base64-encoded payload42 [low] PI-041: Possible base64-encoded payload43 [low] PI-041: Possible base64-encoded payload44 [low] PI-041: Possible base64-encoded payload45 [low] PI-041: Possible base64-encoded payload46 [low] PI-041: Possible base64-encoded payload47 [low] PI-041: Possible base64-encoded payload48 [low] PI-041: Possible base64-encoded payload49✓ Completed in 14ms
gitleaks▸0 findings91874ms
View logs
1[2026-02-12T22:44:24.258Z] $ gitleaks detect --source /tmp/clawguard-scan-DpTogr/repo/skills/yueranlu/botpress-adk --report-format json --report-path /dev/stdout --no-git23⚠ stderr output:4○5 │╲6 │ ○7 ○ ░8 ░ gitleaks910[90m10:44PM[0m [31mFTL[0m [1mReport path is not writable: /dev/stdout[0m [36merror=[0m[31m[1m"open /dev/stdout: no such device or address"[0m[0m1112Process exited with code 113✓ Completed in 91874ms
semgrep▸0 findings150527ms
View logs
1[2026-02-12T22:45:22.916Z] $ semgrep scan --json --quiet --config auto /tmp/clawguard-scan-DpTogr/repo/skills/yueranlu/botpress-adk2{"version":"1.151.0","results":[],"errors":[],"paths":{"scanned":["/tmp/clawguard-scan-DpTogr/repo/skills/yueranlu/botpress-adk/README.md","/tmp/clawguard-scan-DpTogr/repo/skills/yueranlu/botpress-adk/SKILL.md","/tmp/clawguard-scan-DpTogr/repo/skills/yueranlu/botpress-adk/_meta.json"]},"time":{"rules":[],"rules_parse_time":13.623882055282593,"profiling_times":{"config_time":18.02668786048889,"core_time":19.322453498840332,"ignores_time":0.02347111701965332,"total_time":37.423030853271484},"parsing_time":{"total_time":0.0,"per_file_time":{"mean":0.0,"std_dev":0.0},"very_slow_stats":{"time_ratio":0.0,"count_ratio":0.0},"very_slow_files":[]},"scanning_time":{"total_time":1.0944490432739258,"per_file_time":{"mean":0.15634986332484654,"std_dev":0.06969707594804712},"very_slow_stats":{"time_ratio":0.0,"count_ratio":0.0},"very_slow_files":[]},"matching_time":{"total_time":0.0,"per_file_and_rule_time":{"mean":0.0,"std_dev":0.0},"very_slow_stats":{"time_ratio":0.0,"count_ratio":0.0},"very_slow_rules_on_files":[]},"tainting_time":{"total_time":0.0,"per_def_and_rule_time":{"mean":0.0,"std_dev":0.0},"very_slow_stats":{"time_ratio":0.0,"count_ratio":0.0},"very_slow_rules_on_defs":[]},"fixpoint_timeouts":[],"prefiltering":{"project_level_time":0.0,"file_level_time":0.0,"rules_with_project_prefilters_ratio":0.0,"rules_with_file_prefilters_ratio":0.9797297297297297,"rules_selected_ratio":0.04054054054054054,"rules_matched_ratio":0.04054054054054054},"targets":[],"total_bytes":0,"max_memory_bytes":1231881024},"engine_requested":"OSS","skipped_rules":[],"profiling_results":[]}34Process exited with code 05✓ Completed in 150527ms
mcp-scan▸1 finding104869ms
View logs
1[2026-02-12T22:44:37.261Z] $ mcp-scan --skills /tmp/clawguard-scan-DpTogr/repo/skills/yueranlu/botpress-adk --json2{3 "/tmp/clawguard-scan-DpTogr/repo/skills/yueranlu": {4 "client": "not-available",5 "path": "/tmp/clawguard-scan-DpTogr/repo/skills/yueranlu",6 "servers": [7 {8 "name": "botpress-adk",9 "server": {10 "path": "/tmp/clawguard-scan-DpTogr/repo/skills/yueranlu/botpress-adk",11 "type": "skill"12 },13 "signature": {14 "metadata": {15 "meta": null,16 "protocolVersion": "built-in",17 "capabilities": {18 "experimental": null,19 "logging": null,20 "prompts": null,21 "resources": null,22 "tools": {23 "listChanged": false24 },25 "completions": null,26 "tasks": null27 },28 "serverInfo": {29 "name": "adk",30 "title": null,31 "version": "skills",32 "websiteUrl": null,33 "icons": null34 },35 "instructions": "A guide to build AI bots with Botpress's Agent Development Kit (ADK)",36 "prompts": {37 "listChanged": false38 },39 "resources": {40 "subscribe": null,41 "listChanged": false42 }43 },44 "prompts": [45 {46 "name": "SKILL.md",47 "title": null,48 "description": "\n\n# Botpress ADK Development Guide\n\nA comprehensive guide for building AI bots with the Botpress Agent Development Kit (ADK).\n\n## When to Use\n\n- User asks to build a Botpress bot or chatbot\n- User mentions ADK, Agent Development Kit, or Botpress\n- User wants to create actions, tools, workflows, conversations, tables, triggers, or knowledge bases\n- User needs help with `adk` CLI commands (init, dev, deploy, link)\n- User has ADK-related errors or needs troubleshooting\n- User asks about bot configuration, state management, or integrations\n\n## Quick Reference\n\nThe ADK is a **convention-based TypeScript framework** where **file structure maps directly to bot behavior**.\n\n**Your role:** Guide users through the entire bot development lifecycle - from project setup to deployment. Use the patterns and code examples in this skill to write correct, working ADK code.\n\n**Key principle:** In ADK, **where you put files matters**. Each component type has a specific `src/` subdirectory, and files are auto-discovered based on location.\n\n## How to Use This Skill\n\n**This skill is your primary reference for building Botpress bots.** When a user asks you to build something with the ADK:\n\n1. **Identify what they need** - Is it a new bot, a feature (action, tool, workflow), data storage (table), or event handling (trigger)?\n2. **Check the correct directory** - Each component type goes in a specific `src/` subdirectory\n3. **Use the patterns below** - Follow the code examples exactly, they represent the correct ADK conventions\n4. **Run `adk --help`** - For CLI commands not covered here, or `adk <command> --help` for specific help\n\n**Decision Guide - What Component to Create:**\n\n| User Wants To... | Create This | Location |\n|------------------|-------------|----------|\n| Handle user messages | Conversation | `src/conversations/` |\n| Add a function the AI can call | Tool | `src/tools/` |\n| Add reusable business logic | Action | `src/actions/` |\n| Run background/scheduled tasks | Workflow | `src/workflows/` |\n| Store structured data | Table | `src/tables/` |\n| React to events (user created, etc.) | Trigger | `src/triggers/` |\n| Give AI access to docs/data | Knowledge Base | `src/knowledge/` |\n| Connect external service (Slack, etc.) | Integration | `adk add <name>` |\n\n**If the information in this skill isn't enough**, fetch the corresponding GitHub reference file (links provided in each section) for more detailed specifications.\n\n## Important: ADK is AI-Native\n\nThe ADK does **NOT** use traditional chatbot patterns. Don't create intents, entities, or dialog flows.\n\n**Instead of:**\n- Defining intents (`greet`, `orderPizza`, `checkStatus`)\n- Training entity extraction (`@pizzaSize`, `@toppings`)\n- Manually routing to intent handlers\n\n**ADK uses:**\n- `execute()` - The AI understands user intent naturally from instructions\n- Tools - AI autonomously decides when to call your functions\n- `zai.extract()` - Schema-based structured data extraction\n- Knowledge bases - RAG for grounding responses in your docs\n\n**Docs:** https://www.botpress.com/docs/adk/\n**GitHub:** https://github.com/botpress/skills/tree/master/skills/adk\n\n---\n\n## Prerequisites & Installation\n\nBefore using the ADK, ensure the user has:\n\n- **Botpress Account** - Create at https://app.botpress.cloud\n- **Node.js v22.0.0+** - Check with `node --version`\n- **Package Manager** - bun (recommended), pnpm, yarn, or npm\n\n**Install the ADK CLI:**\n\nmacOS & Linux:\n```bash\ncurl -fsSL https://github.com/botpress/adk/releases/latest/download/install.sh | bash\n```\n\nWindows (PowerShell):\n```powershell\npowershell -c \"irm https://github.com/botpress/adk/releases/latest/download/install.ps1 | iex\"\n```\n\n**Verify installation:**\n```bash\nadk --version\n```\n\nIf installation fails, check https://github.com/botpress/adk/releases for manual download options.\n\n**Docs:** https://www.botpress.com/docs/adk/quickstart\n**GitHub:** https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/cli.md\n\n---\n\n## Quick Start\n\nOnce the ADK CLI is installed, create a new bot:\n\n```bash\nadk init my-bot # Create project (choose \"Hello World\" template for beginners)\ncd my-bot\nnpm install # Or bun/pnpm/yarn\nadk login # Authenticate with Botpress Cloud\nadk add chat # Add the chat integration for testing\nadk dev # Start dev server with hot reload\nadk chat # Test in CLI (run in separate terminal)\nadk deploy # Deploy to production when ready\n```\n\nThe visual console at **http://localhost:3001/** lets you configure integrations and test the bot.\n\n**Docs:** https://www.botpress.com/docs/adk/quickstart\n**GitHub:** https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/cli.md\n\n---\n\n## Linking and Deploying Your Bot\n\n**IMPORTANT:** Your bot must be linked to Botpress Cloud and deployed for it to work. The ADK runs locally during development but the bot itself lives in Botpress Cloud.\n\n### The Correct Order: Link \u2192 Dev \u2192 Deploy\n\nFollow this order to get your bot working:\n\n```bash\n# 1. LINK - Connect your project to Botpress Cloud (creates agent.json)\nadk link\n\n# 2. DEV - Start the development server (hot reload, testing)\nadk dev\n\n# 3. DEPLOY - Push to production when ready\nadk deploy\n```\n\n**Step-by-step:**\n\n1. **`adk link`** - Links your local project to a bot in Botpress Cloud. This creates `agent.json` with your workspace and bot IDs. Run this first before anything else.\n\n2. **`adk dev`** - Starts the local development server with hot reloading. Opens the dev console at http://localhost:3001 where you can configure integrations and test your bot. Use `adk chat` in a separate terminal to test.\n\n3. **`adk deploy`** - Deploys your bot to production. Run this when you're ready for your bot to be live and accessible through production channels (Slack, WhatsApp, webchat, etc.).\n\n### Troubleshooting Errors\n\n**If you encounter errors when running `adk dev` or `adk deploy`:**\n\n1. **Check the logs** - Look at the terminal output or the logs panel in the dev console at http://localhost:3001\n2. **Copy the error message** - Select and copy the full error message from the logs\n3. **Ask for help** - Paste the error back to the AI assistant and ask it to help fix the issue\n\nCommon error scenarios:\n- **Integration configuration errors:** Usually means an integration needs to be configured in the UI at localhost:3001\n- **Type errors:** Often caused by incorrect imports or schema mismatches\n- **Deployment failures:** May indicate missing environment variables or invalid configuration\n\n**Example workflow for fixing errors:**\n```\n1. Run `adk dev` or `adk deploy`\n2. See error in terminal/logs\n3. Copy the error message\n4. Tell the AI: \"I got this error when running adk dev: [paste error]\"\n5. The AI will help diagnose and fix the issue\n```\n\n**Docs:** https://www.botpress.com/docs/adk/quickstart\n**GitHub:** https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/cli.md\n\n---\n\n## Project Structure\n\n**Critical rule:** File location determines behavior. Place components in the correct `src/` subdirectory or they won't be discovered.\n\n```\nmy-bot/\n\u251c\u2500\u2500 agent.config.ts # Bot configuration: name, models, state schemas, integrations\n\u251c\u2500\u2500 agent.json # Workspace/bot IDs (auto-generated by adk link/dev, add to .gitignore)\n\u251c\u2500\u2500 package.json # Node.js dependencies and scripts (dev, build, deploy)\n\u251c\u2500\u2500 tsconfig.json # TypeScript configuration\n\u251c\u2500\u2500 .env # API keys and secrets (never commit!)\n\u251c\u2500\u2500 .gitignore # Should include: agent.json, .env, node_modules/, .botpress/\n\u251c\u2500\u2500 src/\n\u2502 \u251c\u2500\u2500 conversations/ # Handle incoming messages \u2192 use execute() for AI responses\n\u2502 \u251c\u2500\u2500 workflows/ # Background processes \u2192 use step() for resumable operations\n\u2502 \u251c\u2500\u2500 actions/ # Reusable functions \u2192 call from anywhere with actions.name()\n\u2502 \u251c\u2500\u2500 tools/ # AI-callable functions \u2192 AI decides when to invoke these\n\u2502 \u251c\u2500\u2500 tables/ # Data storage \u2192 auto-synced to cloud, supports semantic search\n\u2502 \u251c\u2500\u2500 triggers/ # Event handlers \u2192 react to user.created, integration events, etc.\n\u2502 \u2514\u2500\u2500 knowledge/ # RAG sources \u2192 index docs, websites, or tables for AI context\n\u2514\u2500\u2500 .botpress/ # Auto-generated types (never edit manually)\n```\n\n**Key Configuration Files:**\n\n- **agent.config.ts** - Primary configuration defining bot metadata, AI models, state schemas, and integrations (you edit this)\n- **agent.json** - Links agent to workspace/bot IDs. Auto-generated by `adk link` or `adk dev`. **Add to .gitignore** - contains environment-specific IDs that differ per developer\n- **package.json** - Node.js config with `@botpress/runtime` dependency and scripts for `dev`, `build`, `deploy`\n- **tsconfig.json** - TypeScript configuration for the project\n- **.env** - Environment variables for API keys and secrets (never commit!)\n- **.gitignore** - Should include: `agent.json`, `.env`, `node_modules/`, `.botpress/`\n\n**Docs:** https://www.botpress.com/docs/adk/project-structure\n**GitHub:** https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/agent-config.md\n\n---\n\n## Agent Configuration\n\nThe `agent.config.ts` file defines your bot's identity, AI models, state schemas, and integrations. Always start here when setting up a new bot.\n\n```typescript\nimport { defineConfig, z } from \"@botpress/runtime\";\n\nexport default defineConfig({\n name: \"my-support-bot\",\n description: \"AI customer support assistant\",\n\n // AI models for different operations\n defaultModels: {\n autonomous: \"openai:gpt-4o\", // Used by execute() for conversations\n zai: \"openai:gpt-4o-mini\" // Used by zai operations (cheaper, faster)\n },\n\n // Global bot state - shared across all conversations and users\n bot: {\n state: z.object({\n maintenanceMode: z.boolean().default(false),\n totalConversations: z.number().default(0)\n })\n },\n\n // Per-user state - persists across all conversations for each user\n user: {\n state: z.object({\n name: z.string().optional(),\n tier: z.enum([\"free\", \"pro\"]).default(\"free\"),\n preferredLanguage: z.enum([\"en\", \"es\", \"fr\"]).default(\"en\")\n }),\n tags: {\n source: z.string(),\n region: z.string().optional()\n }\n },\n\n // Per-conversation state\n conversation: {\n state: z.object({\n context: z.string().optional()\n }),\n tags: {\n category: z.enum([\"support\", \"sales\", \"general\"]),\n priority: z.enum([\"low\", \"medium\", \"high\"]).optional()\n }\n },\n\n // Integrations your bot uses (ADK 1.9+ format)\n dependencies: {\n integrations: {\n chat: { version: \"chat@0.7.3\", enabled: true },\n slack: { version: \"slack@2.5.5\", enabled: true }\n }\n }\n});\n```\n\n**Available models:**\n- OpenAI: `openai:gpt-4o`, `openai:gpt-4o-mini`, `openai:gpt-4-turbo`\n- Anthropic: `anthropic:claude-3-5-sonnet`, `anthropic:claude-3-opus`\n- Google: `google:gemini-1.5-pro`, `google:gemini-1.5-flash`\n\n**Docs:** https://www.botpress.com/docs/adk/project-structure\n**GitHub:** https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/agent-config.md\n\n---\n\n## Core Concepts\n\n### 1. Actions - Reusable Business Logic\n\n**When to create an Action:**\n- You need reusable logic that will be called from multiple places (workflows, conversations, triggers)\n- You're wrapping an external API or database operation\n- You want testable, composable business logic\n- You need to call integration APIs (Slack, Linear, etc.) with custom logic\n\n**When NOT to use an Action (use a Tool instead):**\n- You want the AI to decide when to call it autonomously\n- The function should be available during `execute()`\n\nActions are **not** directly callable by the AI - convert them to tools with `.asTool()` if the AI needs to use them.\n\n**Location:** `src/actions/*.ts`\n\n```typescript\nimport { Action, z } from \"@botpress/runtime\";\n\nexport const fetchUser = new Action({\n name: \"fetchUser\",\n description: \"Retrieves user details from the database\",\n\n // Define input/output with Zod schemas for type safety\n input: z.object({ userId: z.string() }),\n output: z.object({ name: z.string(), email: z.string() }),\n\n // IMPORTANT: Handler receives { input, client } - destructure input INSIDE the handler\n async handler({ input, client }) {\n const { user } = await client.getUser({ id: input.userId });\n return { name: user.name, email: user.tags.email };\n }\n});\n```\n\n**Calling actions:**\n```typescript\nimport { actions } from \"@botpress/runtime\";\nconst userData = await actions.fetchUser({ userId: \"123\" });\n\n// To make an action callable by the AI, convert it to a tool:\ntools: [actions.fetchUser.asTool()]\n```\n\n**Key Rules:**\n- Handler receives `{ input, client }` - must destructure `input` inside the handler\n- Cannot destructure input fields directly in parameters\n- Can call other actions, integration actions, access state\n- Can be converted to tools with `.asTool()`\n\n**Docs:** https://www.botpress.com/docs/adk/concepts/actions\n**GitHub:** https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/actions.md\n\n---\n\n### 2. Tools - AI-Callable Functions\n\n**When to create a Tool:**\n- You want the AI to autonomously decide when to use this function\n- The function retrieves information the AI needs (search, lookup, fetch)\n- The function performs actions on behalf of the user (create ticket, send message)\n- You're building capabilities the AI should have during conversations\n\n**The AI decides when to use tools based on:**\n1. The tool's `description` - Make this clear and specific about WHEN to use it\n2. The input schema's `.describe()` fields - Help AI understand what parameters mean\n3. The conversation context and user's intent\n\n**Key difference from Actions:** Tools can destructure input directly; Actions cannot.\n\n**Location:** `src/tools/*.ts`\n\n```typescript\nimport { Autonomous, z } from \"@botpress/runtime\";\n\nexport const searchProducts = new Autonomous.Tool({\n name: \"searchProducts\",\n // This description is critical - it tells the AI when to use this tool\n description: \"Search the product catalog. Use when user asks about products, availability, pricing, or wants to browse items.\",\n\n input: z.object({\n query: z.string().describe(\"Search keywords\"),\n category: z.string().optional().describe(\"Filter by category\")\n }),\n output: z.object({\n products: z.array(z.object({ id: z.string(), name: z.string(), price: z.number() }))\n }),\n\n // Unlike actions, tools CAN destructure input directly in the handler\n handler: async ({ query, category }) => {\n // Your search logic here\n return { products: [] };\n }\n});\n```\n\n**Using ThinkSignal:** When a tool can't complete but you want to give the AI context:\n```typescript\nimport { Autonomous } from \"@botpress/runtime\";\n\n// Inside handler - AI will see this message and can respond appropriately\nthrow new Autonomous.ThinkSignal(\n \"No results found\",\n \"No products found matching that query. Ask user to try different search terms.\"\n);\n```\n\n**Advanced Tool Properties:**\n```typescript\nexport const myTool = new Autonomous.Tool({\n name: \"myTool\",\n description: \"Tool description\",\n input: z.object({...}),\n output: z.object({...}),\n aliases: [\"searchDocs\", \"findDocs\"], // Alternative names\n handler: async (input, ctx) => {\n console.log(`Call ID: ${ctx.callId}`); // Unique call identifier\n // ...\n },\n retry: async ({ attempt, error }) => {\n if (attempt < 3 && error?.code === 'RATE_LIMIT') {\n await new Promise(r => setTimeout(r, 1000 * attempt));\n return true; // Retry\n }\n return false; // Don't retry\n }\n});\n```\n\n**Docs:** https://www.botpress.com/docs/adk/concepts/tools\n**GitHub:** https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/tools.md\n\n---\n\n### 3. Conversations - Message Handlers\n\n**When to create a Conversation:**\n- Every bot needs at least one conversation handler to respond to users\n- Create separate handlers for different channels if they need different behavior\n- Use `channel: \"*\"` to handle all channels with one handler\n\n**Key decisions when building a conversation:**\n1. **Which channels?** - Specify `\"*\"` for all, or specific channels like `\"slack.dm\"`\n2. **What tools does the AI need?** - Pass them to `execute({ tools: [...] })`\n3. **What knowledge should ground responses?** - Pass to `execute({ knowledge: [...] })`\n4. **What instructions guide the AI?** - Define personality, rules, and context\n\n**The `execute()` function is the heart of ADK** - it runs autonomous AI logic with your tools and knowledge. Most conversation handlers will call `execute()`.\n\n**Location:** `src/conversations/*.ts`\n\n```typescript\nimport { Conversation, z } from \"@botpress/runtime\";\n\nexport const Chat = new Conversation({\n // Which channels this handler responds to\n channel: \"chat.channel\", // Or \"*\" for all, or [\"slack.dm\", \"webchat.channel\"]\n\n // Per-conversation state (optional)\n state: z.object({\n messageCount: z.number().default(0)\n }),\n\n async handler({ message, state, conversation, execute, user }) {\n state.messageCount += 1;\n\n // Handle commands\n if (message?.payload?.text?.startsWith(\"/help\")) {\n await conversation.send({\n type: \"text\",\n payload: { text: \"Available commands: /help, /status\" }\n });\n return;\n }\n\n // Let the AI handle the response with your tools and knowledge\n await execute({\n // Instructions guide the AI's behavior and personality\n instructions: `You are a helpful customer support agent for Acme Corp.\n User's name: ${user.state.name || \"there\"}\n User's tier: ${user.state.tier}\n Be friendly, concise, and always offer to help further.`,\n\n // Tools the AI can use during this conversation\n tools: [searchProducts, actions.createTicket.asTool()],\n\n // Knowledge bases for RAG - AI will search these to ground responses\n knowledge: [DocsKnowledgeBase],\n\n model: \"openai:gpt-4o\",\n temperature: 0.7,\n iterations: 10 // Max tool call iterations\n });\n }\n});\n```\n\n**Handler Context:**\n- `message` - User's message data\n- `execute` - Run autonomous AI logic\n- `conversation` - Conversation instance methods (send, startTyping, stopTyping)\n- `state` - Mutable state (bot, user, conversation)\n- `client` - Botpress API client\n- `type` - Event classification (message, workflow_request)\n\n**Execute Function Options:**\n```typescript\nawait execute({\n instructions: string | async function, // Required\n tools: Tool[], // AI-callable tools\n knowledge: Knowledge[], // Knowledge bases for RAG\n exits: Exit[], // Structured exit handlers\n model: string, // AI model to use\n temperature: number, // 0-1, default 0.7\n iterations: number, // Max tool calls, default 10\n hooks: {\n onBeforeTool: async ({ tool, input }) => { ... },\n onAfterTool: async ({ tool, output }) => { ... },\n onTrace: async (trace) => { ... }\n }\n});\n```\n\n**Common channels:** `chat.channel`, `webchat.channel`, `slack.dm`, `slack.channel`, `discord.channel`, `whatsapp.channel`, `\"*\"` (all)\n\n**Docs:** https://www.botpress.com/docs/adk/concepts/conversations\n**GitHub:** https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/conversations.md\n\n---\n\n### 4. Workflows - Background & Multi-Step Processes\n\n**When to create a Workflow:**\n- Operations that take longer than 2 minutes (the default timeout)\n- Multi-step processes that need to survive crashes/restarts\n- Scheduled/recurring tasks (daily reports, periodic syncs)\n- Background processing (order fulfillment, data migration)\n- Operations that need to wait for external events or user input\n\n**When NOT to use a Workflow (handle in conversation instead):**\n- Quick operations that complete immediately\n- Simple request-response patterns\n- Operations that don't need persistence\n\n**Key workflow concepts:**\n- **Steps are checkpoints** - If workflow crashes, it resumes from last completed step\n- **State persists** - Store progress in `state` to track across steps\n- **Always pass conversationId** - If the workflow needs to message users back\n\n**Location:** `src/workflows/*.ts`\n\n```typescript\nimport { Workflow, z } from \"@botpress/runtime\";\n\nexport const ProcessOrderWorkflow = new Workflow({\n name: \"processOrder\",\n description: \"Processes customer orders\",\n timeout: \"6h\", // Max duration\n schedule: \"0 9 * * *\", // Optional: run daily at 9am (cron syntax)\n\n input: z.object({\n orderId: z.string(),\n conversationId: z.string() // Include this to message the user back!\n }),\n\n state: z.object({\n currentStep: z.number().default(0),\n processedItems: z.array(z.string()).default([])\n }),\n\n output: z.object({\n success: z.boolean(),\n itemsProcessed: z.number()\n }),\n\n async handler({ input, state, step, client, execute }) {\n // State is passed as parameter, auto-tracked\n state.currentStep = 1;\n\n // IMPORTANT: Each step needs a unique, stable name (no dynamic names!)\n const orderData = await step(\"fetch-order\", async () => {\n return await fetchOrderData(input.orderId);\n });\n\n // Steps can have retry logic\n await step(\"process-payment\", async () => {\n return await processPayment(orderData);\n }, { maxAttempts: 3 });\n\n // To message the user from a workflow, use client.createMessage (NOT conversation.send)\n await step(\"notify-user\", async () => {\n await client.createMessage({\n conversationId: input.conversationId,\n type: \"text\",\n payload: { text: \"Your order has been processed!\" }\n });\n });\n\n return {\n success: true,\n itemsProcessed: state.processedItems.length\n };\n }\n});\n\n// Start a workflow from a conversation or trigger\nawait ProcessOrderWorkflow.start({\n orderId: \"123\",\n conversationId: conversation.id // Always pass this if you need to message back\n});\n\n// Get or create with deduplication\nconst instance = await ProcessOrderWorkflow.getOrCreate({\n key: `order-${orderId}`, // Prevents duplicate workflows\n input: { orderId, conversationId }\n});\n```\n\n**Step Methods:**\n\n| Method | Purpose |\n|--------|---------|\n| `step(name, fn)` | Basic execution with caching |\n| `step.sleep(name, ms)` | Pause for milliseconds |\n| `step.sleepUntil(name, date)` | Pause until specific date |\n| `step.listen()` | Wait for external events |\n| `step.progress(msg)` | Update progress message |\n| `step.request(name, prompt)` | Request user input (blocking) |\n| `step.executeWorkflow()` | Start and await another workflow |\n| `step.waitForWorkflow(id)` | Wait for existing workflow |\n| `step.map(items, fn)` | Process array with concurrency |\n| `step.forEach(items, fn)` | Execute on items without results |\n| `step.batch(items, fn)` | Process in groups |\n| `step.fail(reason)` | Mark workflow as failed |\n| `step.abort()` | Stop immediately without failure |\n\n**Critical Rules:**\n- Step names must be **unique** and **stable** (avoid dynamic naming in loops)\n- State is passed as a parameter, not accessed via `this.state`\n- Always pass `conversationId` for workflows that need to message users\n- Default timeout is 2 minutes - use steps for longer processes\n\n**Docs:** https://www.botpress.com/docs/adk/concepts/workflows/overview\n**GitHub:** https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/workflows.md\n\n---\n\n### 5. Tables - Data Storage\n\n**When to create a Table:**\n- You need to persist structured data (users, orders, tickets, logs)\n- You want to query/filter data by fields\n- You need semantic search on text content (set `searchable: true`)\n- You're storing data that should survive bot restarts\n\n**When NOT to use a Table (use State instead):**\n- Simple key-value data per user/conversation \u2192 use `user.state` or `conversation.state`\n- Temporary data that doesn't need persistence\n- Small amounts of data that fit in state\n\n**Tables vs Knowledge Bases:**\n- **Tables** = Structured data you CRUD (create, read, update, delete)\n- **Knowledge Bases** = Documents/content for AI to search and reference\n\n**Location:** `src/tables/*.ts`\n\n**CRITICAL RULES (violations will cause errors):**\n- Do NOT define an `id` column - it's created automatically as a number\n- Table names MUST end with \"Table\" (e.g., `OrdersTable`, not `Orders`)\n\n```typescript\nimport { Table, z } from \"@botpress/runtime\";\n\nexport const OrdersTable = new Table({\n name: \"OrdersTable\", // Must end with \"Table\"\n description: \"Stores order information\",\n columns: {\n // NO id column - it's automatic!\n orderId: z.string(),\n userId: z.string(),\n status: z.enum([\"pending\", \"completed\", \"cancelled\"]),\n total: z.number(),\n createdAt: z.date(),\n // Enable semantic search on a column:\n notes: {\n schema: z.string(),\n searchable: true\n }\n }\n});\n```\n\n**CRUD operations:**\n```typescript\n// Create - id is auto-assigned\nawait OrdersTable.createRows({\n rows: [{ orderId: \"ord-123\", userId: \"user-456\", status: \"pending\", total: 99.99, createdAt: new Date() }]\n});\n\n// Read with filters\nconst { rows } = await OrdersTable.findRows({\n filter: { userId: \"user-456\", status: \"pending\" },\n orderBy: \"createdAt\",\n orderDirection: \"desc\",\n limit: 10\n});\n\n// Get single row by id\nconst row = await OrdersTable.getRow({ id: 123 });\n\n// Semantic search (on searchable columns)\nconst { rows } = await OrdersTable.findRows({\n search: \"delivery issue\",\n limit: 5\n});\n\n// Update - must include the id\nawait OrdersTable.updateRows({\n rows: [{ id: 1, status: \"completed\" }]\n});\n\n// Upsert - insert or update based on key column\nawait OrdersTable.upsertRows({\n rows: [{ orderId: \"ord-123\", status: \"shipped\" }],\n keyColumn: \"orderId\"\n});\n\n// Delete by filter\nawait OrdersTable.deleteRows({ status: \"cancelled\" });\n\n// Delete by IDs\nawait OrdersTable.deleteRowIds([123, 456]);\n```\n\n**Advanced: Computed Columns:**\n```typescript\ncolumns: {\n basePrice: z.number(),\n taxRate: z.number(),\n fullPrice: {\n computed: true,\n schema: z.number(),\n dependencies: [\"basePrice\", \"taxRate\"],\n value: async (row) => row.basePrice * (1 + row.taxRate)\n }\n}\n```\n\n**Docs:** https://www.botpress.com/docs/adk/concepts/tables\n**GitHub:** https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/tables.md\n\n---\n\n### 6. Knowledge Bases - RAG for AI Context\n\n**When to create a Knowledge Base:**\n- You want the AI to answer questions based on your documentation\n- You have FAQs, policies, or product info the AI should reference\n- You want AI responses grounded in specific content (not hallucinated)\n- You're building a support bot that needs access to help articles\n\n**How RAG works in ADK:**\n1. You define knowledge sources (websites, files, tables)\n2. Content is indexed and embedded for semantic search\n3. During `execute()`, the AI automatically searches relevant knowledge\n4. AI uses retrieved content to generate grounded responses\n\n**Choosing a DataSource type:**\n- **Website** - Index public documentation, help sites, blogs\n- **Directory** - Index local markdown/text files (dev only!)\n- **Table** - Index structured data from your tables\n\n**Location:** `src/knowledge/*.ts`\n\n```typescript\nimport { Knowledge, DataSource } from \"@botpress/runtime\";\n\n// Website source - index via sitemap\nconst websiteSource = DataSource.Website.fromSitemap(\n \"https://docs.example.com/sitemap.xml\",\n {\n id: \"website-docs\",\n maxPages: 500,\n maxDepth: 10,\n filter: (ctx) => ctx.url.includes(\"/docs/\") // Only index /docs/ pages\n }\n);\n\n// Local files (development only - won't work in production)\nconst localSource = DataSource.Directory.fromPath(\"src/knowledge/docs\", {\n id: \"local-docs\",\n filter: (path) => path.endsWith(\".md\")\n});\n\n// Table-based knowledge\nconst tableSource = DataSource.Table.fromTable(FAQTable, {\n id: \"faq-table\",\n transform: ({ row }) => `Question: ${row.question}\\nAnswer: ${row.answer}`,\n filter: ({ row }) => row.published === true\n});\n\nexport const DocsKB = new Knowledge({\n name: \"docsKB\",\n description: \"Product documentation and help articles\",\n sources: [websiteSource, localSource, tableSource]\n});\n\n// Use in conversations - AI will search this knowledge base\nawait execute({\n instructions: \"Answer based on the documentation\",\n knowledge: [DocsKB]\n});\n\n// Manually refresh knowledge base\nawait DocsKB.refresh(); // Smart refresh (only changed content)\nawait DocsKB.refresh({ force: true }); // Force full re-index\nawait DocsKB.refreshSource(\"website-docs\", { force: true }); // Refresh specific source\n```\n\n**Website Source Methods:**\n- `fromSitemap(url, options)` - Parse XML sitemap\n- `fromWebsite(baseUrl, options)` - Crawl from base URL (requires Browser integration)\n- `fromLlmsTxt(url, options)` - Parse llms.txt file\n- `fromUrls(urls, options)` - Index specific URLs\n\n**Docs:** https://www.botpress.com/docs/adk/concepts/knowledge\n**GitHub:** https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/knowledge-bases.md\n\n---\n\n### 7. Triggers - Event-Driven Automation\n\n**When to create a Trigger:**\n- You need to react to events automatically (user signs up, issue created, etc.)\n- You want to start workflows when specific events occur\n- You need to sync data when external systems change\n- You want to send notifications based on events\n\n**Common trigger patterns:**\n- **User onboarding** - Trigger on `user.created` \u2192 start onboarding workflow\n- **Integration sync** - Trigger on `linear:issueCreated` \u2192 create record in table\n- **Notifications** - Trigger on `workflow.completed` \u2192 send Slack message\n\n**Finding available events:**\n- Bot events: `user.created`, `conversation.started`, `workflow.completed`, etc.\n- Integration events: Run `adk info <integration> --events` to see available events\n\n**Location:** `src/triggers/*.ts`\n\n```typescript\nimport { Trigger } from \"@botpress/runtime\";\n\nexport default new Trigger({\n name: \"onNewUser\",\n description: \"Start onboarding when user created\",\n events: [\"user.created\"], // Can listen to multiple events\n\n handler: async ({ event, client, actions }) => {\n const { userId, email } = event.payload;\n\n // Start an onboarding workflow\n await OnboardingWorkflow.start({\n userId,\n email\n });\n }\n});\n\n// Integration events use format: integration:eventName\nexport const LinearTrigger = new Trigger({\n name: \"onLinearIssue\",\n description: \"Handle Linear issue events\",\n events: [\"linear:issueCreated\", \"linear:issueUpdated\"],\n\n handler: async ({ event, actions }) => {\n if (event.type === \"linear:issueCreated\") {\n await actions.slack.sendMessage({\n channel: \"#notifications\",\n text: `New issue: ${event.payload.title}`\n });\n }\n }\n});\n```\n\n**Common Bot Events:**\n- `user.created`, `user.updated`, `user.deleted`\n- `conversation.started`, `conversation.ended`, `message.created`\n- `workflow.started`, `workflow.completed`, `workflow.failed`\n- `bot.started`, `bot.stopped`\n\n**Common Integration Events:**\n- Slack: `slack:reactionAdded`, `slack:memberJoinedChannel`\n- Linear: `linear:issueCreated`, `linear:issueUpdated`\n- GitHub: `github:issueOpened`, `github:pullRequestOpened`\n- Intercom: `intercom:conversationEvent`, `intercom:contactEvent`\n\n**Find integration events:** Run `adk info <integration> --events`\n\n**Docs:** https://www.botpress.com/docs/adk/concepts/triggers\n**GitHub:** https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/triggers.md\n\n---\n\n## Sending Messages\n\n**CRITICAL: The method depends on WHERE you're sending from:**\n\n| Context | Method | Why |\n|---------|--------|-----|\n| In Conversations | `conversation.send()` | Has conversation context |\n| In Workflows/Actions | `client.createMessage()` | Needs explicit `conversationId` |\n\n**Common mistake:** Using `client.createMessage()` in conversations. Always use `conversation.send()` instead.\n\nThe method depends on where you're sending from:\n\n**In conversations** - Use `conversation.send()`:\n```typescript\nawait conversation.send({ type: \"text\", payload: { text: \"Hello!\" } });\nawait conversation.send({ type: \"image\", payload: { imageUrl: \"https://...\" } });\nawait conversation.send({\n type: \"choice\",\n payload: {\n text: \"Pick one:\",\n choices: [\n { title: \"Option A\", value: \"a\" },\n { title: \"Option B\", value: \"b\" }\n ]\n }\n});\n```\n\n**In workflows or actions** - Use `client.createMessage()` with `conversationId`:\n```typescript\nawait client.createMessage({\n conversationId: input.conversationId, // Must have this!\n type: \"text\",\n payload: { text: \"Workflow complete!\" }\n});\n```\n\n**All Message Types:**\n```typescript\n// Text\n{ type: \"text\", payload: { text: \"Hello!\" } }\n\n// Markdown\n{ type: \"markdown\", payload: { text: \"# Heading\\n**Bold**\" } }\n\n// Image\n{ type: \"image\", payload: { imageUrl: \"https://...\" } }\n\n// Audio\n{ type: \"audio\", payload: { audioUrl: \"https://...\" } }\n\n// Video\n{ type: \"video\", payload: { videoUrl: \"https://...\" } }\n\n// File\n{ type: \"file\", payload: { fileUrl: \"https://...\", title: \"Document.pdf\" } }\n\n// Location\n{ type: \"location\", payload: { latitude: 40.7128, longitude: -74.0060, address: \"New York, NY\" } }\n\n// Card\n{ type: \"card\", payload: {\n title: \"Product Name\",\n subtitle: \"Description\",\n imageUrl: \"https://...\",\n actions: [\n { action: \"url\", label: \"View\", value: \"https://...\" },\n { action: \"postback\", label: \"Buy\", value: \"buy_123\" }\n ]\n}}\n\n// Carousel\n{ type: \"carousel\", payload: {\n items: [\n { title: \"Item 1\", subtitle: \"...\", imageUrl: \"...\", actions: [...] },\n { title: \"Item 2\", subtitle: \"...\", imageUrl: \"...\", actions: [...] }\n ]\n}}\n\n// Choice (Quick Replies)\n{ type: \"choice\", payload: {\n text: \"Select an option:\",\n choices: [\n { title: \"Option 1\", value: \"opt1\" },\n { title: \"Option 2\", value: \"opt2\" }\n ]\n}}\n\n// Dropdown\n{ type: \"dropdown\", payload: {\n text: \"Select country:\",\n options: [\n { label: \"United States\", value: \"us\" },\n { label: \"Canada\", value: \"ca\" }\n ]\n}}\n```\n\n**GitHub:** https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/messages.md\n\n---\n\n## Zai - LLM Utility Operations\n\n**When to use Zai vs execute():**\n- **Use `zai`** for specific, structured AI operations (extract data, classify, summarize)\n- **Use `execute()`** for autonomous, multi-turn AI conversations with tools\n\n**Zai is perfect for:**\n- Extracting structured data from user messages (`zai.extract`)\n- Classifying/labeling content (`zai.check`, `zai.label`)\n- Summarizing long content (`zai.summarize`)\n- Answering questions from documents (`zai.answer`)\n- Sorting/filtering/grouping data intelligently (`zai.sort`, `zai.filter`, `zai.group`)\n\n**Zai operations are optimized for speed and cost** - they use the `zai` model configured in `agent.config.ts` (typically a faster/cheaper model).\n\n```typescript\nimport { adk, z } from \"@botpress/runtime\";\n\n// Extract structured data from text\nconst contact = await adk.zai.extract(\n \"Contact John at john@example.com, phone 555-0100\",\n z.object({\n name: z.string(),\n email: z.string(),\n phone: z.string()\n })\n);\n// Returns: { name: \"John\", email: \"john@example.com\", phone: \"555-0100\" }\n\n// Check if text matches a condition (returns boolean)\nconst isSpam = await adk.zai.check(messageText, \"is spam or promotional\");\n\n// Label text with multiple criteria\nconst labels = await adk.zai.label(customerEmail, {\n spam: \"is spam\",\n urgent: \"needs immediate response\",\n complaint: \"expresses dissatisfaction\"\n});\n// Returns: { spam: false, urgent: true, complaint: true }\n\n// Summarize content\nconst summary = await adk.zai.summarize(longDocument, {\n length: 200,\n bulletPoints: true\n});\n\n// Answer questions from documents (with citations)\nconst result = await adk.zai.answer(docs, \"What is the refund policy?\");\nif (result.type === \"answer\") {\n console.log(result.answer);\n console.log(result.citations);\n}\n// Response types: \"answer\", \"ambiguous\", \"out_of_topic\", \"invalid_question\", \"missing_knowledge\"\n\n// Rate items on 1-5 scale\nconst scores = await adk.zai.rate(products, \"quality score\");\n\n// Sort by criteria\nconst sorted = await adk.zai.sort(tickets, \"by urgency, most urgent first\");\n\n// Group items semantically\nconst groups = await adk.zai.group(emails, {\n instructions: \"categorize by topic\"\n});\n\n// Rewrite text\nconst professional = await adk.zai.rewrite(\"hey wassup\", \"make it professional and friendly\");\n\n// Filter arrays\nconst activeUsers = await adk.zai.filter(users, \"have been active this month\");\n\n// Generate text\nconst blogPost = await adk.zai.text(\"Write about AI in healthcare\", {\n length: 1000,\n temperature: 0.7\n});\n\n// Patch code files\nconst patched = await adk.zai.patch(files, \"add JSDoc comments to all functions\");\n```\n\n**Zai Configuration:**\n```typescript\n// Create configured instance\nconst preciseZai = adk.zai.with({\n modelId: \"best\", // \"best\" | \"fast\" | custom model ID\n temperature: 0.1\n});\n\n// Enable active learning\nconst learningZai = adk.zai.learn(\"sentiment-analysis\");\n```\n\n**Docs:** https://www.botpress.com/docs/adk/zai/overview\n**GitHub:** https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/zai-complete-guide.md\n\n---\n\n## Integrations\n\n**When to add an Integration:**\n- You need to connect to an external service (Slack, Linear, GitHub, etc.)\n- You want to receive messages from a channel (webchat, WhatsApp, Discord)\n- You need to call external APIs with pre-built actions\n- You want to react to events from external systems\n\n**Integration workflow:**\n1. **Search** - Find integrations with `adk search <name>`\n2. **Add** - Install with `adk add <name>@<version>`\n3. **Configure** - Set up credentials in the UI at `http://localhost:3001/`\n4. **Use** - Call actions via `actions.<integration>.<action>()`\n\n**Making integration actions available to AI:**\n```typescript\n// Convert any integration action to an AI-callable tool\ntools: [actions.slack.sendMessage.asTool()]\n```\n\n**CLI commands:**\n```bash\nadk search slack # Find integrations\nadk add slack@latest # Add to project\nadk add slack --alias my-slack # Add with custom alias\nadk info slack --events # See available events\nadk list # List installed integrations\nadk upgrade slack # Update to latest\nadk remove slack # Remove integration\n```\n\n**Using integration actions:**\n```typescript\nimport { actions } from \"@botpress/runtime\";\n\n// Slack\nawait actions.slack.sendMessage({ channel: \"#general\", text: \"Hello!\" });\nawait actions.slack.addReaction({ channel: \"C123\", timestamp: \"123\", name: \"thumbsup\" });\n\n// Linear\nawait actions.linear.issueCreate({ teamId: \"123\", title: \"Bug report\", description: \"Details\" });\nconst { items } = await actions.linear.issueList({\n first: 10,\n filter: { state: { name: { eq: \"In Progress\" } } }\n});\n\n// GitHub\nawait actions.github.createIssue({ owner: \"org\", repo: \"repo\", title: \"Issue\" });\n\n// Browser (web scraping)\nconst results = await actions.browser.webSearch({ query: \"Botpress docs\", maxResults: 5 });\n\n// Make integration actions available to AI as tools\nawait execute({ tools: [actions.slack.sendMessage.asTool()] });\n```\n\n**Docs:** https://www.botpress.com/docs/adk/managing-integrations\n**GitHub:** https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/integration-actions.md\n\n---\n\n## State Management\n\n**Understanding the state hierarchy - choose the right level:**\n\n| State Level | Scope | Use For |\n|-------------|-------|---------|\n| `bot.state` | Global, all users | Feature flags, counters, maintenance mode |\n| `user.state` | Per user, all their conversations | User preferences, profile, tier |\n| `conversation.state` | Per conversation | Context, message count, active workflow |\n| `workflow.state` | Per workflow instance | Progress tracking, intermediate results |\n\n**State is automatically persisted** - just modify it and it saves.\n\nAccess and modify state from anywhere in your bot:\n\n```typescript\nimport { bot, user, conversation } from \"@botpress/runtime\";\n\n// Bot state - global, shared across all users\nbot.state.maintenanceMode = true;\nbot.state.totalConversations += 1;\n\n// User state - per user, persists across conversations\nuser.state.name = \"Alice\";\nuser.state.tier = \"pro\";\nuser.state.preferredLanguage = \"es\";\n\n// In handlers, state is passed as a parameter\nasync handler({ state }) {\n state.messageCount += 1; // Auto-persisted\n}\n\n// Tags - simple string key-value pairs for categorization\nuser.tags.source = \"website\";\nuser.tags.region = \"north-america\";\nconversation.tags.category = \"support\";\nconversation.tags.priority = \"high\";\n```\n\n**State Types:**\n- **Bot State** - Global, shared across all users and conversations\n- **User State** - Per-user, persists across all their conversations\n- **Conversation State** - Per-conversation, isolated between conversations\n- **Workflow State** - Per-workflow instance, persists across steps\n\n**Tags vs State:**\n- Use **Tags** for: categorization, simple strings, filtering/querying\n- Use **State** for: complex objects, arrays, nested data, business logic\n\n**GitHub:** https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/tags.md\n\n---\n\n## Context API\n\nAccess runtime services in any handler:\n\n```typescript\nimport { context } from \"@botpress/runtime\";\n\n// Always available\nconst client = context.get(\"client\"); // Botpress API client\nconst citations = context.get(\"citations\"); // Citation manager\nconst cognitive = context.get(\"cognitive\"); // LLM client\nconst logger = context.get(\"logger\"); // Structured logger\nconst botId = context.get(\"botId\"); // Current bot ID\nconst configuration = context.get(\"configuration\"); // Bot config\n\n// Conditionally available (use { optional: true })\nconst user = context.get(\"user\", { optional: true });\nconst conversation = context.get(\"conversation\", { optional: true });\nconst message = context.get(\"message\", { optional: true });\nconst workflow = context.get(\"workflow\", { optional: true });\nconst chat = context.get(\"chat\", { optional: true }); // Conversation transcript\n\nif (user) {\n console.log(`User: ${user.id}`);\n}\n```\n\n**GitHub:** https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/context-api.md\n\n---\n\n## CLI Quick Reference\n\n```bash\n# Project Lifecycle\nadk init <name> # Create new project\nadk login # Authenticate with Botpress\nadk dev # Start dev server (hot reload)\nadk dev --port 3000 # Custom port\nadk chat # Test in CLI\nadk build # Build for production\nadk deploy # Deploy to Botpress Cloud\nadk deploy --env production # Deploy to specific environment\n\n# Integration Management\nadk add <integration> # Add integration\nadk add slack@2.5.5 # Add specific version\nadk add slack --alias my-slack # Add with alias\nadk remove <integration> # Remove integration\nadk search <query> # Search integrations\nadk list # List installed integrations\nadk list --available # List all available\nadk info <name> # Integration details\nadk info <name> --events # Show available events\nadk upgrade <name> # Update integration\nadk upgrade # Interactive upgrade all\n\n# Knowledge & Assets\nadk kb sync --dev # Sync knowledge bases\nadk kb sync --prod --force # Force re-sync production\nadk assets sync # Sync static files\n\n# Advanced\nadk run <script.ts> # Run TypeScript script\nadk mcp # Start MCP server\nadk link --workspace ws_123 --bot bot_456 # Link to existing bot\n\n# Utilities\nadk self-upgrade # Update CLI\nadk telemetry --disable # Disable telemetry\nadk --help # Full CLI help\nadk <command> --help # Help for specific command\n```\n\n**Docs:** https://www.botpress.com/docs/adk/cli-reference\n**GitHub:** https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/cli.md\n\n---\n\n## Autonomous Execution with `execute()`\n\n**The `execute()` function is the core of ADK's AI capabilities.** It runs an autonomous AI agent that can:\n- Understand user intent from natural language\n- Decide which tools to call and when\n- Search knowledge bases for relevant information\n- Generate contextual responses\n- Loop through multiple tool calls until the task is complete\n\n**When to use execute():**\n- In conversation handlers to generate AI responses\n- In workflows when you need AI decision-making\n- Anywhere you want autonomous, multi-step AI behavior\n\n**Key parameters to configure:**\n- `instructions` - Tell the AI who it is and how to behave\n- `tools` - Give the AI capabilities (search, create, update, etc.)\n- `knowledge` - Ground the AI in your documentation\n- `exits` - Define structured output schemas for specific outcomes\n\nThe `execute()` function enables autonomous AI agent behavior:\n\n```typescript\nimport { Autonomous, z } from \"@botpress/runtime\";\n\n// Define custom tool\nconst searchTool = new Autonomous.Tool({\n name: \"search\",\n description: \"Search documentation\",\n input: z.object({ query: z.string() }),\n output: z.string(),\n handler: async ({ query }) => {\n // Search implementation\n return \"results...\";\n }\n});\n\n// Define exit (structured response)\nconst AnswerExit = new Autonomous.Exit({\n name: \"Answer\",\n description: \"Provide final answer to the user\",\n schema: z.object({\n answer: z.string(),\n confidence: z.number(),\n sources: z.array(z.string())\n })\n});\n\n// Execute AI with tools, knowledge, and exits\nconst result = await execute({\n instructions: \"Help the user with their request. Be helpful and concise.\",\n\n // Add tools\n tools: [\n searchTool,\n actions.linear.issueCreate.asTool()\n ],\n\n // Add knowledge bases\n knowledge: [DocsKnowledgeBase, FAQKnowledgeBase],\n\n // Define exits for structured outputs\n exits: [AnswerExit],\n\n // Model configuration\n model: \"openai:gpt-4o\",\n temperature: 0.7,\n iterations: 10, // Max tool call iterations\n\n // Hooks for monitoring\n hooks: {\n onBeforeTool: async ({ tool, input }) => {\n console.log(`Calling ${tool.name}`, input);\n return { input: { ...input, enhanced: true } }; // Modify input\n },\n onAfterTool: async ({ tool, output }) => {\n console.log(`Result:`, output);\n }\n }\n});\n\n// Handle structured exit\nif (result.is(AnswerExit)) {\n console.log(result.output.answer);\n console.log(result.output.sources);\n}\n```\n\n---\n\n## Troubleshooting\n\n| Error | Cause | Solution |\n|-------|-------|----------|\n| \"Cannot destructure property\" in Actions | Destructuring input directly in handler params | Use `async handler({ input, client })` then `const { field } = input` inside |\n| Table creation fails | Invalid table name or `id` defined | Remove `id` column, ensure name ends with \"Table\" |\n| Integration action not found | Integration not installed or configured | Run `adk list`, add with `adk add`, configure in UI at localhost:3001 |\n| Knowledge base not updating | KB not synced | Run `adk kb sync --dev` or `adk kb sync --force` |\n| Workflow not resuming | Dynamic step names | Use stable, unique step names (no `step(\\`item-${i}\\`)`) |\n| Types out of date | Generated types stale | Run `adk dev` or `adk build` to regenerate |\n| Can't message user from workflow | Missing conversationId | Pass `conversationId` when starting workflow, use `client.createMessage()` |\n| \"user is not defined\" | Accessing conversation context outside conversation | Use `context.get(\"user\", { optional: true })` |\n| State changes not persisting | Creating new objects instead of modifying | Modify state directly: `state.user.name = \"Alice\"` |\n| Tool not being used by AI | Poor description | Improve tool description, add detailed `.describe()` to inputs |\n\n**For more help:** Run `adk --help` or check:\n- **Docs:** https://www.botpress.com/docs/adk/\n- **GitHub:** https://github.com/botpress/skills/tree/master/skills/adk/references\n\n---\n\n## Common Patterns & Best Practices\n\n### 1. Always Pass conversationId for Workflows\n```typescript\n// In conversation - starting a workflow that needs to message back\nawait MyWorkflow.start({\n conversationId: conversation.id, // Always include this!\n data: \"...\"\n});\n\n// In workflow - messaging back to user\nawait client.createMessage({\n conversationId: input.conversationId,\n type: \"text\",\n payload: { text: \"Processing complete!\" }\n});\n```\n\n### 2. Use Environment Variables for Secrets\n```typescript\n// In .env (never commit!)\nAPI_KEY=sk-...\nSLACK_TOKEN=xoxb-...\n\n// In code\nconfig: { apiKey: process.env.API_KEY }\n```\n\n### 3. Keep Step Names Stable in Workflows\n```typescript\n// GOOD - Single step for batch\nawait step(\"process-all-items\", async () => {\n for (const item of items) {\n await processItem(item);\n }\n});\n\n// BAD - Dynamic names break resume\nfor (let i = 0; i < items.length; i++) {\n await step(`process-${i}`, async () => { ... }); // Don't do this!\n}\n```\n\n### 4. Error Handling in Actions/Tools\n```typescript\nexport default new Action({\n handler: async ({ input }) => {\n try {\n // Action logic\n return { success: true };\n } catch (error) {\n console.error(\"Action failed:\", error);\n throw new Error(`Failed to process: ${error.message}`);\n }\n }\n});\n```\n\n### 5. ThinkSignal for Tool Edge Cases\n```typescript\nhandler: async ({ query }) => {\n const results = await search(query);\n\n if (!results.length) {\n throw new Autonomous.ThinkSignal(\n \"No results\",\n \"No results found. Ask the user to try different search terms.\"\n );\n }\n\n return results;\n}\n```\n\n### 6. Multi-Channel Handling\n```typescript\nexport default new Conversation({\n channels: [\"slack.channel\", \"webchat.channel\"],\n handler: async ({ conversation }) => {\n const channel = conversation.channel;\n\n if (channel === \"slack.channel\") {\n // Slack-specific handling (threading, mentions, etc.)\n } else if (channel === \"webchat.channel\") {\n // Webchat-specific handling\n }\n }\n});\n```\n\n---\n\n## Complete Reference Documentation\n\n### Official Botpress ADK Documentation\n**Base URL:** https://www.botpress.com/docs/adk/\n\n| Topic | URL |\n|-------|-----|\n| Introduction | https://www.botpress.com/docs/adk/introduction |\n| Quickstart | https://www.botpress.com/docs/adk/quickstart |\n| Project Structure | https://www.botpress.com/docs/adk/project-structure |\n| Actions | https://www.botpress.com/docs/adk/concepts/actions |\n| Tools | https://www.botpress.com/docs/adk/concepts/tools |\n| Conversations | https://www.botpress.com/docs/adk/concepts/conversations |\n| Workflows Overview | https://www.botpress.com/docs/adk/concepts/workflows/overview |\n| Workflow Steps | https://www.botpress.com/docs/adk/concepts/workflows/steps |\n| Tables | https://www.botpress.com/docs/adk/concepts/tables |\n| Triggers | https://www.botpress.com/docs/adk/concepts/triggers |\n| Knowledge Bases | https://www.botpress.com/docs/adk/concepts/knowledge |\n| Managing Integrations | https://www.botpress.com/docs/adk/managing-integrations |\n| Zai Overview | https://www.botpress.com/docs/adk/zai/overview |\n| Zai Reference | https://www.botpress.com/docs/adk/zai/reference |\n| CLI Reference | https://www.botpress.com/docs/adk/cli-reference |\n\n### GitHub Repository References (AI-Optimized)\n**Base URL:** https://github.com/botpress/skills/tree/master/skills/adk/references\n\nFor detailed specifications beyond this guide, fetch the corresponding reference file:\n\n| Topic | Reference File |\n|-------|----------------|\n| Actions | https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/actions.md |\n| Tools | https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/tools.md |\n| Workflows | https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/workflows.md |\n| Conversations | https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/conversations.md |\n| Tables | https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/tables.md |\n| Triggers | https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/triggers.md |\n| Knowledge Bases | https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/knowledge-bases.md |\n| Messages | https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/messages.md |\n| Agent Config | https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/agent-config.md |\n| CLI | https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/cli.md |\n| Integration Actions | https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/integration-actions.md |\n| Model Configuration | https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/model-configuration.md |\n| Context API | https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/context-api.md |\n| Tags | https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/tags.md |\n| Files | https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/files.md |\n| Zai Complete Guide | https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/zai-complete-guide.md |\n| Zai Agent Reference | https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/zai-agent-reference.md |\n| MCP Server | https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/mcp-server.md |\n\n---\n\n## Common Scenarios - What to Build\n\n**\"I want to build a support bot that answers questions from our docs\"**\n1. Create a Knowledge Base with your documentation as a source\n2. Create a Conversation handler that uses `execute()` with that knowledge\n3. Add the `chat` integration for testing\n\n**\"I want the bot to create tickets in Linear when users report issues\"**\n1. Add the Linear integration: `adk add linear`\n2. Create a Tool that calls `actions.linear.issueCreate()`\n3. Pass the tool to `execute()` in your conversation\n\n**\"I need to run a daily sync job\"**\n1. Create a Workflow with `schedule: \"0 9 * * *\"` (cron syntax)\n2. Implement the sync logic in steps\n3. The workflow will run automatically at the scheduled time\n\n**\"I want to store user preferences\"**\n1. Define the schema in `agent.config.ts` under `user.state`\n2. Access/modify via `user.state.preferenceField = value`\n3. State persists automatically\n\n**\"I need to react when a new user signs up\"**\n1. Create a Trigger listening to `user.created` event\n2. In the handler, start an onboarding workflow or send a welcome message\n\n**\"I want to store order data and search it\"**\n1. Create a Table with your schema (remember: no `id` field, name ends with \"Table\")\n2. Use `searchable: true` on text columns you want to search\n3. Use CRUD methods: `createRows`, `findRows`, `updateRows`, `deleteRows`\n\n---\n\n## Summary\n\nThis skill provides comprehensive guidance for building Botpress bots using the ADK:\n\n- **Setup & Initialization** - ADK installation and project creation\n- **Project Structure** - Conventions, files, and organization\n- **Core Concepts** - Actions, Tools, Workflows, Conversations, Tables, Knowledge, Triggers\n- **State Management** - Bot, user, conversation, and workflow state\n- **Integration Management** - Adding and configuring integrations\n- **Zai (AI Operations)** - Extract, check, label, summarize, answer, sort, group, rewrite, filter\n- **CLI Reference** - Complete command guide\n- **Testing & Deployment** - Local testing and cloud deployment\n- **Common Patterns** - Best practices and troubleshooting\n\n**Core Principle:** The ADK is a convention-based framework where file location determines behavior. Place components in the correct `src/` subdirectory and they automatically become bot capabilities.\n\n**When to use this skill:**\n- User wants to create a new Botpress bot\n- User asks how to add actions, tools, workflows, conversations, tables, knowledge bases, or triggers\n- User needs help with integrations (Slack, Linear, GitHub, etc.)\n- User wants to understand ADK patterns and best practices\n- User has errors or needs troubleshooting\n- User asks about CLI commands, configuration, or deployment\n\n**Official Documentation:** https://www.botpress.com/docs/adk/\n**GitHub Repository:** https://github.com/botpress/adk\n**Skills Repository:** https://github.com/botpress/skills\n",49 "arguments": [],50 "icons": null,51 "meta": null52 },53 {54 "name": "README.md",55 "title": null,56 "description": "# Botpress ADK Skill\n\nA comprehensive skill for building AI bots with the Botpress Agent Development Kit (ADK).\n\n## What's Inside\n\n- `SKILL.md` - Complete ADK development guide with code examples and patterns\n\n## Quick Summary\n\nThis skill provides guidance for the entire Botpress ADK development lifecycle:\n\n- **Project Setup** - `adk init`, `adk login`, `adk link`\n- **Development** - `adk dev`, `adk chat`, hot reloading\n- **Core Concepts** - Actions, Tools, Workflows, Conversations, Tables, Triggers, Knowledge Bases\n- **Deployment** - `adk deploy`, production configuration\n- **Troubleshooting** - Common errors and fixes\n\n## Usage\n\nLoad this skill when working with Botpress ADK projects. It will help with:\n\n- Creating new bots from scratch\n- Adding features (actions, tools, workflows, etc.)\n- Configuring integrations (Slack, Linear, GitHub, etc.)\n- Debugging deployment issues\n- Understanding ADK conventions and patterns\n\n## Requirements\n\n- Node.js v22.0.0+\n- Botpress Account (https://app.botpress.cloud)\n- ADK CLI installed\n\n## Links\n\n- [Botpress ADK Documentation](https://www.botpress.com/docs/adk/)\n- [ADK GitHub Repository](https://github.com/botpress/adk)\n- [Botpress Skills Repository](https://github.com/botpress/skills)\n\n## License\n\nMIT License\n",57 "arguments": null,58 "icons": null,59 "meta": null60 }61 ],62 "resources": [63 {64 "name": "_meta.json",65 "title": null,66 "uri": "skill://_meta.json",67 "description": "{\n \"owner\": \"yueranlu\",\n \"slug\": \"botpress-adk\",\n \"displayName\": \"Create a Botpress AI agent with the ADK using OpenClaw\",\n \"latest\": {\n \"version\": \"1.0.1\",\n \"publishedAt\": 1770066884691,\n \"commit\": \"https://github.com/clawdbot/skills/commit/ea08470057cf35c0449adfb6a51727e6563687b3\"\n },\n \"history\": [\n {\n \"version\": \"1.0.0\",\n \"publishedAt\": 1769807188387,\n \"commit\": \"https://github.com/clawdbot/skills/commit/8eb9ea0e7efe0cb691bb5959c798073cc28af398\"\n }\n ]\n}\n",68 "mimeType": null,69 "size": null,70 "icons": null,71 "annotations": null,72 "meta": null73 }74 ],75 "resource_templates": [],76 "tools": []77 },78 "error": null79 }80 ],81 "issues": [82 {83 "code": "W004",84 "message": "The MCP server is not in our registry.",85 "reference": [86 0,87 null88 ],89 "extra_data": null90 }91 ],92 "labels": [93 [94 {95 "is_public_sink": 0,96 "destructive": 0,97 "untrusted_content": 0,98 "private_data": 099 },100 {101 "is_public_sink": 0,102 "destructive": 0,103 "untrusted_content": 0,104 "private_data": 0105 },106 {107 "is_public_sink": 0,108 "destructive": 0,109 "untrusted_content": 0,110 "private_data": 0111 }112 ]113 ],114 "error": null115 }116}117118Process exited with code 0119✓ Completed in 104869ms
npm-audit▸No package.json found — skipping npm audit
View logs
1No package.json found at /tmp/clawguard-scan-DpTogr/repo/skills/yueranlu/botpress-adk/package.json2Skipping npm audit.
Scanned: 2/12/2026, 10:45:25 PM