@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/agent-init/agent-project-generator.d.ts.map +1 -1
- package/dist/commands/bp-dev-command.d.ts +2 -0
- package/dist/commands/bp-dev-command.d.ts.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1445 -15
- package/dist/index.js.map +7 -6
- package/dist/runner/index.d.ts +12 -0
- package/dist/runner/index.d.ts.map +1 -0
- package/dist/runner/script-runner.d.ts +67 -0
- package/dist/runner/script-runner.d.ts.map +1 -0
- package/package.json +2 -2
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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
|
|
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(
|
|
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
|
|
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(
|
|
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=
|
|
10937
|
+
//# debugId=BDE819CF4ECB952364756E2164756E21
|
|
9508
10938
|
//# sourceMappingURL=index.js.map
|