{
  "schemaVersion": "1.0",
  "item": {
    "slug": "botpress-adk",
    "name": "Create a Botpress AI agent with the ADK using OpenClaw",
    "source": "tencent",
    "type": "skill",
    "category": "AI 智能",
    "sourceUrl": "https://clawhub.ai/yueranlu/botpress-adk",
    "canonicalUrl": "https://clawhub.ai/yueranlu/botpress-adk",
    "targetPlatform": "OpenClaw"
  },
  "install": {
    "downloadMode": "redirect",
    "downloadUrl": "/downloads/botpress-adk",
    "sourceDownloadUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=botpress-adk",
    "sourcePlatform": "tencent",
    "targetPlatform": "OpenClaw",
    "installMethod": "Manual import",
    "extraction": "Extract archive",
    "prerequisites": [
      "OpenClaw"
    ],
    "packageFormat": "ZIP package",
    "includedAssets": [
      "README.md",
      "SKILL.md"
    ],
    "primaryDoc": "SKILL.md",
    "quickSetup": [
      "Download the package from Yavira.",
      "Extract the archive and review SKILL.md first.",
      "Import or place the package into your OpenClaw setup."
    ],
    "agentAssist": {
      "summary": "Hand the extracted package to your coding agent with a concrete install brief instead of figuring it out manually.",
      "steps": [
        "Download the package from Yavira.",
        "Extract it into a folder your agent can access.",
        "Paste one of the prompts below and point your agent at the extracted folder."
      ],
      "prompts": [
        {
          "label": "New install",
          "body": "I downloaded a skill package from Yavira. Read SKILL.md from the extracted folder and install it by following the included instructions. Then review README.md for any prerequisites, environment setup, or post-install checks. Tell me what you changed and call out any manual steps you could not complete."
        },
        {
          "label": "Upgrade existing",
          "body": "I downloaded an updated skill package from Yavira. Read SKILL.md from the extracted folder, compare it with my current installation, and upgrade it while preserving any custom configuration unless the package docs explicitly say otherwise. Then review README.md for any prerequisites, environment setup, or post-install checks. Summarize what changed and any follow-up checks I should run."
        }
      ]
    },
    "sourceHealth": {
      "source": "tencent",
      "status": "healthy",
      "reason": "direct_download_ok",
      "recommendedAction": "download",
      "checkedAt": "2026-04-30T16:55:25.780Z",
      "expiresAt": "2026-05-07T16:55:25.780Z",
      "httpStatus": 200,
      "finalUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=network",
      "contentType": "application/zip",
      "probeMethod": "head",
      "details": {
        "probeUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=network",
        "contentDisposition": "attachment; filename=\"network-1.0.0.zip\"",
        "redirectLocation": null,
        "bodySnippet": null
      },
      "scope": "source",
      "summary": "Source download looks usable.",
      "detail": "Yavira can redirect you to the upstream package for this source.",
      "primaryActionLabel": "Download for OpenClaw",
      "primaryActionHref": "/downloads/botpress-adk"
    },
    "validation": {
      "installChecklist": [
        "Use the Yavira download entry.",
        "Review SKILL.md after the package is downloaded.",
        "Confirm the extracted package contains the expected setup assets."
      ],
      "postInstallChecks": [
        "Confirm the extracted package includes the expected docs or setup files.",
        "Validate the skill or prompts are available in your target agent workspace.",
        "Capture any manual follow-up steps the agent could not complete."
      ]
    },
    "downloadPageUrl": "https://openagent3.xyz/downloads/botpress-adk",
    "agentPageUrl": "https://openagent3.xyz/skills/botpress-adk/agent",
    "manifestUrl": "https://openagent3.xyz/skills/botpress-adk/agent.json",
    "briefUrl": "https://openagent3.xyz/skills/botpress-adk/agent.md"
  },
  "agentAssist": {
    "summary": "Hand the extracted package to your coding agent with a concrete install brief instead of figuring it out manually.",
    "steps": [
      "Download the package from Yavira.",
      "Extract it into a folder your agent can access.",
      "Paste one of the prompts below and point your agent at the extracted folder."
    ],
    "prompts": [
      {
        "label": "New install",
        "body": "I downloaded a skill package from Yavira. Read SKILL.md from the extracted folder and install it by following the included instructions. Then review README.md for any prerequisites, environment setup, or post-install checks. Tell me what you changed and call out any manual steps you could not complete."
      },
      {
        "label": "Upgrade existing",
        "body": "I downloaded an updated skill package from Yavira. Read SKILL.md from the extracted folder, compare it with my current installation, and upgrade it while preserving any custom configuration unless the package docs explicitly say otherwise. Then review README.md for any prerequisites, environment setup, or post-install checks. Summarize what changed and any follow-up checks I should run."
      }
    ]
  },
  "documentation": {
    "source": "clawhub",
    "primaryDoc": "SKILL.md",
    "sections": [
      {
        "title": "Botpress ADK Development Guide",
        "body": "A comprehensive guide for building AI bots with the Botpress Agent Development Kit (ADK)."
      },
      {
        "title": "When to Use",
        "body": "User asks to build a Botpress bot or chatbot\nUser mentions ADK, Agent Development Kit, or Botpress\nUser wants to create actions, tools, workflows, conversations, tables, triggers, or knowledge bases\nUser needs help with adk CLI commands (init, dev, deploy, link)\nUser has ADK-related errors or needs troubleshooting\nUser asks about bot configuration, state management, or integrations"
      },
      {
        "title": "Quick Reference",
        "body": "The ADK is a convention-based TypeScript framework where file structure maps directly to bot behavior.\n\nYour 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\nKey principle: In ADK, where you put files matters. Each component type has a specific src/ subdirectory, and files are auto-discovered based on location."
      },
      {
        "title": "How to Use This Skill",
        "body": "This skill is your primary reference for building Botpress bots. When a user asks you to build something with the ADK:\n\nIdentify what they need - Is it a new bot, a feature (action, tool, workflow), data storage (table), or event handling (trigger)?\nCheck the correct directory - Each component type goes in a specific src/ subdirectory\nUse the patterns below - Follow the code examples exactly, they represent the correct ADK conventions\nRun adk --help - For CLI commands not covered here, or adk <command> --help for specific help\n\nDecision Guide - What Component to Create:\n\nUser Wants To...Create ThisLocationHandle user messagesConversationsrc/conversations/Add a function the AI can callToolsrc/tools/Add reusable business logicActionsrc/actions/Run background/scheduled tasksWorkflowsrc/workflows/Store structured dataTablesrc/tables/React to events (user created, etc.)Triggersrc/triggers/Give AI access to docs/dataKnowledge Basesrc/knowledge/Connect external service (Slack, etc.)Integrationadk add <name>\n\nIf the information in this skill isn't enough, fetch the corresponding GitHub reference file (links provided in each section) for more detailed specifications."
      },
      {
        "title": "Important: ADK is AI-Native",
        "body": "The ADK does NOT use traditional chatbot patterns. Don't create intents, entities, or dialog flows.\n\nInstead of:\n\nDefining intents (greet, orderPizza, checkStatus)\nTraining entity extraction (@pizzaSize, @toppings)\nManually routing to intent handlers\n\nADK uses:\n\nexecute() - The AI understands user intent naturally from instructions\nTools - AI autonomously decides when to call your functions\nzai.extract() - Schema-based structured data extraction\nKnowledge bases - RAG for grounding responses in your docs\n\nDocs: https://www.botpress.com/docs/adk/\nGitHub: https://github.com/botpress/skills/tree/master/skills/adk"
      },
      {
        "title": "Prerequisites & Installation",
        "body": "Before using the ADK, ensure the user has:\n\nBotpress Account - Create at https://app.botpress.cloud\nNode.js v22.0.0+ - Check with node --version\nPackage Manager - bun (recommended), pnpm, yarn, or npm\n\nInstall the ADK CLI:\n\nmacOS & Linux:\n\ncurl -fsSL https://github.com/botpress/adk/releases/latest/download/install.sh | bash\n\nWindows (PowerShell):\n\npowershell -c \"irm https://github.com/botpress/adk/releases/latest/download/install.ps1 | iex\"\n\nVerify installation:\n\nadk --version\n\nIf installation fails, check https://github.com/botpress/adk/releases for manual download options.\n\nDocs: https://www.botpress.com/docs/adk/quickstart\nGitHub: https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/cli.md"
      },
      {
        "title": "Quick Start",
        "body": "Once the ADK CLI is installed, create a new bot:\n\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\nThe visual console at http://localhost:3001/ lets you configure integrations and test the bot.\n\nDocs: https://www.botpress.com/docs/adk/quickstart\nGitHub: https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/cli.md"
      },
      {
        "title": "Linking and Deploying Your Bot",
        "body": "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."
      },
      {
        "title": "The Correct Order: Link → Dev → Deploy",
        "body": "Follow this order to get your bot working:\n\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\nStep-by-step:\n\nadk 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\n\nadk 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\n\nadk 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.)."
      },
      {
        "title": "Troubleshooting Errors",
        "body": "If you encounter errors when running adk dev or adk deploy:\n\nCheck the logs - Look at the terminal output or the logs panel in the dev console at http://localhost:3001\nCopy the error message - Select and copy the full error message from the logs\nAsk for help - Paste the error back to the AI assistant and ask it to help fix the issue\n\nCommon error scenarios:\n\nIntegration configuration errors: Usually means an integration needs to be configured in the UI at localhost:3001\nType errors: Often caused by incorrect imports or schema mismatches\nDeployment failures: May indicate missing environment variables or invalid configuration\n\nExample 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\nDocs: https://www.botpress.com/docs/adk/quickstart\nGitHub: https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/cli.md"
      },
      {
        "title": "Project Structure",
        "body": "Critical rule: File location determines behavior. Place components in the correct src/ subdirectory or they won't be discovered.\n\nmy-bot/\n├── agent.config.ts    # Bot configuration: name, models, state schemas, integrations\n├── agent.json         # Workspace/bot IDs (auto-generated by adk link/dev, add to .gitignore)\n├── package.json       # Node.js dependencies and scripts (dev, build, deploy)\n├── tsconfig.json      # TypeScript configuration\n├── .env               # API keys and secrets (never commit!)\n├── .gitignore         # Should include: agent.json, .env, node_modules/, .botpress/\n├── src/\n│   ├── conversations/ # Handle incoming messages → use execute() for AI responses\n│   ├── workflows/     # Background processes → use step() for resumable operations\n│   ├── actions/       # Reusable functions → call from anywhere with actions.name()\n│   ├── tools/         # AI-callable functions → AI decides when to invoke these\n│   ├── tables/        # Data storage → auto-synced to cloud, supports semantic search\n│   ├── triggers/      # Event handlers → react to user.created, integration events, etc.\n│   └── knowledge/     # RAG sources → index docs, websites, or tables for AI context\n└── .botpress/         # Auto-generated types (never edit manually)\n\nKey Configuration Files:\n\nagent.config.ts - Primary configuration defining bot metadata, AI models, state schemas, and integrations (you edit this)\nagent.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\npackage.json - Node.js config with @botpress/runtime dependency and scripts for dev, build, deploy\ntsconfig.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\nDocs: https://www.botpress.com/docs/adk/project-structure\nGitHub: https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/agent-config.md"
      },
      {
        "title": "Agent Configuration",
        "body": "The 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\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\nAvailable models:\n\nOpenAI: openai:gpt-4o, openai:gpt-4o-mini, openai:gpt-4-turbo\nAnthropic: anthropic:claude-3-5-sonnet, anthropic:claude-3-opus\nGoogle: google:gemini-1.5-pro, google:gemini-1.5-flash\n\nDocs: https://www.botpress.com/docs/adk/project-structure\nGitHub: https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/agent-config.md"
      },
      {
        "title": "1. Actions - Reusable Business Logic",
        "body": "When to create an Action:\n\nYou need reusable logic that will be called from multiple places (workflows, conversations, triggers)\nYou're wrapping an external API or database operation\nYou want testable, composable business logic\nYou need to call integration APIs (Slack, Linear, etc.) with custom logic\n\nWhen NOT to use an Action (use a Tool instead):\n\nYou want the AI to decide when to call it autonomously\nThe 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\nLocation: src/actions/*.ts\n\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\nCalling actions:\n\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\nKey Rules:\n\nHandler receives { input, client } - must destructure input inside the handler\nCannot destructure input fields directly in parameters\nCan call other actions, integration actions, access state\nCan be converted to tools with .asTool()\n\nDocs: https://www.botpress.com/docs/adk/concepts/actions\nGitHub: https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/actions.md"
      },
      {
        "title": "2. Tools - AI-Callable Functions",
        "body": "When to create a Tool:\n\nYou want the AI to autonomously decide when to use this function\nThe function retrieves information the AI needs (search, lookup, fetch)\nThe function performs actions on behalf of the user (create ticket, send message)\nYou're building capabilities the AI should have during conversations\n\nThe AI decides when to use tools based on:\n\nThe tool's description - Make this clear and specific about WHEN to use it\nThe input schema's .describe() fields - Help AI understand what parameters mean\nThe conversation context and user's intent\n\nKey difference from Actions: Tools can destructure input directly; Actions cannot.\n\nLocation: src/tools/*.ts\n\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\nUsing ThinkSignal: When a tool can't complete but you want to give the AI context:\n\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\nAdvanced Tool Properties:\n\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\nDocs: https://www.botpress.com/docs/adk/concepts/tools\nGitHub: https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/tools.md"
      },
      {
        "title": "3. Conversations - Message Handlers",
        "body": "When to create a Conversation:\n\nEvery bot needs at least one conversation handler to respond to users\nCreate separate handlers for different channels if they need different behavior\nUse channel: \"*\" to handle all channels with one handler\n\nKey decisions when building a conversation:\n\nWhich channels? - Specify \"*\" for all, or specific channels like \"slack.dm\"\nWhat tools does the AI need? - Pass them to execute({ tools: [...] })\nWhat knowledge should ground responses? - Pass to execute({ knowledge: [...] })\nWhat instructions guide the AI? - Define personality, rules, and context\n\nThe execute() function is the heart of ADK - it runs autonomous AI logic with your tools and knowledge. Most conversation handlers will call execute().\n\nLocation: src/conversations/*.ts\n\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\nHandler Context:\n\nmessage - User's message data\nexecute - Run autonomous AI logic\nconversation - Conversation instance methods (send, startTyping, stopTyping)\nstate - Mutable state (bot, user, conversation)\nclient - Botpress API client\ntype - Event classification (message, workflow_request)\n\nExecute Function Options:\n\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\nCommon channels: chat.channel, webchat.channel, slack.dm, slack.channel, discord.channel, whatsapp.channel, \"*\" (all)\n\nDocs: https://www.botpress.com/docs/adk/concepts/conversations\nGitHub: https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/conversations.md"
      },
      {
        "title": "4. Workflows - Background & Multi-Step Processes",
        "body": "When to create a Workflow:\n\nOperations that take longer than 2 minutes (the default timeout)\nMulti-step processes that need to survive crashes/restarts\nScheduled/recurring tasks (daily reports, periodic syncs)\nBackground processing (order fulfillment, data migration)\nOperations that need to wait for external events or user input\n\nWhen NOT to use a Workflow (handle in conversation instead):\n\nQuick operations that complete immediately\nSimple request-response patterns\nOperations that don't need persistence\n\nKey workflow concepts:\n\nSteps are checkpoints - If workflow crashes, it resumes from last completed step\nState persists - Store progress in state to track across steps\nAlways pass conversationId - If the workflow needs to message users back\n\nLocation: src/workflows/*.ts\n\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\nStep Methods:\n\nMethodPurposestep(name, fn)Basic execution with cachingstep.sleep(name, ms)Pause for millisecondsstep.sleepUntil(name, date)Pause until specific datestep.listen()Wait for external eventsstep.progress(msg)Update progress messagestep.request(name, prompt)Request user input (blocking)step.executeWorkflow()Start and await another workflowstep.waitForWorkflow(id)Wait for existing workflowstep.map(items, fn)Process array with concurrencystep.forEach(items, fn)Execute on items without resultsstep.batch(items, fn)Process in groupsstep.fail(reason)Mark workflow as failedstep.abort()Stop immediately without failure\n\nCritical Rules:\n\nStep names must be unique and stable (avoid dynamic naming in loops)\nState is passed as a parameter, not accessed via this.state\nAlways pass conversationId for workflows that need to message users\nDefault timeout is 2 minutes - use steps for longer processes\n\nDocs: https://www.botpress.com/docs/adk/concepts/workflows/overview\nGitHub: https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/workflows.md"
      },
      {
        "title": "5. Tables - Data Storage",
        "body": "When to create a Table:\n\nYou need to persist structured data (users, orders, tickets, logs)\nYou want to query/filter data by fields\nYou need semantic search on text content (set searchable: true)\nYou're storing data that should survive bot restarts\n\nWhen NOT to use a Table (use State instead):\n\nSimple key-value data per user/conversation → use user.state or conversation.state\nTemporary data that doesn't need persistence\nSmall amounts of data that fit in state\n\nTables vs Knowledge Bases:\n\nTables = Structured data you CRUD (create, read, update, delete)\nKnowledge Bases = Documents/content for AI to search and reference\n\nLocation: src/tables/*.ts\n\nCRITICAL RULES (violations will cause errors):\n\nDo NOT define an id column - it's created automatically as a number\nTable names MUST end with \"Table\" (e.g., OrdersTable, not Orders)\n\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\nCRUD operations:\n\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\nAdvanced: Computed Columns:\n\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\nDocs: https://www.botpress.com/docs/adk/concepts/tables\nGitHub: https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/tables.md"
      },
      {
        "title": "6. Knowledge Bases - RAG for AI Context",
        "body": "When to create a Knowledge Base:\n\nYou want the AI to answer questions based on your documentation\nYou have FAQs, policies, or product info the AI should reference\nYou want AI responses grounded in specific content (not hallucinated)\nYou're building a support bot that needs access to help articles\n\nHow RAG works in ADK:\n\nYou define knowledge sources (websites, files, tables)\nContent is indexed and embedded for semantic search\nDuring execute(), the AI automatically searches relevant knowledge\nAI uses retrieved content to generate grounded responses\n\nChoosing a DataSource type:\n\nWebsite - Index public documentation, help sites, blogs\nDirectory - Index local markdown/text files (dev only!)\nTable - Index structured data from your tables\n\nLocation: src/knowledge/*.ts\n\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\nWebsite Source Methods:\n\nfromSitemap(url, options) - Parse XML sitemap\nfromWebsite(baseUrl, options) - Crawl from base URL (requires Browser integration)\nfromLlmsTxt(url, options) - Parse llms.txt file\nfromUrls(urls, options) - Index specific URLs\n\nDocs: https://www.botpress.com/docs/adk/concepts/knowledge\nGitHub: https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/knowledge-bases.md"
      },
      {
        "title": "7. Triggers - Event-Driven Automation",
        "body": "When to create a Trigger:\n\nYou need to react to events automatically (user signs up, issue created, etc.)\nYou want to start workflows when specific events occur\nYou need to sync data when external systems change\nYou want to send notifications based on events\n\nCommon trigger patterns:\n\nUser onboarding - Trigger on user.created → start onboarding workflow\nIntegration sync - Trigger on linear:issueCreated → create record in table\nNotifications - Trigger on workflow.completed → send Slack message\n\nFinding available events:\n\nBot events: user.created, conversation.started, workflow.completed, etc.\nIntegration events: Run adk info <integration> --events to see available events\n\nLocation: src/triggers/*.ts\n\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\nCommon Bot Events:\n\nuser.created, user.updated, user.deleted\nconversation.started, conversation.ended, message.created\nworkflow.started, workflow.completed, workflow.failed\nbot.started, bot.stopped\n\nCommon Integration Events:\n\nSlack: slack:reactionAdded, slack:memberJoinedChannel\nLinear: linear:issueCreated, linear:issueUpdated\nGitHub: github:issueOpened, github:pullRequestOpened\nIntercom: intercom:conversationEvent, intercom:contactEvent\n\nFind integration events: Run adk info <integration> --events\n\nDocs: https://www.botpress.com/docs/adk/concepts/triggers\nGitHub: https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/triggers.md"
      },
      {
        "title": "Sending Messages",
        "body": "CRITICAL: The method depends on WHERE you're sending from:\n\nContextMethodWhyIn Conversationsconversation.send()Has conversation contextIn Workflows/Actionsclient.createMessage()Needs explicit conversationId\n\nCommon mistake: Using client.createMessage() in conversations. Always use conversation.send() instead.\n\nThe method depends on where you're sending from:\n\nIn conversations - Use conversation.send():\n\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\nIn workflows or actions - Use client.createMessage() with conversationId:\n\nawait client.createMessage({\n  conversationId: input.conversationId,  // Must have this!\n  type: \"text\",\n  payload: { text: \"Workflow complete!\" }\n});\n\nAll Message Types:\n\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\nGitHub: https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/messages.md"
      },
      {
        "title": "Zai - LLM Utility Operations",
        "body": "When to use Zai vs execute():\n\nUse zai for specific, structured AI operations (extract data, classify, summarize)\nUse execute() for autonomous, multi-turn AI conversations with tools\n\nZai is perfect for:\n\nExtracting structured data from user messages (zai.extract)\nClassifying/labeling content (zai.check, zai.label)\nSummarizing long content (zai.summarize)\nAnswering questions from documents (zai.answer)\nSorting/filtering/grouping data intelligently (zai.sort, zai.filter, zai.group)\n\nZai operations are optimized for speed and cost - they use the zai model configured in agent.config.ts (typically a faster/cheaper model).\n\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\nZai Configuration:\n\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\nDocs: https://www.botpress.com/docs/adk/zai/overview\nGitHub: https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/zai-complete-guide.md"
      },
      {
        "title": "Integrations",
        "body": "When to add an Integration:\n\nYou need to connect to an external service (Slack, Linear, GitHub, etc.)\nYou want to receive messages from a channel (webchat, WhatsApp, Discord)\nYou need to call external APIs with pre-built actions\nYou want to react to events from external systems\n\nIntegration workflow:\n\nSearch - Find integrations with adk search <name>\nAdd - Install with adk add <name>@<version>\nConfigure - Set up credentials in the UI at http://localhost:3001/\nUse - Call actions via actions.<integration>.<action>()\n\nMaking integration actions available to AI:\n\n// Convert any integration action to an AI-callable tool\ntools: [actions.slack.sendMessage.asTool()]\n\nCLI commands:\n\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\nUsing integration actions:\n\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\nDocs: https://www.botpress.com/docs/adk/managing-integrations\nGitHub: https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/integration-actions.md"
      },
      {
        "title": "State Management",
        "body": "Understanding the state hierarchy - choose the right level:\n\nState LevelScopeUse Forbot.stateGlobal, all usersFeature flags, counters, maintenance modeuser.statePer user, all their conversationsUser preferences, profile, tierconversation.statePer conversationContext, message count, active workflowworkflow.statePer workflow instanceProgress tracking, intermediate results\n\nState is automatically persisted - just modify it and it saves.\n\nAccess and modify state from anywhere in your bot:\n\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\nState Types:\n\nBot State - Global, shared across all users and conversations\nUser State - Per-user, persists across all their conversations\nConversation State - Per-conversation, isolated between conversations\nWorkflow State - Per-workflow instance, persists across steps\n\nTags vs State:\n\nUse Tags for: categorization, simple strings, filtering/querying\nUse State for: complex objects, arrays, nested data, business logic\n\nGitHub: https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/tags.md"
      },
      {
        "title": "Context API",
        "body": "Access runtime services in any handler:\n\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\nGitHub: https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/context-api.md"
      },
      {
        "title": "CLI Quick Reference",
        "body": "# 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\nDocs: https://www.botpress.com/docs/adk/cli-reference\nGitHub: https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/cli.md"
      },
      {
        "title": "Autonomous Execution with execute()",
        "body": "The execute() function is the core of ADK's AI capabilities. It runs an autonomous AI agent that can:\n\nUnderstand user intent from natural language\nDecide which tools to call and when\nSearch knowledge bases for relevant information\nGenerate contextual responses\nLoop through multiple tool calls until the task is complete\n\nWhen to use execute():\n\nIn conversation handlers to generate AI responses\nIn workflows when you need AI decision-making\nAnywhere you want autonomous, multi-step AI behavior\n\nKey parameters to configure:\n\ninstructions - Tell the AI who it is and how to behave\ntools - Give the AI capabilities (search, create, update, etc.)\nknowledge - Ground the AI in your documentation\nexits - Define structured output schemas for specific outcomes\n\nThe execute() function enables autonomous AI agent behavior:\n\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}"
      },
      {
        "title": "Troubleshooting",
        "body": "ErrorCauseSolution\"Cannot destructure property\" in ActionsDestructuring input directly in handler paramsUse async handler({ input, client }) then const { field } = input insideTable creation failsInvalid table name or id definedRemove id column, ensure name ends with \"Table\"Integration action not foundIntegration not installed or configuredRun adk list, add with adk add, configure in UI at localhost:3001Knowledge base not updatingKB not syncedRun adk kb sync --dev or adk kb sync --forceWorkflow not resumingDynamic step namesUse stable, unique step names (no step(\\item-${i}`)`)Types out of dateGenerated types staleRun adk dev or adk build to regenerateCan't message user from workflowMissing conversationIdPass conversationId when starting workflow, use client.createMessage()\"user is not defined\"Accessing conversation context outside conversationUse context.get(\"user\", { optional: true })State changes not persistingCreating new objects instead of modifyingModify state directly: state.user.name = \"Alice\"Tool not being used by AIPoor descriptionImprove tool description, add detailed .describe() to inputs\n\nFor more help: Run adk --help or check:\n\nDocs: https://www.botpress.com/docs/adk/\nGitHub: https://github.com/botpress/skills/tree/master/skills/adk/references"
      },
      {
        "title": "1. Always Pass conversationId for Workflows",
        "body": "// 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});"
      },
      {
        "title": "2. Use Environment Variables for Secrets",
        "body": "// In .env (never commit!)\nAPI_KEY=sk-...\nSLACK_TOKEN=xoxb-...\n\n// In code\nconfig: { apiKey: process.env.API_KEY }"
      },
      {
        "title": "3. Keep Step Names Stable in Workflows",
        "body": "// 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}"
      },
      {
        "title": "4. Error Handling in Actions/Tools",
        "body": "export 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});"
      },
      {
        "title": "5. ThinkSignal for Tool Edge Cases",
        "body": "handler: 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}"
      },
      {
        "title": "6. Multi-Channel Handling",
        "body": "export 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});"
      },
      {
        "title": "Official Botpress ADK Documentation",
        "body": "Base URL: https://www.botpress.com/docs/adk/\n\nTopicURLIntroductionhttps://www.botpress.com/docs/adk/introductionQuickstarthttps://www.botpress.com/docs/adk/quickstartProject Structurehttps://www.botpress.com/docs/adk/project-structureActionshttps://www.botpress.com/docs/adk/concepts/actionsToolshttps://www.botpress.com/docs/adk/concepts/toolsConversationshttps://www.botpress.com/docs/adk/concepts/conversationsWorkflows Overviewhttps://www.botpress.com/docs/adk/concepts/workflows/overviewWorkflow Stepshttps://www.botpress.com/docs/adk/concepts/workflows/stepsTableshttps://www.botpress.com/docs/adk/concepts/tablesTriggershttps://www.botpress.com/docs/adk/concepts/triggersKnowledge Baseshttps://www.botpress.com/docs/adk/concepts/knowledgeManaging Integrationshttps://www.botpress.com/docs/adk/managing-integrationsZai Overviewhttps://www.botpress.com/docs/adk/zai/overviewZai Referencehttps://www.botpress.com/docs/adk/zai/referenceCLI Referencehttps://www.botpress.com/docs/adk/cli-reference"
      },
      {
        "title": "GitHub Repository References (AI-Optimized)",
        "body": "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\nTopicReference FileActionshttps://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/actions.mdToolshttps://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/tools.mdWorkflowshttps://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/workflows.mdConversationshttps://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/conversations.mdTableshttps://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/tables.mdTriggershttps://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/triggers.mdKnowledge Baseshttps://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/knowledge-bases.mdMessageshttps://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/messages.mdAgent Confighttps://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/agent-config.mdCLIhttps://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/cli.mdIntegration Actionshttps://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/integration-actions.mdModel Configurationhttps://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/model-configuration.mdContext APIhttps://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/context-api.mdTagshttps://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/tags.mdFileshttps://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/files.mdZai Complete Guidehttps://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/zai-complete-guide.mdZai Agent Referencehttps://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/zai-agent-reference.mdMCP Serverhttps://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/mcp-server.md"
      },
      {
        "title": "Common Scenarios - What to Build",
        "body": "\"I want to build a support bot that answers questions from our docs\"\n\nCreate a Knowledge Base with your documentation as a source\nCreate a Conversation handler that uses execute() with that knowledge\nAdd the chat integration for testing\n\n\"I want the bot to create tickets in Linear when users report issues\"\n\nAdd the Linear integration: adk add linear\nCreate a Tool that calls actions.linear.issueCreate()\nPass the tool to execute() in your conversation\n\n\"I need to run a daily sync job\"\n\nCreate a Workflow with schedule: \"0 9 * * *\" (cron syntax)\nImplement the sync logic in steps\nThe workflow will run automatically at the scheduled time\n\n\"I want to store user preferences\"\n\nDefine the schema in agent.config.ts under user.state\nAccess/modify via user.state.preferenceField = value\nState persists automatically\n\n\"I need to react when a new user signs up\"\n\nCreate a Trigger listening to user.created event\nIn the handler, start an onboarding workflow or send a welcome message\n\n\"I want to store order data and search it\"\n\nCreate a Table with your schema (remember: no id field, name ends with \"Table\")\nUse searchable: true on text columns you want to search\nUse CRUD methods: createRows, findRows, updateRows, deleteRows"
      },
      {
        "title": "Summary",
        "body": "This skill provides comprehensive guidance for building Botpress bots using the ADK:\n\nSetup & Initialization - ADK installation and project creation\nProject Structure - Conventions, files, and organization\nCore Concepts - Actions, Tools, Workflows, Conversations, Tables, Knowledge, Triggers\nState Management - Bot, user, conversation, and workflow state\nIntegration Management - Adding and configuring integrations\nZai (AI Operations) - Extract, check, label, summarize, answer, sort, group, rewrite, filter\nCLI Reference - Complete command guide\nTesting & Deployment - Local testing and cloud deployment\nCommon Patterns - Best practices and troubleshooting\n\nCore 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\nWhen to use this skill:\n\nUser wants to create a new Botpress bot\nUser asks how to add actions, tools, workflows, conversations, tables, knowledge bases, or triggers\nUser needs help with integrations (Slack, Linear, GitHub, etc.)\nUser wants to understand ADK patterns and best practices\nUser has errors or needs troubleshooting\nUser asks about CLI commands, configuration, or deployment\n\nOfficial Documentation: https://www.botpress.com/docs/adk/\nGitHub Repository: https://github.com/botpress/adk\nSkills Repository: https://github.com/botpress/skills"
      }
    ],
    "body": "Botpress ADK Development Guide\n\nA comprehensive guide for building AI bots with the Botpress Agent Development Kit (ADK).\n\nWhen to Use\nUser asks to build a Botpress bot or chatbot\nUser mentions ADK, Agent Development Kit, or Botpress\nUser wants to create actions, tools, workflows, conversations, tables, triggers, or knowledge bases\nUser needs help with adk CLI commands (init, dev, deploy, link)\nUser has ADK-related errors or needs troubleshooting\nUser asks about bot configuration, state management, or integrations\nQuick Reference\n\nThe ADK is a convention-based TypeScript framework where file structure maps directly to bot behavior.\n\nYour 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\nKey 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\nHow to Use This Skill\n\nThis skill is your primary reference for building Botpress bots. When a user asks you to build something with the ADK:\n\nIdentify what they need - Is it a new bot, a feature (action, tool, workflow), data storage (table), or event handling (trigger)?\nCheck the correct directory - Each component type goes in a specific src/ subdirectory\nUse the patterns below - Follow the code examples exactly, they represent the correct ADK conventions\nRun adk --help - For CLI commands not covered here, or adk <command> --help for specific help\n\nDecision Guide - What Component to Create:\n\nUser Wants To...\tCreate This\tLocation\nHandle user messages\tConversation\tsrc/conversations/\nAdd a function the AI can call\tTool\tsrc/tools/\nAdd reusable business logic\tAction\tsrc/actions/\nRun background/scheduled tasks\tWorkflow\tsrc/workflows/\nStore structured data\tTable\tsrc/tables/\nReact to events (user created, etc.)\tTrigger\tsrc/triggers/\nGive AI access to docs/data\tKnowledge Base\tsrc/knowledge/\nConnect external service (Slack, etc.)\tIntegration\tadk add <name>\n\nIf the information in this skill isn't enough, fetch the corresponding GitHub reference file (links provided in each section) for more detailed specifications.\n\nImportant: ADK is AI-Native\n\nThe ADK does NOT use traditional chatbot patterns. Don't create intents, entities, or dialog flows.\n\nInstead of:\n\nDefining intents (greet, orderPizza, checkStatus)\nTraining entity extraction (@pizzaSize, @toppings)\nManually routing to intent handlers\n\nADK uses:\n\nexecute() - The AI understands user intent naturally from instructions\nTools - AI autonomously decides when to call your functions\nzai.extract() - Schema-based structured data extraction\nKnowledge bases - RAG for grounding responses in your docs\n\nDocs: https://www.botpress.com/docs/adk/ GitHub: https://github.com/botpress/skills/tree/master/skills/adk\n\nPrerequisites & Installation\n\nBefore using the ADK, ensure the user has:\n\nBotpress Account - Create at https://app.botpress.cloud\nNode.js v22.0.0+ - Check with node --version\nPackage Manager - bun (recommended), pnpm, yarn, or npm\n\nInstall the ADK CLI:\n\nmacOS & Linux:\n\ncurl -fsSL https://github.com/botpress/adk/releases/latest/download/install.sh | bash\n\n\nWindows (PowerShell):\n\npowershell -c \"irm https://github.com/botpress/adk/releases/latest/download/install.ps1 | iex\"\n\n\nVerify installation:\n\nadk --version\n\n\nIf installation fails, check https://github.com/botpress/adk/releases for manual download options.\n\nDocs: https://www.botpress.com/docs/adk/quickstart GitHub: https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/cli.md\n\nQuick Start\n\nOnce the ADK CLI is installed, create a new bot:\n\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\nDocs: https://www.botpress.com/docs/adk/quickstart GitHub: https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/cli.md\n\nLinking and Deploying Your Bot\n\nIMPORTANT: 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\nThe Correct Order: Link → Dev → Deploy\n\nFollow this order to get your bot working:\n\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\nStep-by-step:\n\nadk 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\nadk 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\nadk 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\nTroubleshooting Errors\n\nIf you encounter errors when running adk dev or adk deploy:\n\nCheck the logs - Look at the terminal output or the logs panel in the dev console at http://localhost:3001\nCopy the error message - Select and copy the full error message from the logs\nAsk for help - Paste the error back to the AI assistant and ask it to help fix the issue\n\nCommon error scenarios:\n\nIntegration configuration errors: Usually means an integration needs to be configured in the UI at localhost:3001\nType errors: Often caused by incorrect imports or schema mismatches\nDeployment failures: May indicate missing environment variables or invalid configuration\n\nExample 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\nDocs: https://www.botpress.com/docs/adk/quickstart GitHub: https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/cli.md\n\nProject Structure\n\nCritical rule: File location determines behavior. Place components in the correct src/ subdirectory or they won't be discovered.\n\nmy-bot/\n├── agent.config.ts    # Bot configuration: name, models, state schemas, integrations\n├── agent.json         # Workspace/bot IDs (auto-generated by adk link/dev, add to .gitignore)\n├── package.json       # Node.js dependencies and scripts (dev, build, deploy)\n├── tsconfig.json      # TypeScript configuration\n├── .env               # API keys and secrets (never commit!)\n├── .gitignore         # Should include: agent.json, .env, node_modules/, .botpress/\n├── src/\n│   ├── conversations/ # Handle incoming messages → use execute() for AI responses\n│   ├── workflows/     # Background processes → use step() for resumable operations\n│   ├── actions/       # Reusable functions → call from anywhere with actions.name()\n│   ├── tools/         # AI-callable functions → AI decides when to invoke these\n│   ├── tables/        # Data storage → auto-synced to cloud, supports semantic search\n│   ├── triggers/      # Event handlers → react to user.created, integration events, etc.\n│   └── knowledge/     # RAG sources → index docs, websites, or tables for AI context\n└── .botpress/         # Auto-generated types (never edit manually)\n\n\nKey Configuration Files:\n\nagent.config.ts - Primary configuration defining bot metadata, AI models, state schemas, and integrations (you edit this)\nagent.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\npackage.json - Node.js config with @botpress/runtime dependency and scripts for dev, build, deploy\ntsconfig.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\nDocs: https://www.botpress.com/docs/adk/project-structure GitHub: https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/agent-config.md\n\nAgent 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\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\nAvailable models:\n\nOpenAI: openai:gpt-4o, openai:gpt-4o-mini, openai:gpt-4-turbo\nAnthropic: anthropic:claude-3-5-sonnet, anthropic:claude-3-opus\nGoogle: google:gemini-1.5-pro, google:gemini-1.5-flash\n\nDocs: https://www.botpress.com/docs/adk/project-structure GitHub: https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/agent-config.md\n\nCore Concepts\n1. Actions - Reusable Business Logic\n\nWhen to create an Action:\n\nYou need reusable logic that will be called from multiple places (workflows, conversations, triggers)\nYou're wrapping an external API or database operation\nYou want testable, composable business logic\nYou need to call integration APIs (Slack, Linear, etc.) with custom logic\n\nWhen NOT to use an Action (use a Tool instead):\n\nYou want the AI to decide when to call it autonomously\nThe 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\nLocation: src/actions/*.ts\n\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\nCalling actions:\n\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\nKey Rules:\n\nHandler receives { input, client } - must destructure input inside the handler\nCannot destructure input fields directly in parameters\nCan call other actions, integration actions, access state\nCan be converted to tools with .asTool()\n\nDocs: https://www.botpress.com/docs/adk/concepts/actions GitHub: https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/actions.md\n\n2. Tools - AI-Callable Functions\n\nWhen to create a Tool:\n\nYou want the AI to autonomously decide when to use this function\nThe function retrieves information the AI needs (search, lookup, fetch)\nThe function performs actions on behalf of the user (create ticket, send message)\nYou're building capabilities the AI should have during conversations\n\nThe AI decides when to use tools based on:\n\nThe tool's description - Make this clear and specific about WHEN to use it\nThe input schema's .describe() fields - Help AI understand what parameters mean\nThe conversation context and user's intent\n\nKey difference from Actions: Tools can destructure input directly; Actions cannot.\n\nLocation: src/tools/*.ts\n\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\nUsing ThinkSignal: When a tool can't complete but you want to give the AI context:\n\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\nAdvanced Tool Properties:\n\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\nDocs: https://www.botpress.com/docs/adk/concepts/tools GitHub: https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/tools.md\n\n3. Conversations - Message Handlers\n\nWhen to create a Conversation:\n\nEvery bot needs at least one conversation handler to respond to users\nCreate separate handlers for different channels if they need different behavior\nUse channel: \"*\" to handle all channels with one handler\n\nKey decisions when building a conversation:\n\nWhich channels? - Specify \"*\" for all, or specific channels like \"slack.dm\"\nWhat tools does the AI need? - Pass them to execute({ tools: [...] })\nWhat knowledge should ground responses? - Pass to execute({ knowledge: [...] })\nWhat instructions guide the AI? - Define personality, rules, and context\n\nThe execute() function is the heart of ADK - it runs autonomous AI logic with your tools and knowledge. Most conversation handlers will call execute().\n\nLocation: src/conversations/*.ts\n\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\nHandler Context:\n\nmessage - User's message data\nexecute - Run autonomous AI logic\nconversation - Conversation instance methods (send, startTyping, stopTyping)\nstate - Mutable state (bot, user, conversation)\nclient - Botpress API client\ntype - Event classification (message, workflow_request)\n\nExecute Function Options:\n\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\nCommon channels: chat.channel, webchat.channel, slack.dm, slack.channel, discord.channel, whatsapp.channel, \"*\" (all)\n\nDocs: https://www.botpress.com/docs/adk/concepts/conversations GitHub: https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/conversations.md\n\n4. Workflows - Background & Multi-Step Processes\n\nWhen to create a Workflow:\n\nOperations that take longer than 2 minutes (the default timeout)\nMulti-step processes that need to survive crashes/restarts\nScheduled/recurring tasks (daily reports, periodic syncs)\nBackground processing (order fulfillment, data migration)\nOperations that need to wait for external events or user input\n\nWhen NOT to use a Workflow (handle in conversation instead):\n\nQuick operations that complete immediately\nSimple request-response patterns\nOperations that don't need persistence\n\nKey workflow concepts:\n\nSteps are checkpoints - If workflow crashes, it resumes from last completed step\nState persists - Store progress in state to track across steps\nAlways pass conversationId - If the workflow needs to message users back\n\nLocation: src/workflows/*.ts\n\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\nStep Methods:\n\nMethod\tPurpose\nstep(name, fn)\tBasic execution with caching\nstep.sleep(name, ms)\tPause for milliseconds\nstep.sleepUntil(name, date)\tPause until specific date\nstep.listen()\tWait for external events\nstep.progress(msg)\tUpdate progress message\nstep.request(name, prompt)\tRequest user input (blocking)\nstep.executeWorkflow()\tStart and await another workflow\nstep.waitForWorkflow(id)\tWait for existing workflow\nstep.map(items, fn)\tProcess array with concurrency\nstep.forEach(items, fn)\tExecute on items without results\nstep.batch(items, fn)\tProcess in groups\nstep.fail(reason)\tMark workflow as failed\nstep.abort()\tStop immediately without failure\n\nCritical Rules:\n\nStep names must be unique and stable (avoid dynamic naming in loops)\nState is passed as a parameter, not accessed via this.state\nAlways pass conversationId for workflows that need to message users\nDefault timeout is 2 minutes - use steps for longer processes\n\nDocs: https://www.botpress.com/docs/adk/concepts/workflows/overview GitHub: https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/workflows.md\n\n5. Tables - Data Storage\n\nWhen to create a Table:\n\nYou need to persist structured data (users, orders, tickets, logs)\nYou want to query/filter data by fields\nYou need semantic search on text content (set searchable: true)\nYou're storing data that should survive bot restarts\n\nWhen NOT to use a Table (use State instead):\n\nSimple key-value data per user/conversation → use user.state or conversation.state\nTemporary data that doesn't need persistence\nSmall amounts of data that fit in state\n\nTables vs Knowledge Bases:\n\nTables = Structured data you CRUD (create, read, update, delete)\nKnowledge Bases = Documents/content for AI to search and reference\n\nLocation: src/tables/*.ts\n\nCRITICAL RULES (violations will cause errors):\n\nDo NOT define an id column - it's created automatically as a number\nTable names MUST end with \"Table\" (e.g., OrdersTable, not Orders)\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\nCRUD operations:\n\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\nAdvanced: Computed Columns:\n\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\nDocs: https://www.botpress.com/docs/adk/concepts/tables GitHub: https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/tables.md\n\n6. Knowledge Bases - RAG for AI Context\n\nWhen to create a Knowledge Base:\n\nYou want the AI to answer questions based on your documentation\nYou have FAQs, policies, or product info the AI should reference\nYou want AI responses grounded in specific content (not hallucinated)\nYou're building a support bot that needs access to help articles\n\nHow RAG works in ADK:\n\nYou define knowledge sources (websites, files, tables)\nContent is indexed and embedded for semantic search\nDuring execute(), the AI automatically searches relevant knowledge\nAI uses retrieved content to generate grounded responses\n\nChoosing a DataSource type:\n\nWebsite - Index public documentation, help sites, blogs\nDirectory - Index local markdown/text files (dev only!)\nTable - Index structured data from your tables\n\nLocation: src/knowledge/*.ts\n\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\nWebsite Source Methods:\n\nfromSitemap(url, options) - Parse XML sitemap\nfromWebsite(baseUrl, options) - Crawl from base URL (requires Browser integration)\nfromLlmsTxt(url, options) - Parse llms.txt file\nfromUrls(urls, options) - Index specific URLs\n\nDocs: https://www.botpress.com/docs/adk/concepts/knowledge GitHub: https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/knowledge-bases.md\n\n7. Triggers - Event-Driven Automation\n\nWhen to create a Trigger:\n\nYou need to react to events automatically (user signs up, issue created, etc.)\nYou want to start workflows when specific events occur\nYou need to sync data when external systems change\nYou want to send notifications based on events\n\nCommon trigger patterns:\n\nUser onboarding - Trigger on user.created → start onboarding workflow\nIntegration sync - Trigger on linear:issueCreated → create record in table\nNotifications - Trigger on workflow.completed → send Slack message\n\nFinding available events:\n\nBot events: user.created, conversation.started, workflow.completed, etc.\nIntegration events: Run adk info <integration> --events to see available events\n\nLocation: src/triggers/*.ts\n\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\nCommon Bot Events:\n\nuser.created, user.updated, user.deleted\nconversation.started, conversation.ended, message.created\nworkflow.started, workflow.completed, workflow.failed\nbot.started, bot.stopped\n\nCommon Integration Events:\n\nSlack: slack:reactionAdded, slack:memberJoinedChannel\nLinear: linear:issueCreated, linear:issueUpdated\nGitHub: github:issueOpened, github:pullRequestOpened\nIntercom: intercom:conversationEvent, intercom:contactEvent\n\nFind integration events: Run adk info <integration> --events\n\nDocs: https://www.botpress.com/docs/adk/concepts/triggers GitHub: https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/triggers.md\n\nSending Messages\n\nCRITICAL: The method depends on WHERE you're sending from:\n\nContext\tMethod\tWhy\nIn Conversations\tconversation.send()\tHas conversation context\nIn Workflows/Actions\tclient.createMessage()\tNeeds explicit conversationId\n\nCommon mistake: Using client.createMessage() in conversations. Always use conversation.send() instead.\n\nThe method depends on where you're sending from:\n\nIn conversations - Use conversation.send():\n\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\nIn workflows or actions - Use client.createMessage() with conversationId:\n\nawait client.createMessage({\n  conversationId: input.conversationId,  // Must have this!\n  type: \"text\",\n  payload: { text: \"Workflow complete!\" }\n});\n\n\nAll Message Types:\n\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\nGitHub: https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/messages.md\n\nZai - LLM Utility Operations\n\nWhen to use Zai vs execute():\n\nUse zai for specific, structured AI operations (extract data, classify, summarize)\nUse execute() for autonomous, multi-turn AI conversations with tools\n\nZai is perfect for:\n\nExtracting structured data from user messages (zai.extract)\nClassifying/labeling content (zai.check, zai.label)\nSummarizing long content (zai.summarize)\nAnswering questions from documents (zai.answer)\nSorting/filtering/grouping data intelligently (zai.sort, zai.filter, zai.group)\n\nZai operations are optimized for speed and cost - they use the zai model configured in agent.config.ts (typically a faster/cheaper model).\n\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\nZai Configuration:\n\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\nDocs: https://www.botpress.com/docs/adk/zai/overview GitHub: https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/zai-complete-guide.md\n\nIntegrations\n\nWhen to add an Integration:\n\nYou need to connect to an external service (Slack, Linear, GitHub, etc.)\nYou want to receive messages from a channel (webchat, WhatsApp, Discord)\nYou need to call external APIs with pre-built actions\nYou want to react to events from external systems\n\nIntegration workflow:\n\nSearch - Find integrations with adk search <name>\nAdd - Install with adk add <name>@<version>\nConfigure - Set up credentials in the UI at http://localhost:3001/\nUse - Call actions via actions.<integration>.<action>()\n\nMaking integration actions available to AI:\n\n// Convert any integration action to an AI-callable tool\ntools: [actions.slack.sendMessage.asTool()]\n\n\nCLI commands:\n\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\nUsing integration actions:\n\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\nDocs: https://www.botpress.com/docs/adk/managing-integrations GitHub: https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/integration-actions.md\n\nState Management\n\nUnderstanding the state hierarchy - choose the right level:\n\nState Level\tScope\tUse For\nbot.state\tGlobal, all users\tFeature flags, counters, maintenance mode\nuser.state\tPer user, all their conversations\tUser preferences, profile, tier\nconversation.state\tPer conversation\tContext, message count, active workflow\nworkflow.state\tPer workflow instance\tProgress tracking, intermediate results\n\nState is automatically persisted - just modify it and it saves.\n\nAccess and modify state from anywhere in your bot:\n\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\nState Types:\n\nBot State - Global, shared across all users and conversations\nUser State - Per-user, persists across all their conversations\nConversation State - Per-conversation, isolated between conversations\nWorkflow State - Per-workflow instance, persists across steps\n\nTags vs State:\n\nUse Tags for: categorization, simple strings, filtering/querying\nUse State for: complex objects, arrays, nested data, business logic\n\nGitHub: https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/tags.md\n\nContext API\n\nAccess runtime services in any handler:\n\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\nGitHub: https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/context-api.md\n\nCLI Quick Reference\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\nDocs: https://www.botpress.com/docs/adk/cli-reference GitHub: https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/cli.md\n\nAutonomous Execution with execute()\n\nThe execute() function is the core of ADK's AI capabilities. It runs an autonomous AI agent that can:\n\nUnderstand user intent from natural language\nDecide which tools to call and when\nSearch knowledge bases for relevant information\nGenerate contextual responses\nLoop through multiple tool calls until the task is complete\n\nWhen to use execute():\n\nIn conversation handlers to generate AI responses\nIn workflows when you need AI decision-making\nAnywhere you want autonomous, multi-step AI behavior\n\nKey parameters to configure:\n\ninstructions - Tell the AI who it is and how to behave\ntools - Give the AI capabilities (search, create, update, etc.)\nknowledge - Ground the AI in your documentation\nexits - Define structured output schemas for specific outcomes\n\nThe execute() function enables autonomous AI agent behavior:\n\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\nTroubleshooting\nError\tCause\tSolution\n\"Cannot destructure property\" in Actions\tDestructuring input directly in handler params\tUse async handler({ input, client }) then const { field } = input inside\nTable creation fails\tInvalid table name or id defined\tRemove id column, ensure name ends with \"Table\"\nIntegration action not found\tIntegration not installed or configured\tRun adk list, add with adk add, configure in UI at localhost:3001\nKnowledge base not updating\tKB not synced\tRun adk kb sync --dev or adk kb sync --force\nWorkflow not resuming\tDynamic step names\tUse stable, unique step names (no step(\\item-${i}`)`)\nTypes out of date\tGenerated types stale\tRun adk dev or adk build to regenerate\nCan't message user from workflow\tMissing conversationId\tPass conversationId when starting workflow, use client.createMessage()\n\"user is not defined\"\tAccessing conversation context outside conversation\tUse context.get(\"user\", { optional: true })\nState changes not persisting\tCreating new objects instead of modifying\tModify state directly: state.user.name = \"Alice\"\nTool not being used by AI\tPoor description\tImprove tool description, add detailed .describe() to inputs\n\nFor more help: Run adk --help or check:\n\nDocs: https://www.botpress.com/docs/adk/\nGitHub: https://github.com/botpress/skills/tree/master/skills/adk/references\nCommon Patterns & Best Practices\n1. Always Pass conversationId for Workflows\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\n2. Use Environment Variables for Secrets\n// In .env (never commit!)\nAPI_KEY=sk-...\nSLACK_TOKEN=xoxb-...\n\n// In code\nconfig: { apiKey: process.env.API_KEY }\n\n3. Keep Step Names Stable in Workflows\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\n4. Error Handling in Actions/Tools\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\n5. ThinkSignal for Tool Edge Cases\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\n6. Multi-Channel Handling\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\nComplete Reference Documentation\nOfficial Botpress ADK Documentation\n\nBase URL: https://www.botpress.com/docs/adk/\n\nTopic\tURL\nIntroduction\thttps://www.botpress.com/docs/adk/introduction\nQuickstart\thttps://www.botpress.com/docs/adk/quickstart\nProject Structure\thttps://www.botpress.com/docs/adk/project-structure\nActions\thttps://www.botpress.com/docs/adk/concepts/actions\nTools\thttps://www.botpress.com/docs/adk/concepts/tools\nConversations\thttps://www.botpress.com/docs/adk/concepts/conversations\nWorkflows Overview\thttps://www.botpress.com/docs/adk/concepts/workflows/overview\nWorkflow Steps\thttps://www.botpress.com/docs/adk/concepts/workflows/steps\nTables\thttps://www.botpress.com/docs/adk/concepts/tables\nTriggers\thttps://www.botpress.com/docs/adk/concepts/triggers\nKnowledge Bases\thttps://www.botpress.com/docs/adk/concepts/knowledge\nManaging Integrations\thttps://www.botpress.com/docs/adk/managing-integrations\nZai Overview\thttps://www.botpress.com/docs/adk/zai/overview\nZai Reference\thttps://www.botpress.com/docs/adk/zai/reference\nCLI Reference\thttps://www.botpress.com/docs/adk/cli-reference\nGitHub Repository References (AI-Optimized)\n\nBase URL: https://github.com/botpress/skills/tree/master/skills/adk/references\n\nFor detailed specifications beyond this guide, fetch the corresponding reference file:\n\nTopic\tReference File\nActions\thttps://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/actions.md\nTools\thttps://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/tools.md\nWorkflows\thttps://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/workflows.md\nConversations\thttps://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/conversations.md\nTables\thttps://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/tables.md\nTriggers\thttps://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/triggers.md\nKnowledge Bases\thttps://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/knowledge-bases.md\nMessages\thttps://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/messages.md\nAgent Config\thttps://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/agent-config.md\nCLI\thttps://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/cli.md\nIntegration Actions\thttps://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/integration-actions.md\nModel Configuration\thttps://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/model-configuration.md\nContext API\thttps://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/context-api.md\nTags\thttps://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/tags.md\nFiles\thttps://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/files.md\nZai Complete Guide\thttps://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/zai-complete-guide.md\nZai Agent Reference\thttps://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/zai-agent-reference.md\nMCP Server\thttps://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/mcp-server.md\nCommon Scenarios - What to Build\n\n\"I want to build a support bot that answers questions from our docs\"\n\nCreate a Knowledge Base with your documentation as a source\nCreate a Conversation handler that uses execute() with that knowledge\nAdd the chat integration for testing\n\n\"I want the bot to create tickets in Linear when users report issues\"\n\nAdd the Linear integration: adk add linear\nCreate a Tool that calls actions.linear.issueCreate()\nPass the tool to execute() in your conversation\n\n\"I need to run a daily sync job\"\n\nCreate a Workflow with schedule: \"0 9 * * *\" (cron syntax)\nImplement the sync logic in steps\nThe workflow will run automatically at the scheduled time\n\n\"I want to store user preferences\"\n\nDefine the schema in agent.config.ts under user.state\nAccess/modify via user.state.preferenceField = value\nState persists automatically\n\n\"I need to react when a new user signs up\"\n\nCreate a Trigger listening to user.created event\nIn the handler, start an onboarding workflow or send a welcome message\n\n\"I want to store order data and search it\"\n\nCreate a Table with your schema (remember: no id field, name ends with \"Table\")\nUse searchable: true on text columns you want to search\nUse CRUD methods: createRows, findRows, updateRows, deleteRows\nSummary\n\nThis skill provides comprehensive guidance for building Botpress bots using the ADK:\n\nSetup & Initialization - ADK installation and project creation\nProject Structure - Conventions, files, and organization\nCore Concepts - Actions, Tools, Workflows, Conversations, Tables, Knowledge, Triggers\nState Management - Bot, user, conversation, and workflow state\nIntegration Management - Adding and configuring integrations\nZai (AI Operations) - Extract, check, label, summarize, answer, sort, group, rewrite, filter\nCLI Reference - Complete command guide\nTesting & Deployment - Local testing and cloud deployment\nCommon Patterns - Best practices and troubleshooting\n\nCore 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\nWhen to use this skill:\n\nUser wants to create a new Botpress bot\nUser asks how to add actions, tools, workflows, conversations, tables, knowledge bases, or triggers\nUser needs help with integrations (Slack, Linear, GitHub, etc.)\nUser wants to understand ADK patterns and best practices\nUser has errors or needs troubleshooting\nUser asks about CLI commands, configuration, or deployment\n\nOfficial Documentation: https://www.botpress.com/docs/adk/ GitHub Repository: https://github.com/botpress/adk Skills Repository: https://github.com/botpress/skills"
  },
  "trust": {
    "sourceLabel": "tencent",
    "provenanceUrl": "https://clawhub.ai/yueranlu/botpress-adk",
    "publisherUrl": "https://clawhub.ai/yueranlu/botpress-adk",
    "owner": "yueranlu",
    "version": "1.0.1",
    "license": null,
    "verificationStatus": "Indexed source record"
  },
  "links": {
    "detailUrl": "https://openagent3.xyz/skills/botpress-adk",
    "downloadUrl": "https://openagent3.xyz/downloads/botpress-adk",
    "agentUrl": "https://openagent3.xyz/skills/botpress-adk/agent",
    "manifestUrl": "https://openagent3.xyz/skills/botpress-adk/agent.json",
    "briefUrl": "https://openagent3.xyz/skills/botpress-adk/agent.md"
  }
}