@botpress/adk 1.11.8 → 1.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -654,7 +654,7 @@ var PRETTIER_CONFIG, formatCode = async (code, filepath) => {
654
654
  `));
655
655
  return code;
656
656
  }
657
- }, ADK_VERSION = "1.11.8", relative2 = (from, to) => {
657
+ }, ADK_VERSION = "1.12.0", relative2 = (from, to) => {
658
658
  const fromDir = path10.dirname(from);
659
659
  const relative3 = path10.relative(fromDir, to);
660
660
  return relative3.startsWith(".") ? relative3 : `./${relative3}`;
@@ -797,7 +797,7 @@ var init_integration_action_types = __esm(() => {
797
797
  var require_package = __commonJS((exports, module) => {
798
798
  module.exports = {
799
799
  name: "@botpress/adk",
800
- version: "1.11.8",
800
+ version: "1.12.0",
801
801
  description: "Core ADK library for building AI agents on Botpress",
802
802
  type: "module",
803
803
  main: "dist/index.js",
@@ -844,7 +844,7 @@ var require_package = __commonJS((exports, module) => {
844
844
  "@botpress/cli": "^4.27.3",
845
845
  "@botpress/client": "^1.27.2",
846
846
  "@botpress/cognitive": "^0.2.0",
847
- "@botpress/runtime": "^1.11.8",
847
+ "@botpress/runtime": "^1.12.0",
848
848
  "@botpress/sdk": "^4.18.1",
849
849
  "@bpinternal/jex": "^1.2.4",
850
850
  "@bpinternal/yargs-extra": "^0.0.21",
@@ -1570,6 +1570,9 @@ class BpDevCommand extends BaseCommand {
1570
1570
  NODE_ENV: "development",
1571
1571
  VM_DRIVER: "node",
1572
1572
  ADK_LOCAL_PAT: credentials.token,
1573
+ ADK_TOKEN: credentials.token,
1574
+ ADK_API_URL: credentials.apiUrl,
1575
+ ADK_BOT_ID: this.options.devBotId || "",
1573
1576
  WORKER_MODE: "true",
1574
1577
  WORKER_LIFETIME_MS: process.env.WORKER_LIFETIME_MS || "120000",
1575
1578
  ADK_DIRECTORY: join2(botPath, ".."),
@@ -4827,10 +4830,1271 @@ class ConfigManager {
4827
4830
  init_utils();
4828
4831
  import * as fs11 from "fs";
4829
4832
  import * as path15 from "path";
4830
- import { fileURLToPath } from "url";
4831
- var __filename2 = fileURLToPath(import.meta.url);
4832
- var __dirname2 = path15.dirname(__filename2);
4833
4833
 
4834
+ // src/agent-init/CLAUDE.template.md
4835
+ var CLAUDE_template_default = `# Botpress ADK Project Context
4836
+
4837
+ This project is built with the **Botpress Agent Development Kit (ADK)** - a TypeScript-first framework for building AI agents.
4838
+
4839
+ ## Table of Contents
4840
+
4841
+ - [Quick Reference: Use the Botpress MCP Server](#quick-reference-use-the-botpress-mcp-server)
4842
+ - [What is the ADK?](#what-is-the-adk)
4843
+ - [ADK CLI](#adk-cli)
4844
+ - [Core Concepts](#core-concepts)
4845
+ - [1. Agent Configuration](#1-agent-configuration-agentconfigts)
4846
+ - [2. Conversations](#2-conversations-srcconversations)
4847
+ - [3. Workflows](#3-workflows-srcworkflows)
4848
+ - [4. Tools](#4-tools-srctools)
4849
+ - [5. Knowledge Bases](#5-knowledge-bases-srcknowledge)
4850
+ - [6. Actions](#6-actions-srcactions)
4851
+ - [7. Zai Library](#7-zai-library)
4852
+ - [Project Structure](#project-structure)
4853
+ - [Development Workflow](#development-workflow)
4854
+ - [Examples](#examples)
4855
+ - [Best Practices](#best-practices)
4856
+ - [Common APIs](#common-apis)
4857
+ - [Advanced Autonomous Execution](#advanced-autonomous-execution)
4858
+ - [State and Metadata Management](#state-and-metadata-management)
4859
+ - [Advanced Table Operations](#advanced-table-operations)
4860
+ - [Knowledge Base Operations](#knowledge-base-operations)
4861
+ - [Advanced Conversation Patterns](#advanced-conversation-patterns)
4862
+ - [Citations System](#citations-system)
4863
+ - [When Making Changes](#when-making-changes)
4864
+ - [Resources](#resources)
4865
+
4866
+ ## Quick Reference: Use the Botpress MCP Server
4867
+
4868
+ **IMPORTANT**: When working on this project, always search the Botpress documentation using the \`mcp__botpress-docs__SearchBotpress\` tool before making changes. The ADK has specific patterns and APIs that are well-documented.
4869
+
4870
+ ## What is the ADK?
4871
+
4872
+ The ADK allows developers to build Botpress agents using **code instead of the Studio interface**. It provides:
4873
+
4874
+ - Project scaffolding with TypeScript
4875
+ - Hot reloading development server (\`adk dev\`)
4876
+ - Type-safe APIs and auto-generated types
4877
+ - Build and deploy to Botpress Cloud
4878
+
4879
+ ## ADK CLI
4880
+
4881
+ The ADK CLI is installed globally. You can run it using \`adk <command>\`.
4882
+ Always use bash to run ADK. (\`Bash(adk)\`)
4883
+ To install an integration: \`adk install <integration>\`
4884
+ To generate types without running in dev mode: \`adk build\`
4885
+
4886
+ ## Core Concepts
4887
+
4888
+ ### 1. Agent Configuration (\`agent.config.ts\`)
4889
+
4890
+ The main configuration file defines:
4891
+
4892
+ - **Agent name and description**
4893
+ - **Default models** for autonomous and zai operations
4894
+ - **State schemas** (bot-level and user-level state using Zod)
4895
+ - **Configuration variables** (encrypted, secure storage for API keys)
4896
+ - **Integration dependencies** (webchat, chat, etc.)
4897
+
4898
+ \`\`\`typescript
4899
+ export default defineConfig({
4900
+ name: "my-agent",
4901
+ defaultModels: {
4902
+ autonomous: "cerebras:gpt-oss-120b",
4903
+ zai: "cerebras:gpt-oss-120b",
4904
+ },
4905
+ bot: { state: z.object({}) },
4906
+ user: { state: z.object({}) },
4907
+ dependencies: {
4908
+ integrations: {
4909
+ webchat: { version: "webchat@0.3.0", enabled: true },
4910
+ },
4911
+ },
4912
+ });
4913
+ \`\`\`
4914
+
4915
+ ### 2. Conversations (\`src/conversations/\`)
4916
+
4917
+ **Primary way agents handle user messages**. Each conversation handler:
4918
+
4919
+ - Responds to messages from specific channels
4920
+ - Uses \`execute()\` to run autonomous AI logic
4921
+ - Can access conversation state, send messages, and call tools
4922
+
4923
+ **Key Pattern**: The \`execute()\` function runs the agent's AI loop:
4924
+
4925
+ \`\`\`typescript
4926
+ export default new Conversation({
4927
+ channel: "webchat.channel",
4928
+ handler: async ({ execute, conversation, state }) => {
4929
+ await execute({
4930
+ instructions: "Your agent's instructions here",
4931
+ tools: [myTool1, myTool2],
4932
+ knowledge: [myKnowledgeBase],
4933
+ });
4934
+ },
4935
+ });
4936
+ \`\`\`
4937
+
4938
+ ### 3. Workflows (\`src/workflows/\`)
4939
+
4940
+ **Long-running processes** for complex, multi-step operations:
4941
+
4942
+ - Can run on schedules (cron syntax)
4943
+ - Run independently or triggered by events
4944
+ - NOT the same as Studio Workflows
4945
+ - Use \`step()\` for durable execution (survives restarts)
4946
+
4947
+ \`\`\`typescript
4948
+ export default new Workflow({
4949
+ name: "periodic-indexing",
4950
+ schedule: "0 */6 * * *",
4951
+ handler: async ({ step }) => {
4952
+ await step("task-name", async () => {
4953
+ // Your logic here
4954
+ });
4955
+ },
4956
+ });
4957
+ \`\`\`
4958
+
4959
+ #### Advanced Workflow Step Methods
4960
+
4961
+ Beyond basic \`step()\`, workflows have powerful methods for complex orchestration:
4962
+
4963
+ **Parallel Processing:**
4964
+
4965
+ - \`step.map()\` - Process array items in parallel with concurrency control
4966
+ - \`step.forEach()\` - Like map but for side effects (returns void)
4967
+ - \`step.batch()\` - Process in sequential batches
4968
+
4969
+ \`\`\`typescript
4970
+ // Process items in parallel
4971
+ const results = await step.map(
4972
+ 'process-items',
4973
+ items,
4974
+ async (item, { i }) => processItem(item),
4975
+ { concurrency: 5, maxAttempts: 3 }
4976
+ )
4977
+
4978
+ // Batch processing
4979
+ await step.batch(
4980
+ 'bulk-insert',
4981
+ records,
4982
+ async (batch) => database.bulkInsert(batch),
4983
+ { batchSize: 100 }
4984
+ )
4985
+ \`\`\`
4986
+
4987
+ **Workflow Coordination:**
4988
+
4989
+ - \`step.waitForWorkflow()\` - Wait for another workflow to complete
4990
+ - \`step.executeWorkflow()\` - Start and wait in one call
4991
+
4992
+ \`\`\`typescript
4993
+ const result = await step.executeWorkflow('run-child', ChildWorkflow, { input })
4994
+ \`\`\`
4995
+
4996
+ **Timing Control:**
4997
+
4998
+ - \`step.sleep()\` - Pause execution (< 10s in-memory, >= 10s uses listening mode)
4999
+ - \`step.sleepUntil()\` - Sleep until specific time
5000
+ - \`step.listen()\` - Pause and wait for external event
5001
+
5002
+ \`\`\`typescript
5003
+ await step.sleep('wait-5s', 5000)
5004
+ await step.sleepUntil('wait-until-noon', new Date('2025-01-15T12:00:00Z'))
5005
+ \`\`\`
5006
+
5007
+ **Request Data from Conversation:**
5008
+
5009
+ \`\`\`typescript
5010
+ // In workflow
5011
+ const { topic } = await step.request('topic', 'What topic should I research?')
5012
+
5013
+ // In conversation
5014
+ if (isWorkflowDataRequest(event)) {
5015
+ await workflow.provide(event, { topic: userInput })
5016
+ }
5017
+ \`\`\`
5018
+
5019
+ **Execution Control:**
5020
+
5021
+ - \`step.fail()\` - Mark workflow as failed
5022
+ - \`step.abort()\` - Abort without failing
5023
+ - \`step.progress()\` - Record progress checkpoint
5024
+
5025
+ ### 4. Tools (\`src/tools/\`)
5026
+
5027
+ **AI-callable functions** that enable agents to perform actions:
5028
+
5029
+ - Must have clear name and description
5030
+ - Use Zod schemas for input/output
5031
+ - Can be passed to \`execute()\`
5032
+
5033
+ \`\`\`typescript
5034
+ export default new Autonomous.Tool({
5035
+ name: "searchDatabase",
5036
+ description: "Search the database",
5037
+ input: z.object({ query: z.string() }),
5038
+ output: z.object({ results: z.array(z.any()) }),
5039
+ handler: async ({ query }) => {
5040
+ // Tool logic
5041
+ return { results: [] };
5042
+ },
5043
+ });
5044
+ \`\`\`
5045
+
5046
+ ### 5. Knowledge Bases (\`src/knowledge/\`)
5047
+
5048
+ **RAG (Retrieval-Augmented Generation)** for providing context:
5049
+
5050
+ - Website scraping
5051
+ - Document ingestion
5052
+ - Can be passed to \`execute()\` via \`knowledge\` parameter
5053
+
5054
+ ### 6. Actions (\`src/actions/\`)
5055
+
5056
+ **Reusable business logic** that can:
5057
+
5058
+ - Be called from anywhere (import \`actions\` from \`@botpress/runtime\`)
5059
+ - Be converted to tools with \`.asTool()\`
5060
+ - Encapsulate logic not tied to conversational flow
5061
+
5062
+ ### 7. Zai Library
5063
+
5064
+ **Zai** is an LLM utility library that provides a clean, type-safe API for common AI operations. It's designed to work seamlessly with the ADK and SDK to process LLM inputs and outputs programmatically.
5065
+
5066
+ #### Importing Zai in ADK
5067
+
5068
+ In the ADK, Zai is available from \`@botpress/runtime\`:
5069
+
5070
+ \`\`\`typescript
5071
+ import { adk } from '@botpress/runtime'
5072
+ // then adk.zai.<method_name>
5073
+ \`\`\`
5074
+
5075
+ The default model for Zai operations is configured in \`agent.config.ts\`:
5076
+
5077
+ \`\`\`typescript
5078
+ export default defineConfig({
5079
+ defaultModels: {
5080
+ autonomous: "cerebras:gpt-oss-120b",
5081
+ zai: "cerebras:gpt-oss-120b", // Model used for Zai operations
5082
+ },
5083
+ })
5084
+ \`\`\`
5085
+
5086
+ #### When to Use Zai
5087
+
5088
+ Use Zai when you need to:
5089
+ - Extract structured data from unstructured text
5090
+ - Answer questions from documents with source citations
5091
+ - Verify Boolean conditions in content
5092
+ - Summarize long text into concise summaries
5093
+ - Generate text programmatically based on prompts
5094
+
5095
+ **Use Zai instead of \`execute()\` when**: You need deterministic, structured outputs for specific AI tasks (extraction, validation, summarization) rather than conversational interactions.
5096
+
5097
+ #### Zai Methods
5098
+
5099
+ **1. \`answer()\` - Answer Questions with Citations**
5100
+
5101
+ Answers questions from documents with intelligent source citations.
5102
+
5103
+ \`\`\`typescript
5104
+ const documents = [
5105
+ 'Botpress was founded in 2016.',
5106
+ 'The company is based in Quebec, Canada.',
5107
+ ]
5108
+
5109
+ const result = await zai.answer(documents, 'When was Botpress founded?')
5110
+
5111
+ if (result.type === 'answer') {
5112
+ console.log(result.answer) // "Botpress was founded in 2016."
5113
+ console.log(result.citations) // Array of citations with source references
5114
+ }
5115
+ \`\`\`
5116
+
5117
+ **When to use**: When you need to answer questions from a set of documents with traceable sources (e.g., custom RAG implementations, document Q&A).
5118
+
5119
+ **2. \`extract()\` - Extract Structured Data**
5120
+
5121
+ Extracts structured data from unstructured input using Zod schemas.
5122
+
5123
+ \`\`\`typescript
5124
+ import { z, adk } from '@botpress/runtime'
5125
+
5126
+ const userSchema = z.object({
5127
+ name: z.string(),
5128
+ email: z.string().email(),
5129
+ age: z.number()
5130
+ })
5131
+
5132
+ const input = "My name is John Doe, I'm 30 years old and my email is john@example.com"
5133
+ // zai.extract returns the extracted data DIRECTLY (not wrapped in { output: ... })
5134
+ const result = await adk.zai.extract(input, userSchema)
5135
+
5136
+ console.log(result)
5137
+ // { name: "John Doe", email: "john@example.com", age: 30 }
5138
+ \`\`\`
5139
+
5140
+ **When to use**: When you need to parse unstructured user input into structured data (e.g., form extraction from natural language, parsing contact information).
5141
+
5142
+ **3. \`check()\` - Verify Boolean Conditions**
5143
+
5144
+ Verifies a condition against some input and returns a boolean with explanation.
5145
+
5146
+ \`\`\`typescript
5147
+ const email = "Get rich quick! Click here now!!!"
5148
+ const { output } = await zai.check(email, 'is spam').result()
5149
+
5150
+ console.log(output.value) // true
5151
+ console.log(output.explanation) // "This email contains typical spam indicators..."
5152
+ \`\`\`
5153
+
5154
+ **When to use**: When you need to validate content or make binary decisions (e.g., content moderation, intent verification, condition checking).
5155
+
5156
+ **4. \`summarize()\` - Summarize Text**
5157
+
5158
+ Creates concise summaries of lengthy text to a desired length.
5159
+
5160
+ \`\`\`typescript
5161
+ const longArticle = "..." // Long article content
5162
+
5163
+ const summary = await zai.summarize(longArticle, {
5164
+ length: 100, // tokens
5165
+ prompt: 'key findings and main conclusions'
5166
+ })
5167
+ \`\`\`
5168
+
5169
+ **When to use**: When you need to condense long content (e.g., article summaries, transcript summaries, document overviews).
5170
+
5171
+ **5. \`text()\` - Generate Text**
5172
+
5173
+ Generates text of the desired length according to a prompt.
5174
+
5175
+ \`\`\`typescript
5176
+ const generated = await zai.text('Write a welcome message for new users', {
5177
+ length: 50 // tokens
5178
+ })
5179
+ \`\`\`
5180
+
5181
+ **When to use**: When you need to generate specific text content programmatically (e.g., dynamic content generation, templated responses).
5182
+
5183
+ #### Response Methods
5184
+
5185
+ All Zai operations return a Response object with promise-like behavior and additional functionality:
5186
+
5187
+ \`\`\`typescript
5188
+ // Await the result directly
5189
+ const result = await zai.extract(input, schema)
5190
+
5191
+ // Or use .result() for explicit promise handling
5192
+ const { output } = await zai.check(content, 'is valid').result()
5193
+ \`\`\`
5194
+
5195
+ ## Project Structure
5196
+
5197
+ \`\`\`
5198
+ agent.config.ts # Main configuration
5199
+ src/
5200
+ conversations/ # Message handlers (primary user interaction)
5201
+ workflows/ # Long-running processes
5202
+ tools/ # AI-callable functions
5203
+ actions/ # Reusable business logic
5204
+ knowledge/ # Knowledge bases for RAG
5205
+ triggers/ # Event-based triggers
5206
+ tables/ # Database tables
5207
+ .botpress/ # Auto-generated types (DO NOT EDIT)
5208
+ \`\`\`
5209
+
5210
+ ## Development Workflow
5211
+
5212
+ 1. **Start dev server**: \`adk dev\` (http://localhost:3001 for console)
5213
+ 2. **Add integrations**: \`adk add webchat@latest\`
5214
+ 3. **Build**: \`adk build\`
5215
+ 4. **Deploy**: \`adk deploy\`
5216
+ 5. **Chat in CLI**: \`adk chat\`
5217
+
5218
+ ## Examples
5219
+
5220
+ Official examples: https://github.com/botpress/adk/tree/main/examples
5221
+
5222
+ ### subagents
5223
+
5224
+ **What you'll learn:** How to build a multi-agent system where an orchestrator delegates to specialists.
5225
+
5226
+ Shows the \`SubAgent\` pattern where each specialist (HR, IT, Sales, etc.) runs in its own context with \`mode: "worker"\`, returns structured results via custom exits, and reports progress through \`onTrace\` hooks.
5227
+
5228
+ ### webchat-rag
5229
+
5230
+ **What you'll learn:** How to build a RAG assistant with scheduled indexing, guardrails, and admin features.
5231
+
5232
+ Shows \`Autonomous.Object\` for dynamic tool grouping, \`onBeforeTool\` hooks to enforce knowledge search before answering, scheduled workflows for KB refresh, and \`ThinkSignal\` for interrupting execution.
5233
+
5234
+ ### deep-research
5235
+
5236
+ **What you'll learn:** How to build complex, long-running workflows with progress tracking.
5237
+
5238
+ Shows \`step()\` and \`step.map()\` for workflow phases, \`Reference.Workflow\` for conversation-workflow linking, Tables for activity tracking, and extensive Zai usage (\`extract\`, \`answer\`, \`filter\`, \`text\`).
5239
+
5240
+ ## Best Practices
5241
+
5242
+ 1. **Search Botpress docs first** - Use the MCP tool before implementing
5243
+ 2. **Keep tools focused** - Single responsibility per tool
5244
+ 3. **Use Zod schemas** with \`.describe()\` for clarity
5245
+ 4. **State management** - Minimize large variables in main workflow
5246
+ 5. **Type safety** - Run \`adk dev\` or \`adk build\` to regenerate types after config changes
5247
+ 6. **Conversations vs Workflows**:
5248
+ - Conversations: User interactions, real-time responses
5249
+ - Workflows: Background tasks, scheduled jobs, long-running processes
5250
+
5251
+ ## Common APIs
5252
+
5253
+ ### Conversation Handler
5254
+
5255
+ \`\`\`typescript
5256
+ handler: async ({
5257
+ execute, // Run autonomous AI loop
5258
+ conversation, // Send messages, manage conversation
5259
+ state, // Conversation state (persisted)
5260
+ message, // Incoming message
5261
+ client, // Botpress API client
5262
+ }) => {};
5263
+ \`\`\`
5264
+
5265
+ ### Execute Function
5266
+
5267
+ \`\`\`typescript
5268
+ await execute({
5269
+ instructions: "String or function returning instructions",
5270
+ tools: [tool1, tool2], // Optional tools
5271
+ knowledge: [kb1, kb2], // Optional knowledge bases
5272
+ exits: [customExit], // Optional custom exits
5273
+ hooks: { onTrace, onBeforeTool }, // Optional hooks
5274
+ mode: "worker", // Optional: autonomous until exit
5275
+ iterations: 10, // Max loops (default 10)
5276
+ });
5277
+ \`\`\`
5278
+
5279
+ ## Advanced Autonomous Execution
5280
+
5281
+ ### Autonomous Namespace
5282
+
5283
+ The \`Autonomous\` namespace provides powerful primitives for controlling LLM behavior:
5284
+
5285
+ #### Autonomous.Exit - Custom Exit Conditions
5286
+
5287
+ Define custom exits for autonomous execution loops:
5288
+
5289
+ \`\`\`typescript
5290
+ import { Autonomous, z } from '@botpress/runtime'
5291
+
5292
+ const AnswerExit = new Autonomous.Exit({
5293
+ name: 'answer',
5294
+ description: 'Return when you have the final answer',
5295
+ schema: z.object({
5296
+ answer: z.string(),
5297
+ confidence: z.number()
5298
+ })
5299
+ })
5300
+
5301
+ const NoAnswerExit = new Autonomous.Exit({
5302
+ name: 'no_answer',
5303
+ description: 'No answer could be found'
5304
+ })
5305
+
5306
+ const result = await execute({
5307
+ instructions: 'Research and answer the question',
5308
+ exits: [AnswerExit, NoAnswerExit],
5309
+ mode: 'worker' // Run until exit triggered
5310
+ })
5311
+
5312
+ // ✅ CORRECT - Use result.is() and result.output
5313
+ if (result.is(AnswerExit)) {
5314
+ console.log(result.output.answer) // Type-safe access
5315
+ console.log(result.output.confidence)
5316
+ } else if (result.is(NoAnswerExit)) {
5317
+ console.log('No answer found')
5318
+ }
5319
+
5320
+ // ❌ WRONG - Don't use result.exit.name or result.exit.value
5321
+ // if (result.exit?.name === 'answer') { ... }
5322
+ \`\`\`
5323
+
5324
+ #### Autonomous.ThinkSignal - Inject Context
5325
+
5326
+ Provide context to the LLM without continuing execution:
5327
+
5328
+ \`\`\`typescript
5329
+ const results = await fetchData()
5330
+
5331
+ if (!results.length) {
5332
+ throw new ThinkSignal('error', 'No results found')
5333
+ }
5334
+
5335
+ // Inject formatted results into LLM context
5336
+ throw new ThinkSignal('results ready', formatResults(results))
5337
+ \`\`\`
5338
+
5339
+ #### Autonomous.Object - Dynamic Tool Grouping
5340
+
5341
+ Group tools dynamically based on state:
5342
+
5343
+ \`\`\`typescript
5344
+ const adminTools = new Autonomous.Object({
5345
+ name: 'admin',
5346
+ description: user.isAdmin ? 'Admin tools available' : 'Login required',
5347
+ tools: user.isAdmin ? [refreshKB, manageBots] : [generateLoginCode]
5348
+ })
5349
+
5350
+ await execute({
5351
+ objects: [adminTools]
5352
+ })
5353
+ \`\`\`
5354
+
5355
+ ### Execution Hooks
5356
+
5357
+ Full control over the autonomous execution loop:
5358
+
5359
+ \`\`\`typescript
5360
+ await execute({
5361
+ instructions: '...',
5362
+ hooks: {
5363
+ // Before tool execution - can modify input
5364
+ onBeforeTool: async ({ iteration, tool, input, controller }) => {
5365
+ console.log(\`About to call \${tool.name}\`)
5366
+ return { input: modifiedInput } // Optional: transform input
5367
+ },
5368
+
5369
+ // After tool execution - can modify output
5370
+ onAfterTool: async ({ iteration, tool, input, output, controller }) => {
5371
+ console.log(\`\${tool.name} returned:\`, output)
5372
+ return { output: modifiedOutput } // Optional: transform output
5373
+ },
5374
+
5375
+ // Before code execution in iteration
5376
+ onBeforeExecution: async (iteration, controller) => {
5377
+ return { code: modifiedCode } // Optional: transform generated code
5378
+ },
5379
+
5380
+ // When exit is triggered
5381
+ onExit: async (result) => {
5382
+ console.log('Exited with:', result)
5383
+ },
5384
+
5385
+ // After each iteration completes
5386
+ onIterationEnd: async (iteration, controller) => {
5387
+ if (iteration > 5) {
5388
+ controller.abort() // Stop execution
5389
+ }
5390
+ },
5391
+
5392
+ // On trace events (synchronous, non-blocking)
5393
+ onTrace: ({ trace, iteration }) => {
5394
+ if (trace.type === 'comment') {
5395
+ console.log('LLM thinking:', trace.comment)
5396
+ }
5397
+ if (trace.type === 'tool_call') {
5398
+ console.log('Calling:', trace.tool_name)
5399
+ }
5400
+ }
5401
+ }
5402
+ })
5403
+ \`\`\`
5404
+
5405
+ **Hook use cases:**
5406
+ - Logging and debugging
5407
+ - Input/output validation and transformation
5408
+ - Rate limiting tool calls
5409
+ - Custom abort conditions
5410
+ - Injecting dynamic context
5411
+
5412
+ ## State and Metadata Management
5413
+
5414
+ ### Tags - Key-Value Metadata
5415
+
5416
+ Track metadata for any entity (bot, user, conversation, workflow):
5417
+
5418
+ \`\`\`typescript
5419
+ import { TrackedTags } from '@botpress/runtime'
5420
+
5421
+ // Create tags instance
5422
+ const tags = TrackedTags.create({
5423
+ type: 'bot', // or 'user' | 'conversation' | 'workflow'
5424
+ id: entityId,
5425
+ client: botClient,
5426
+ initialTags: { status: 'active' }
5427
+ })
5428
+
5429
+ // Load from server
5430
+ await tags.load()
5431
+
5432
+ // Modify tags
5433
+ tags.tags = {
5434
+ ...tags.tags,
5435
+ lastSync: new Date().toISOString()
5436
+ }
5437
+
5438
+ // Check if modified
5439
+ if (tags.isDirty()) {
5440
+ await tags.save()
5441
+ }
5442
+
5443
+ // Batch operations
5444
+ await TrackedTags.saveAllDirty()
5445
+ await TrackedTags.loadAll()
5446
+ \`\`\`
5447
+
5448
+ **Access via workflow instance:**
5449
+
5450
+ \`\`\`typescript
5451
+ workflow.tags = { status: 'processing' }
5452
+ await workflow.save()
5453
+ \`\`\`
5454
+
5455
+ ### Reference.Workflow - Typed Workflow References
5456
+
5457
+ Serialize workflow references in state that auto-hydrate on access:
5458
+
5459
+ \`\`\`typescript
5460
+ import { Reference, z } from '@botpress/runtime'
5461
+
5462
+ // In conversation state schema
5463
+ state: z.object({
5464
+ research: Reference.Workflow('deep_research').optional()
5465
+ // or untyped: Reference.Workflow().optional()
5466
+ })
5467
+
5468
+ // In handler - always a WorkflowInstance
5469
+ handler: async ({ state }) => {
5470
+ if (state.research) {
5471
+ // state.research is typed WorkflowInstance
5472
+ console.log(state.research.status) // 'running' | 'completed' | etc
5473
+ console.log(state.research.output) // Typed output
5474
+
5475
+ if (state.research.status === 'completed') {
5476
+ // Access completed workflow data
5477
+ }
5478
+ }
5479
+ }
5480
+ \`\`\`
5481
+
5482
+ ### Context Object - Runtime Access
5483
+
5484
+ Global context for accessing runtime information:
5485
+
5486
+ \`\`\`typescript
5487
+ import { context } from '@botpress/runtime'
5488
+
5489
+ // Get specific context
5490
+ const client = context.get('client')
5491
+ const citations = context.get('citations')
5492
+ const logger = context.get('logger')
5493
+
5494
+ // Get all context
5495
+ const { client, cognitive, logger, operation } = context.getAll()
5496
+ \`\`\`
5497
+
5498
+ **Available context properties:**
5499
+ - \`client\` - Botpress API client
5500
+ - \`cognitive\` - LLM access
5501
+ - \`logger\` - Logging
5502
+ - \`operation\` - Current operation info
5503
+ - \`citations\` - Citation tracking
5504
+ - \`chat\` - Chat interface
5505
+ - \`bot\` - Bot tags and metadata
5506
+ - \`user\` - User information
5507
+ - \`conversation\` - Current conversation
5508
+ - \`message\` - Incoming message
5509
+ - \`event\` - Current event
5510
+ - \`workflow\` - Current workflow
5511
+ - \`workflowControlContext\` - Workflow control (abort, fail, restart)
5512
+
5513
+ ### State Management
5514
+
5515
+ Access and modify tracked state:
5516
+
5517
+ \`\`\`typescript
5518
+ import { bot, user } from '@botpress/runtime'
5519
+
5520
+ // Bot state
5521
+ bot.state.lastIndexed = new Date().toISOString()
5522
+ bot.state.config = { theme: 'dark' }
5523
+
5524
+ // User state
5525
+ user.state.preferences = { notifications: true }
5526
+ user.state.lastActive = Date.now()
5527
+ \`\`\`
5528
+
5529
+ State persists automatically across executions.
5530
+
5531
+ ## Advanced Table Operations
5532
+
5533
+ ### Table Naming Rules
5534
+
5535
+ **IMPORTANT**: Tables have strict naming requirements:
5536
+
5537
+ \`\`\`typescript
5538
+ // ✅ CORRECT - Name must end with "Table"
5539
+ export const MyDataTable = new Table({
5540
+ name: "mydataTable", // Must end with "Table"
5541
+ columns: { ... }
5542
+ });
5543
+
5544
+ // ❌ WRONG - Missing "Table" suffix
5545
+ name: "mydata"
5546
+ name: "my_data"
5547
+ \`\`\`
5548
+
5549
+ **Reserved column names** - Cannot use these as column names:
5550
+ - \`id\` (auto-generated)
5551
+ - \`createdAt\` (auto-generated)
5552
+ - \`updatedAt\` (auto-generated)
5553
+ - \`computed\`
5554
+ - \`stale\`
5555
+
5556
+ \`\`\`typescript
5557
+ // ❌ WRONG - Using reserved column name
5558
+ columns: {
5559
+ createdAt: z.string() // Reserved!
5560
+ }
5561
+
5562
+ // ✅ CORRECT - Use alternative name
5563
+ columns: {
5564
+ savedAt: z.string()
5565
+ }
5566
+ \`\`\`
5567
+
5568
+ ### Auto-Registration
5569
+
5570
+ Files in \`src/tables/\` are **auto-registered** by the ADK. Do NOT re-export from index.ts:
5571
+
5572
+ \`\`\`typescript
5573
+ // src/tables/index.ts
5574
+ // ❌ WRONG - Causes duplicate registration errors
5575
+ export { MyTable } from "./myTable";
5576
+
5577
+ // ✅ CORRECT - Leave empty or add comment
5578
+ // Tables are auto-registered from src/tables/*.ts files
5579
+ \`\`\`
5580
+
5581
+ Same applies to \`src/conversations/\`, \`src/workflows/\`, \`src/triggers/\`, etc.
5582
+
5583
+ Beyond basic CRUD, Tables support powerful query and manipulation features:
5584
+
5585
+ ### Complex Filtering
5586
+
5587
+ Use logical operators and conditions:
5588
+
5589
+ \`\`\`typescript
5590
+ await MyTable.findRows({
5591
+ filter: {
5592
+ $and: [
5593
+ { status: 'open' },
5594
+ { priority: { $in: ['high', 'urgent'] } }
5595
+ ],
5596
+ $or: [
5597
+ { assignee: userId },
5598
+ { reporter: userId }
5599
+ ],
5600
+ title: { $regex: 'bug|error', $options: 'i' }
5601
+ }
5602
+ })
5603
+ \`\`\`
5604
+
5605
+ **Filter operators:**
5606
+ - \`$eq\`, \`$ne\` - Equal, not equal
5607
+ - \`$gt\`, \`$gte\`, \`$lt\`, \`$lte\` - Comparisons
5608
+ - \`$in\`, \`$nin\` - In array, not in array
5609
+ - \`$exists\` - Field exists
5610
+ - \`$regex\` - Regular expression match
5611
+ - \`$options\` - Regex options (e.g., 'i' for case-insensitive)
5612
+ - \`$and\`, \`$or\` - Logical operators
5613
+
5614
+ ### Full-Text Search
5615
+
5616
+ Search across searchable columns:
5617
+
5618
+ \`\`\`typescript
5619
+ await MyTable.findRows({
5620
+ search: 'query string',
5621
+ filter: { status: 'active' }
5622
+ })
5623
+ \`\`\`
5624
+
5625
+ Mark columns as searchable in schema:
5626
+
5627
+ \`\`\`typescript
5628
+ columns: {
5629
+ title: z.string().searchable(),
5630
+ description: z.string().searchable()
5631
+ }
5632
+ \`\`\`
5633
+
5634
+ ### Aggregation and Grouping
5635
+
5636
+ Group and aggregate data:
5637
+
5638
+ \`\`\`typescript
5639
+ await MyTable.findRows({
5640
+ group: {
5641
+ status: 'count',
5642
+ priority: ['sum', 'avg'],
5643
+ complexity: ['max', 'min']
5644
+ }
5645
+ })
5646
+ \`\`\`
5647
+
5648
+ **Aggregation operations:** \`key\`, \`count\`, \`sum\`, \`avg\`, \`max\`, \`min\`, \`unique\`
5649
+
5650
+ ### Computed Columns
5651
+
5652
+ Columns with values computed from row data:
5653
+
5654
+ \`\`\`typescript
5655
+ columns: {
5656
+ fullName: {
5657
+ computed: true,
5658
+ schema: z.string(),
5659
+ dependencies: ['firstName', 'lastName'],
5660
+ value: async (row) => \`\${row.firstName} \${row.lastName}\`
5661
+ },
5662
+ age: {
5663
+ computed: true,
5664
+ schema: z.number(),
5665
+ dependencies: ['birthDate'],
5666
+ value: async (row) => {
5667
+ const today = new Date()
5668
+ const birth = new Date(row.birthDate)
5669
+ return today.getFullYear() - birth.getFullYear()
5670
+ }
5671
+ }
5672
+ }
5673
+ \`\`\`
5674
+
5675
+ ### Upsert Operations
5676
+
5677
+ Insert or update based on key column:
5678
+
5679
+ \`\`\`typescript
5680
+ await MyTable.upsertRows({
5681
+ rows: [
5682
+ { externalId: '123', name: 'Item 1' },
5683
+ { externalId: '456', name: 'Item 2' }
5684
+ ],
5685
+ keyColumn: 'externalId', // Update if exists, insert if not
5686
+ waitComputed: true // Wait for computed columns to update
5687
+ })
5688
+ \`\`\`
5689
+
5690
+ ### Bulk Operations
5691
+
5692
+ Efficient batch operations:
5693
+
5694
+ \`\`\`typescript
5695
+ // Delete by filter
5696
+ await MyTable.deleteRows({
5697
+ filter: { status: 'archived', createdAt: { $lt: '2024-01-01' } }
5698
+ })
5699
+
5700
+ // Delete by IDs
5701
+ await MyTable.deleteRowIds([1, 2, 3])
5702
+
5703
+ // Delete all
5704
+ await MyTable.deleteAllRows()
5705
+
5706
+ // Update multiple
5707
+ await MyTable.updateRows({
5708
+ rows: [
5709
+ { id: 1, status: 'active' },
5710
+ { id: 2, status: 'inactive' }
5711
+ ],
5712
+ waitComputed: true
5713
+ })
5714
+ \`\`\`
5715
+
5716
+ ### Error Handling
5717
+
5718
+ Collect errors and warnings from bulk operations:
5719
+
5720
+ \`\`\`typescript
5721
+ const { errors, warnings } = await MyTable.createRows({
5722
+ rows: data,
5723
+ waitComputed: true
5724
+ })
5725
+
5726
+ if (errors?.length) {
5727
+ console.error('Failed rows:', errors)
5728
+ }
5729
+ if (warnings?.length) {
5730
+ console.warn('Warnings:', warnings)
5731
+ }
5732
+ \`\`\`
5733
+
5734
+ ## Knowledge Base Operations
5735
+
5736
+ ### Data Sources
5737
+
5738
+ Multiple source types for knowledge bases:
5739
+
5740
+ #### Directory Source
5741
+
5742
+ \`\`\`typescript
5743
+ import { DataSource } from '@botpress/runtime'
5744
+
5745
+ const docs = DataSource.Directory.fromPath('src/knowledge', {
5746
+ id: 'docs',
5747
+ filter: (path) => path.endsWith('.md') || path.endsWith('.txt')
5748
+ })
5749
+ \`\`\`
5750
+
5751
+ #### Website Source
5752
+
5753
+ \`\`\`typescript
5754
+ const siteDocs = DataSource.Website.fromSitemap('https://example.com/sitemap.xml', {
5755
+ id: 'website',
5756
+ maxPages: 500,
5757
+ fetch: 'node:fetch' // or custom fetch implementation
5758
+ })
5759
+ \`\`\`
5760
+
5761
+ ### Knowledge Base Definition
5762
+
5763
+ \`\`\`typescript
5764
+ import { Knowledge } from '@botpress/runtime'
5765
+
5766
+ export default new Knowledge({
5767
+ name: 'docs',
5768
+ description: 'Product documentation',
5769
+ sources: [docsDirectory, websiteSource]
5770
+ })
5771
+ \`\`\`
5772
+
5773
+ ### Refresh Operations
5774
+
5775
+ Manually refresh knowledge base content:
5776
+
5777
+ \`\`\`typescript
5778
+ // Refresh entire knowledge base
5779
+ await DocsKB.refresh({ force: true })
5780
+
5781
+ // Refresh specific source
5782
+ await DocsKB.refreshSource('website', { force: true })
5783
+ \`\`\`
5784
+
5785
+ **Options:**
5786
+ - \`force: true\` - Force refresh even if recently updated
5787
+ - Automatic refresh via scheduled workflows recommended
5788
+
5789
+ ### Using Knowledge in Execute
5790
+
5791
+ \`\`\`typescript
5792
+ await execute({
5793
+ instructions: 'Answer using the documentation',
5794
+ knowledge: [DocsKB, APIKB],
5795
+ tools: [searchTool]
5796
+ })
5797
+ \`\`\`
5798
+
5799
+ Knowledge bases are automatically searchable via the \`search_knowledge\` tool.
5800
+
5801
+ ## Advanced Conversation Patterns
5802
+
5803
+ ### Multiple Channel Support
5804
+
5805
+ Handle messages from multiple channels in one handler:
5806
+
5807
+ \`\`\`typescript
5808
+ export default new Conversation({
5809
+ channel: ['chat.channel', 'webchat.channel', 'slack.dm'],
5810
+ handler: async ({ channel, execute }) => {
5811
+ console.log(\`Message from: \${channel}\`)
5812
+ await execute({ instructions: '...' })
5813
+ }
5814
+ })
5815
+ \`\`\`
5816
+
5817
+ ### Event Handling
5818
+
5819
+ Subscribe to integration events:
5820
+
5821
+ \`\`\`typescript
5822
+ export default new Conversation({
5823
+ channel: 'webchat.channel',
5824
+ events: ['webchat:conversationStarted', 'webchat:conversationEnded'],
5825
+ handler: async ({ type, event, message }) => {
5826
+ if (type === 'event' && event.type === 'webchat:conversationStarted') {
5827
+ // Send welcome message
5828
+ await conversation.send({
5829
+ type: 'text',
5830
+ payload: { text: 'Welcome!' }
5831
+ })
5832
+ }
5833
+
5834
+ if (type === 'message' && message?.type === 'text') {
5835
+ // Handle regular messages
5836
+ await execute({ instructions: '...' })
5837
+ }
5838
+ }
5839
+ })
5840
+ \`\`\`
5841
+
5842
+ ### Workflow Request Handling
5843
+
5844
+ Handle data requests from workflows:
5845
+
5846
+ \`\`\`typescript
5847
+ import { isWorkflowDataRequest } from '@botpress/runtime'
5848
+
5849
+ handler: async ({ type, event, execute }) => {
5850
+ // Check if this is a workflow requesting data
5851
+ if (type === 'workflow_request' && isWorkflowDataRequest(event)) {
5852
+ const userInput = await promptUser(event.payload.message)
5853
+
5854
+ // Provide data back to workflow
5855
+ await workflow.provide(event, { topic: userInput })
5856
+ return
5857
+ }
5858
+
5859
+ // Regular message handling
5860
+ await execute({ instructions: '...' })
5861
+ }
5862
+ \`\`\`
5863
+
5864
+ ### Typed Workflow Interactions
5865
+
5866
+ Work with typed workflow instances:
5867
+
5868
+ \`\`\`typescript
5869
+ import { isWorkflow, ResearchWorkflow } from '@botpress/runtime'
5870
+
5871
+ handler: async ({ state }) => {
5872
+ if (state.research && isWorkflow(state.research, 'research')) {
5873
+ // state.research is now typed as ResearchWorkflow
5874
+ console.log(state.research.status)
5875
+ console.log(state.research.output) // Typed output
5876
+
5877
+ if (state.research.status === 'completed') {
5878
+ await conversation.send({
5879
+ type: 'text',
5880
+ payload: { text: state.research.output.result }
5881
+ })
5882
+ }
5883
+ }
5884
+ }
5885
+ \`\`\`
5886
+
5887
+ ### Dynamic Tools Based on State
5888
+
5889
+ Provide different tools based on conversation state:
5890
+
5891
+ \`\`\`typescript
5892
+ handler: async ({ state, execute }) => {
5893
+ const tools = () => {
5894
+ if (state.workflowRunning) {
5895
+ return [cancelWorkflowTool, checkStatusTool]
5896
+ } else {
5897
+ return [startWorkflowTool, browseTool, searchTool]
5898
+ }
5899
+ }
5900
+
5901
+ await execute({
5902
+ instructions: '...',
5903
+ tools: tools()
5904
+ })
5905
+ }
5906
+ \`\`\`
5907
+
5908
+ ### Message Sending
5909
+
5910
+ Send different message types:
5911
+
5912
+ \`\`\`typescript
5913
+ // Text message
5914
+ await conversation.send({
5915
+ type: 'text',
5916
+ payload: { text: 'Hello!' }
5917
+ })
5918
+
5919
+ // Custom message type (integration-specific)
5920
+ await conversation.send({
5921
+ type: 'custom:messageType',
5922
+ payload: { data: 'custom payload' }
5923
+ })
5924
+ \`\`\`
5925
+
5926
+ ## Citations System
5927
+
5928
+ Track and manage source citations for LLM responses:
5929
+
5930
+ ### CitationsManager
5931
+
5932
+ Access via context:
5933
+
5934
+ \`\`\`typescript
5935
+ import { context } from '@botpress/runtime'
5936
+
5937
+ const citations = context.get('citations')
5938
+ \`\`\`
5939
+
5940
+ ### Registering Sources
5941
+
5942
+ Register sources that can be cited:
5943
+
5944
+ \`\`\`typescript
5945
+ // Register with URL
5946
+ const { tag } = citations.registerSource({
5947
+ url: 'https://example.com/doc',
5948
+ title: 'Documentation Page'
5949
+ })
5950
+
5951
+ // Register with file reference
5952
+ const { tag } = citations.registerSource({
5953
+ file: fileKey,
5954
+ title: 'Internal Document'
5955
+ })
5956
+ \`\`\`
5957
+
5958
+ ### Using Citation Tags
5959
+
5960
+ Inject citation tags into LLM content:
5961
+
5962
+ \`\`\`typescript
5963
+ const results = await searchKnowledgeBase(query)
5964
+
5965
+ for (const result of results) {
5966
+ const { tag } = citations.registerSource({
5967
+ file: result.file.key,
5968
+ title: result.file.name
5969
+ })
5970
+
5971
+ content += \`\${result.content} \${tag}\\n\`
5972
+ }
5973
+
5974
+ // Return cited content
5975
+ throw new ThinkSignal('results', content)
5976
+ \`\`\`
5977
+
5978
+ ### Citation Format
5979
+
5980
+ Citations are automatically formatted with tags like \`[1]\`, \`[2]\`, etc., and tracked by the system for reference.
5981
+
5982
+ ### Example: Tool with Citations
5983
+
5984
+ \`\`\`typescript
5985
+ export default new Autonomous.Tool({
5986
+ name: 'search_docs',
5987
+ description: 'Search documentation',
5988
+ handler: async ({ query }) => {
5989
+ const citations = context.get('citations')
5990
+ const results = await searchDocs(query)
5991
+
5992
+ let response = ''
5993
+ for (const doc of results) {
5994
+ const { tag } = citations.registerSource({
5995
+ url: doc.url,
5996
+ title: doc.title
5997
+ })
5998
+ response += \`\${doc.content} \${tag}\\n\\n\`
5999
+ }
6000
+
6001
+ return response
6002
+ }
6003
+ })
6004
+ \`\`\`
6005
+
6006
+ ## Common Mistakes to Avoid
6007
+
6008
+ ### 1. Wrong Zai Import
6009
+ \`\`\`typescript
6010
+ // ❌ WRONG
6011
+ import { zai } from '@botpress/runtime'
6012
+ const result = await zai.extract(...)
6013
+
6014
+ // ✅ CORRECT
6015
+ import { adk } from '@botpress/runtime'
6016
+ const result = await adk.zai.extract(...)
6017
+ \`\`\`
6018
+
6019
+ ### 2. Expecting \`.output\` from zai.extract
6020
+ \`\`\`typescript
6021
+ // ❌ WRONG - zai.extract returns data directly
6022
+ const result = await adk.zai.extract(input, schema)
6023
+ console.log(result.output) // undefined!
6024
+
6025
+ // ✅ CORRECT
6026
+ const result = await adk.zai.extract(input, schema)
6027
+ console.log(result) // { name: "John", age: 30 }
6028
+ \`\`\`
6029
+
6030
+ ### 3. Wrong Exit Result Handling
6031
+ \`\`\`typescript
6032
+ // ❌ WRONG
6033
+ if (result.exit?.name === 'my_exit') {
6034
+ const data = result.exit.value
6035
+ }
6036
+
6037
+ // ✅ CORRECT
6038
+ if (result.is(MyExit)) {
6039
+ const data = result.output // Type-safe!
6040
+ }
6041
+ \`\`\`
6042
+
6043
+ ### 4. Reserved Table Column Names
6044
+ \`\`\`typescript
6045
+ // ❌ WRONG - These are reserved
6046
+ columns: {
6047
+ id: z.string(),
6048
+ createdAt: z.string(),
6049
+ updatedAt: z.string()
6050
+ }
6051
+
6052
+ // ✅ CORRECT - Use alternatives
6053
+ columns: {
6054
+ visibleId: z.string(),
6055
+ savedAt: z.string(),
6056
+ modifiedAt: z.string()
6057
+ }
6058
+ \`\`\`
6059
+
6060
+ ### 5. Re-exporting Auto-Registered Files
6061
+ \`\`\`typescript
6062
+ // ❌ WRONG - src/tables/index.ts
6063
+ export { MyTable } from "./myTable" // Causes duplicates!
6064
+
6065
+ // ✅ CORRECT - Leave index.ts empty
6066
+ // Files in src/tables/, src/conversations/, etc. are auto-registered
6067
+ \`\`\`
6068
+
6069
+ ### 6. Table Name Missing "Table" Suffix
6070
+ \`\`\`typescript
6071
+ // ❌ WRONG
6072
+ name: "users"
6073
+ name: "user_data"
6074
+
6075
+ // ✅ CORRECT
6076
+ name: "usersTable"
6077
+ name: "userdataTable"
6078
+ \`\`\`
6079
+
6080
+ ## When Making Changes
6081
+
6082
+ 1. **Always search Botpress docs** using \`mcp__botpress-docs__SearchBotpress\`
6083
+ 2. **Check examples** for patterns
6084
+ 3. **Regenerate types** after changing \`agent.config.ts\` (run \`adk dev\`)
6085
+ 4. **Test in dev mode** with hot reloading (\`adk dev\`)
6086
+ 5. **Follow TypeScript types** - They're auto-generated from integrations
6087
+
6088
+ ## Resources
6089
+
6090
+ - [ADK Overview](https://botpress.com/docs/for-developers/adk/overview)
6091
+ - [ADK Getting Started](https://botpress.com/docs/for-developers/adk/getting-started)
6092
+ - [Project Structure](https://botpress.com/docs/for-developers/adk/project-structure)
6093
+ - [Conversations](https://botpress.com/docs/for-developers/adk/concepts/conversations)
6094
+ - [Workflows](https://botpress.com/docs/for-developers/adk/concepts/workflows)
6095
+ `;
6096
+
6097
+ // src/agent-init/agent-project-generator.ts
4834
6098
  class AgentProjectGenerator {
4835
6099
  projectPath;
4836
6100
  projectName;
@@ -4875,7 +6139,7 @@ class AgentProjectGenerator {
4875
6139
  deploy: "adk deploy"
4876
6140
  },
4877
6141
  dependencies: {
4878
- "@botpress/runtime": `^${"1.11.8"}`
6142
+ "@botpress/runtime": `^${"1.12.0"}`
4879
6143
  },
4880
6144
  devDependencies: {
4881
6145
  typescript: "^5.9.3"
@@ -5039,9 +6303,7 @@ A Botpress Agent built with the ADK.
5039
6303
  await this.writeFormattedFile("README.md", readme);
5040
6304
  }
5041
6305
  createClaudeMd() {
5042
- const templatePath = path15.join(__dirname2, "CLAUDE.template.md");
5043
- const claudeMd = fs11.readFileSync(templatePath, "utf-8");
5044
- this.writeFile("CLAUDE.md", claudeMd);
6306
+ this.writeFile("CLAUDE.md", CLAUDE_template_default);
5045
6307
  }
5046
6308
  async createSourceStructure() {
5047
6309
  const srcPath = path15.join(this.projectPath, "src");
@@ -8840,7 +10102,7 @@ class KnowledgeManager {
8840
10102
  }
8841
10103
  }
8842
10104
  // src/knowledge/sync-formatter.ts
8843
- import { readFileSync as readFileSync2 } from "fs";
10105
+ import { readFileSync } from "fs";
8844
10106
  import { join as join7 } from "path";
8845
10107
  var getAdkVersion = () => {
8846
10108
  try {
@@ -8849,7 +10111,7 @@ var getAdkVersion = () => {
8849
10111
  } catch {
8850
10112
  try {
8851
10113
  const adkPackagePath = join7(process.cwd(), "node_modules/@botpress/adk/package.json");
8852
- const pkg = JSON.parse(readFileSync2(adkPackagePath, "utf-8"));
10114
+ const pkg = JSON.parse(readFileSync(adkPackagePath, "utf-8"));
8853
10115
  return pkg.version;
8854
10116
  } catch {
8855
10117
  return "unknown";
@@ -9137,7 +10399,7 @@ class AgentConfigSyncManager {
9137
10399
  }
9138
10400
 
9139
10401
  // src/preflight/formatter.ts
9140
- import { readFileSync as readFileSync3 } from "fs";
10402
+ import { readFileSync as readFileSync2 } from "fs";
9141
10403
  import { join as join9 } from "path";
9142
10404
  var getAdkVersion2 = () => {
9143
10405
  try {
@@ -9146,7 +10408,7 @@ var getAdkVersion2 = () => {
9146
10408
  } catch {
9147
10409
  try {
9148
10410
  const adkPackagePath = join9(process.cwd(), "node_modules/@botpress/adk/package.json");
9149
- const pkg = JSON.parse(readFileSync3(adkPackagePath, "utf-8"));
10411
+ const pkg = JSON.parse(readFileSync2(adkPackagePath, "utf-8"));
9150
10412
  return pkg.version;
9151
10413
  } catch {
9152
10414
  return "unknown";
@@ -9447,9 +10709,176 @@ class PreflightChecker {
9447
10709
  options?.onSuccess?.("Bot project regenerated");
9448
10710
  }
9449
10711
  }
10712
+ // src/runner/script-runner.ts
10713
+ import dedent2 from "dedent";
10714
+ import { existsSync as existsSync8 } from "fs";
10715
+ import fs17 from "fs/promises";
10716
+ import path35 from "path";
10717
+ import { spawn } from "child_process";
10718
+ init_utils();
10719
+ class ScriptRunner {
10720
+ projectPath;
10721
+ forceRegenerate;
10722
+ prod;
10723
+ credentials;
10724
+ constructor(options) {
10725
+ this.projectPath = path35.resolve(options.projectPath);
10726
+ this.forceRegenerate = options.forceRegenerate ?? false;
10727
+ this.prod = options.prod ?? false;
10728
+ this.credentials = options.credentials;
10729
+ }
10730
+ async prepare() {
10731
+ const project = await AgentProject.load(this.projectPath, {
10732
+ adkCommand: "adk-build"
10733
+ });
10734
+ const botPath = path35.join(this.projectPath, ".adk", "bot");
10735
+ const runnerPath = path35.join(botPath, "src", "script-runner.ts");
10736
+ const botpressTypesPath = path35.join(botPath, ".botpress", "implementation", "index.ts");
10737
+ const needsRegenerate = this.forceRegenerate || !existsSync8(runnerPath) || !existsSync8(botpressTypesPath);
10738
+ if (needsRegenerate) {
10739
+ try {
10740
+ await generateAssetsTypes(project.path);
10741
+ await generateAssetsRuntime(project.path, project.agentInfo?.botId, project.agentInfo?.workspaceId);
10742
+ } catch {}
10743
+ await generateBotProject({
10744
+ projectPath: project.path,
10745
+ outputPath: botPath
10746
+ });
10747
+ await this.generateScriptRunner(botPath);
10748
+ await this.runBpBuild(botPath);
10749
+ }
10750
+ return { botPath, runnerPath, project };
10751
+ }
10752
+ async runBpBuild(botPath) {
10753
+ const buildCommand = new BpBuildCommand({ botPath });
10754
+ return new Promise((resolve3, reject) => {
10755
+ buildCommand.on("done", () => resolve3());
10756
+ buildCommand.on("error", (err) => reject(new Error(err.message)));
10757
+ buildCommand.run();
10758
+ });
10759
+ }
10760
+ async generateScriptRunner(botPath) {
10761
+ const content = dedent2`
10762
+ /**
10763
+ * ADK Script Runner Entry Point
10764
+ *
10765
+ * This file bootstraps the ADK runtime and then executes a user script.
10766
+ * It's auto-generated by the ADK CLI.
10767
+ */
10768
+ import * as bp from '.botpress'
10769
+ import { setupAdkRuntime } from './adk-runtime'
10770
+
10771
+ // Create a minimal bot instance for runtime initialization
10772
+ const bot = new bp.Bot({
10773
+ actions: {}
10774
+ })
10775
+
10776
+ // Initialize the ADK runtime
10777
+ setupAdkRuntime(bot)
10778
+
10779
+ // Export runtime utilities for scripts to use
10780
+ export { bot }
10781
+
10782
+ // Get the script path from command line arguments
10783
+ const scriptPath = process.argv[2]
10784
+
10785
+ if (!scriptPath) {
10786
+ console.error('Error: No script path provided')
10787
+ console.error('Usage: bun run script-runner.ts <script-path> [args...]')
10788
+ process.exit(1)
10789
+ }
10790
+
10791
+ // Import and run the user script
10792
+ async function runScript() {
10793
+ try {
10794
+ // Dynamic import of the user script
10795
+ const scriptModule = await import(scriptPath)
10796
+
10797
+ // If the script exports a default function, call it
10798
+ if (typeof scriptModule.default === 'function') {
10799
+ const args = process.argv.slice(3)
10800
+ await scriptModule.default(...args)
10801
+ }
10802
+ // If it exports a 'run' function, call it
10803
+ else if (typeof scriptModule.run === 'function') {
10804
+ const args = process.argv.slice(3)
10805
+ await scriptModule.run(...args)
10806
+ }
10807
+ // If it exports a 'main' function, call it
10808
+ else if (typeof scriptModule.main === 'function') {
10809
+ const args = process.argv.slice(3)
10810
+ await scriptModule.main(...args)
10811
+ }
10812
+ // Otherwise, the script should have run on import (top-level code)
10813
+ } catch (error) {
10814
+ console.error('Script execution failed:', error)
10815
+ process.exit(1)
10816
+ }
10817
+ }
10818
+
10819
+ runScript()
10820
+ `;
10821
+ await fs17.writeFile(path35.join(botPath, "src", "script-runner.ts"), await formatCode(content), "utf-8");
10822
+ }
10823
+ async run(scriptPath, options = {}) {
10824
+ const { botPath, runnerPath, project } = await this.prepare();
10825
+ const absoluteScriptPath = path35.isAbsolute(scriptPath) ? scriptPath : path35.resolve(this.projectPath, scriptPath);
10826
+ if (!existsSync8(absoluteScriptPath)) {
10827
+ throw new Error(`Script not found: ${absoluteScriptPath}`);
10828
+ }
10829
+ const botId = this.prod ? project.agentInfo?.botId : project.agentInfo?.devId || project.agentInfo?.botId;
10830
+ const workspaceId = project.agentInfo?.workspaceId;
10831
+ if (!botId) {
10832
+ const idType = this.prod ? "botId" : "devId";
10833
+ throw new Error(`No ${idType} found in agent.json. ` + (this.prod ? 'Please deploy your agent first with "adk deploy".' : 'Please run "adk dev" first to create a development bot, or use --prod to use the production bot.'));
10834
+ }
10835
+ const args = ["run", runnerPath, absoluteScriptPath, ...options.args || []];
10836
+ const env = {
10837
+ ...process.env,
10838
+ ADK_PROJECT_PATH: this.projectPath,
10839
+ ADK_BOT_PATH: botPath,
10840
+ ADK_BOT_ID: botId,
10841
+ ADK_WORKSPACE_ID: workspaceId || "",
10842
+ ADK_IS_PROD: this.prod ? "true" : "false",
10843
+ BP_DISABLE_WORKER_MODE: "true",
10844
+ ...options.env,
10845
+ ADK_SCRIPT_MODE: "true",
10846
+ ADK_SCRIPT_PATH: absoluteScriptPath,
10847
+ ADK_TOKEN: this.credentials.token,
10848
+ ADK_API_URL: this.credentials.apiUrl
10849
+ };
10850
+ return new Promise((resolve3, reject) => {
10851
+ const child = spawn("bun", args, {
10852
+ cwd: botPath,
10853
+ env,
10854
+ stdio: options.inheritStdio !== false ? "inherit" : "pipe"
10855
+ });
10856
+ child.on("error", (error) => {
10857
+ reject(error);
10858
+ });
10859
+ child.on("close", (code) => {
10860
+ resolve3(code ?? 0);
10861
+ });
10862
+ });
10863
+ }
10864
+ }
10865
+ async function runScript(options) {
10866
+ const runner = new ScriptRunner({
10867
+ projectPath: options.projectPath,
10868
+ forceRegenerate: options.forceRegenerate,
10869
+ prod: options.prod,
10870
+ credentials: options.credentials
10871
+ });
10872
+ return runner.run(options.scriptPath, {
10873
+ args: options.args,
10874
+ env: options.env,
10875
+ inheritStdio: options.inheritStdio
10876
+ });
10877
+ }
9450
10878
  export {
9451
10879
  workspaceCache,
9452
10880
  stringifyWithOrder,
10881
+ runScript,
9453
10882
  orderKeys,
9454
10883
  integrationKeyOrder,
9455
10884
  initAssets,
@@ -9466,6 +10895,7 @@ export {
9466
10895
  ValidationErrors,
9467
10896
  ValidationErrorCode,
9468
10897
  TableManager,
10898
+ ScriptRunner,
9469
10899
  ProjectState,
9470
10900
  PreflightFormatter,
9471
10901
  PreflightChecker,
@@ -9504,5 +10934,5 @@ export {
9504
10934
  AgentProject
9505
10935
  };
9506
10936
 
9507
- //# debugId=E3C3F451B27CF2ED64756E2164756E21
10937
+ //# debugId=BDE819CF4ECB952364756E2164756E21
9508
10938
  //# sourceMappingURL=index.js.map