diff --git a/adk/concepts/actions.mdx b/adk/concepts/actions.mdx
index a6860acb..762f7e61 100644
--- a/adk/concepts/actions.mdx
+++ b/adk/concepts/actions.mdx
@@ -81,15 +81,18 @@ export default new Trigger({
});
```
-```ts From a tool highlight={1,6}
-import { Autonomous, actions } from "@botpress/runtime"
+```ts From a tool highlight={1,8}
+import { Autonomous, actions, z } from "@botpress/runtime"
export default new Autonomous.Tool({
name: "someTool",
+ description: "A tool that does something",
+ input: z.object({}),
+ output: z.object({ result: z.string() }),
handler: async () => {
const result = await actions.doSomething()
- }
- // Other logic
+ return { result }
+ },
})
```
diff --git a/adk/concepts/conversations.mdx b/adk/concepts/conversations.mdx
index 35b8aa9e..5a7b59b4 100644
--- a/adk/concepts/conversations.mdx
+++ b/adk/concepts/conversations.mdx
@@ -13,7 +13,7 @@ import { Conversation } from "@botpress/runtime";
export default new Conversation({
channel: "*",
- handler: async ({ execute, message }) => {
+ handler: async ({ execute }) => {
await execute({
instructions: "You are a helpful assistant.",
});
@@ -71,7 +71,7 @@ The handler function receives context about the incoming message and provides AP
export default new Conversation({
channel: "*",
handler: async ({ execute, message }) => {
- const userMessage = message.text;
+ const userMessage = message.payload.text;
await execute({
instructions: `You are a helpful assistant. The user said: ${userMessage}`,
});
@@ -122,7 +122,18 @@ export default new Conversation({
## Conversation instance
-The `handler` receives a `conversation` object that provides methods to interact with the current conversation:
+The `handler` receives a `conversation` object that provides methods to interact with the current conversation. It exposes `conversation.id` (the conversation ID) and `conversation.channel` (the channel name) as read-only properties.
+
+### Loading a conversation by ID
+
+You can load any conversation by its ID using the static `Conversation.get()` method:
+
+```typescript
+import { Conversation } from "@botpress/runtime";
+
+const otherConversation = await Conversation.get("conversation-id");
+await otherConversation.send({ type: "text", payload: { text: "Hello from another conversation!" } });
+```
### Sending messages
@@ -157,21 +168,6 @@ const priority = conversation.tags.priority;
conversation.tags.priority = "high";
```
-### Trigger subscriptions
-
-Subscribe a conversation to triggers:
-
-```typescript
-// Subscribe to a trigger
-await conversation.subscribeToTrigger("myTrigger");
-
-// Subscribe with a key for filtered triggers
-await conversation.subscribeToTrigger("myTrigger", "specific-key");
-
-// Unsubscribe
-await conversation.unsubscribeFromTrigger("myTrigger");
-```
-
## Multiple conversations
You can create multiple conversation handlers for different channels or use cases:
@@ -209,36 +205,26 @@ src/conversations/
Optional Zod schema defining the conversation state structure. Defaults to an empty object if not provided.
+ Optional array of event names this conversation should listen to. Supports integration events with a `conversationId` (e.g., `"webchat:conversationStarted"`) and custom events defined in `agent.config.ts`. By default, conversations only receive messages, not events.
+
+
- Optional function to determine if a conversation should start from a trigger event. Returns a conversation ID or false.
+ Optional factory function to provide a custom `Chat` instance for the conversation.
### Handler parameters
-The handler function receives different parameters depending on the incoming request type. The handler uses a discriminated union based on the `type` field:
+The handler function receives different parameters depending on the incoming request type. The handler uses a discriminated union based on the `type` field.
-#### Message handler
-
-When `type` is `"message"`:
+All handler types share these common parameters:
-
-
- Indicates this is a message request.
-
-
- Message object containing the user's message data. Typed by `ConversationInstance` at runtime based on the channel.
-
+
Function to execute autonomous AI logic. Used to run the agent with instructions, tools, knowledge bases, etc.
-
-
- Instructions for the AI agent. Can be a string or a function that returns a string.
-
-
- Optional array of tools the agent can use.
-
-
- Optional array of objects the agent can interact with.
-
-
- Optional record of exit handlers.
-
-
- Optional abort signal to cancel execution.
-
-
- Optional hooks for customizing behavior (`onBeforeTool`, `onAfterTool`, `onTrace`, etc.).
-
-
- Optional temperature for the AI model. Defaults to 0.7.
-
-
- Optional model name(s) to use. Can be a string, array of strings, or a function that returns either.
-
-
- Optional array of knowledge bases to use.
-
-
- Optional maximum number of iterations (loops). Defaults to 10.
-
-
-
-
-#### Event handler
-
-When `type` is `"event"`:
-
-
- Indicates this is an event request.
+ Chat instance for sending messages and managing the conversation transcript.
- Event object containing event data from integrations or system events. Typed by `ConversationInstance` at runtime based on the channel.
+ The channel this message or event came from.
+
+
+#### Message handler
+
+When `type` is `"message"`:
+
+
- Conversation instance providing methods to interact with the conversation (send messages, manage state, etc.).
+ Indicates this is a message request.
- Mutable conversation state object. Changes to this object are automatically persisted. Typed based on your state schema.
+ Message object containing `type` and `payload` properties. Typed based on the channel at runtime.
+
+
+#### Event handler
+
+When `type` is `"event"`:
+
+
- Botpress client for making API calls (tables, events, etc.).
+ Indicates this is an event request.
- Function to execute autonomous AI logic. Used to run the agent with instructions, tools, knowledge bases, etc.
-
-
- Instructions for the AI agent. Can be a string or a function that returns a string.
-
-
- Optional array of tools the agent can use.
-
-
- Optional array of objects the agent can interact with.
-
-
- Optional record of exit handlers.
-
-
- Optional abort signal to cancel execution.
-
-
- Optional hooks for customizing behavior (`onBeforeTool`, `onAfterTool`, `onTrace`, etc.).
-
-
- Optional temperature for the AI model. Defaults to 0.7.
-
-
- Optional model name(s) to use. Can be a string, array of strings, or a function that returns either.
-
-
- Optional array of knowledge bases to use.
-
-
- Optional maximum number of iterations (loops). Defaults to 10.
-
-
+ Event object containing `type` and `payload` properties from integrations or custom events. Typed based on the channel at runtime.
+
+ To receive events in a conversation, you must specify the `events` prop on the Conversation definition. By default, conversations only receive messages.
+
+
#### Workflow request handler
When `type` is `"workflow_request"`:
@@ -486,96 +357,100 @@ When `type` is `"workflow_request"`:
+
+
+#### Workflow callback handler
+
+When `type` is `"workflow_callback"`:
+
+
+ Indicates this is a workflow callback event.
+
+
- Conversation instance providing methods to interact with the conversation (send messages, manage state, etc.).
+ Workflow callback object containing information about the completed workflow.
+
+
+### Execute props
+
+The `execute` function accepts the following props:
+
+
- Mutable conversation state object. Changes to this object are automatically persisted. Typed based on your state schema.
+ Instructions for the AI agent. Can be a string or a function that returns a string.
+ Optional array of tools the agent can use.
+
+
+ Optional array of objects the agent can interact with.
+
+
+ Optional record of exit handlers.
+
+
+ Optional abort signal to cancel execution.
+
+
- Botpress client for making API calls (tables, events, etc.).
+ Optional hooks for customizing behavior (`onBeforeTool`, `onAfterTool`, `onTrace`, `onBeforeExecution`, `onExit`, `onIterationStart`, `onIterationEnd`).
- Function to execute autonomous AI logic. Used to run the agent with instructions, tools, knowledge bases, etc.
-
-
- Instructions for the AI agent. Can be a string or a function that returns a string.
-
-
- Optional array of tools the agent can use.
-
-
- Optional array of objects the agent can interact with.
-
-
- Optional record of exit handlers.
-
-
- Optional abort signal to cancel execution.
-
-
- Optional hooks for customizing behavior (`onBeforeTool`, `onAfterTool`, `onTrace`, etc.).
-
-
- Optional temperature for the AI model. Defaults to 0.7.
-
-
- Optional model name(s) to use. Can be a string, array of strings, or a function that returns either.
-
-
- Optional array of knowledge bases to use.
-
-
- Optional maximum number of iterations (loops). Defaults to 10.
-
-
+ Optional temperature for the AI model.
+
+
+ Optional model name(s) to use. Can be a string, array of strings, or a function that returns either.
+
+
+ Optional reasoning effort for models that support reasoning. `"none"` disables reasoning, `"dynamic"` lets the provider decide.
+
+
+ Optional array of knowledge bases to use.
+
+
+ Optional maximum number of iterations (loops). Defaults to 10.
diff --git a/adk/concepts/knowledge.mdx b/adk/concepts/knowledge.mdx
index 8faafca7..23a60015 100644
--- a/adk/concepts/knowledge.mdx
+++ b/adk/concepts/knowledge.mdx
@@ -184,6 +184,34 @@ export default new Conversation({
});
```
+## Searching knowledge
+
+You can search a knowledge base directly using the `search()` method:
+
+```typescript
+import { DocsKB } from "../knowledge/files";
+
+const results = await DocsKB.search("How do I deploy my agent?");
+
+for (const passage of results.passages) {
+ console.log(passage.content);
+ console.log(passage.metadata.url);
+}
+```
+
+The `search()` method accepts optional options:
+
+```typescript
+const results = await DocsKB.search("deployment", {
+ contextDepth: 4, // Surrounding passages for context (1–20, default: 4)
+ limit: 10, // Max passages to return (1–50, default: 20)
+});
+```
+
+
+ In most cases you don't need to call `search()` directly. When you pass a knowledge base to `execute()` via the `knowledge` prop, the AI model automatically searches it as needed.
+
+
## Refreshing knowledge
Refresh knowledge bases to update their content:
@@ -240,6 +268,20 @@ await WebsiteKB.refreshSource("my-source-id", { force: true });
type="DataSource[]"
required
>
- Array of data sources to index. Can include `Website`, `Directory`, or `Table` sources.
+ Array of data sources to index. Can include `Website` or `Directory` sources.
+
+
+
+### Knowledge methods
+
+
+
+ Search the knowledge base for relevant passages. Options: `contextDepth` (1–20, default 4), `limit` (1–50, default 20). Returns passages with `content` and `metadata` (url, title, file, description, etc.).
+
+
+ Refresh the knowledge base by re-indexing all sources. Pass `{ force: true }` to re-index even if unchanged.
+
+
+ Refresh a specific data source by its ID. Pass `{ force: true }` to force re-indexing.
diff --git a/adk/concepts/tables.mdx b/adk/concepts/tables.mdx
index d82e036a..5b8525e3 100644
--- a/adk/concepts/tables.mdx
+++ b/adk/concepts/tables.mdx
@@ -24,8 +24,8 @@ export default new Table({
To properly deploy your agent, make sure your table name follows the correct convention:
-- Cannot start with a number
-- Must be 30 characters or less
+- Must start with a letter or underscore
+- Must be 35 characters or less
- Can contain only letters, numbers, and underscores
- Must end with `Table`
@@ -75,45 +75,171 @@ Check out the [column options](#table-props) in the Table props reference for a
## Using tables
-Tables are automatically created and synced when you deploy. You can import them and interact with them directly using the table instance methods, which provide type-safe operations:
+Tables are automatically created and synced when you deploy. You can import them and interact with them directly using the table instance methods, which provide type-safe operations.
-```ts src/workflows/create-order.ts highlight={8-10, 13-18, 21-23}
-import { Workflow } from "@botpress/runtime";
+### Creating rows
+
+```typescript
import OrderTable from "../tables/order-table";
-export default new Workflow({
- name: "createOrder",
- handler: async ({ step }) => {
- // Create rows
- const { rows } = await OrderTable.createRows({
- rows: [{ userId: "user123", total: 99.99, status: "pending" }],
- });
-
- // Find rows with filters
- const { rows: orders } = await OrderTable.findRows({
- filter: { status: "pending" },
- orderBy: "createdAt",
- orderDirection: "desc",
- limit: 10,
- });
-
- // Update rows
- await OrderTable.updateRows({
- rows: [{ id: orders[0].id, status: "completed" }],
- });
+const { rows } = await OrderTable.createRows({
+ rows: [{ userId: "user123", total: 99.99, status: "pending" }],
+});
+```
+
+### Finding rows
+
+```typescript
+const { rows: orders } = await OrderTable.findRows({
+ filter: { status: "pending" },
+ orderBy: "createdAt",
+ orderDirection: "desc",
+ limit: 10,
+});
+```
+
+### Updating rows
+
+```typescript
+await OrderTable.updateRows({
+ rows: [{ id: orders[0].id, status: "completed" }],
+});
+```
+
+### Getting a single row
+
+```typescript
+const row = await OrderTable.getRow({ id: 42 });
+```
+
+### Deleting rows
+
+
+```ts By filter
+await OrderTable.deleteRows({ status: "cancelled" });
+```
+
+```ts By IDs
+await OrderTable.deleteRowIds([1, 2, 3]);
+```
+
+```ts All rows
+await OrderTable.deleteAllRows();
+```
+
+
+### Upserting rows
+
+Insert rows or update them if they match on a key column:
+
+```typescript
+const { inserted, updated } = await OrderTable.upsertRows({
+ keyColumn: "userId",
+ rows: [{ userId: "user123", total: 149.99, status: "pending" }],
+});
+```
+
+## Filtering
+
+Use filter operators for advanced queries:
+
+```typescript
+const { rows } = await OrderTable.findRows({
+ filter: {
+ total: { $gt: 100 },
+ status: { $in: ["pending", "processing"] },
+ },
+});
+```
+
+### Available operators
+
+| Operator | Description |
+|----------|-------------|
+| `$eq` | Equal to |
+| `$ne` | Not equal to |
+| `$gt` | Greater than |
+| `$gte` | Greater than or equal |
+| `$lt` | Less than |
+| `$lte` | Less than or equal |
+| `$in` | In array |
+| `$nin` | Not in array |
+| `$exists` | Field exists |
+| `$regex` | Regex match |
+
+### Logical operators
+
+Combine filters with `$and`, `$or`, and `$not`:
+
+```typescript
+const { rows } = await OrderTable.findRows({
+ filter: {
+ $or: [
+ { status: "pending" },
+ { total: { $gt: 500 } },
+ ],
+ },
+});
+```
+
+## Semantic search
+
+Search across columns marked as `searchable` using natural language:
+
+```typescript
+const { rows } = await TicketsTable.findRows({
+ search: "VPN connection issues",
+ limit: 10,
+});
+```
+
+You can combine `search` with `filter` for hybrid queries:
+
+```typescript
+const { rows } = await TicketsTable.findRows({
+ search: "network problems",
+ filter: { status: "open" },
+ limit: 20,
+});
+```
+
+## Aggregation
+
+Use the `group` parameter to perform aggregation operations:
+
+```typescript
+const { rows } = await OrderTable.findRows({
+ group: {
+ status: "key",
+ total: ["sum", "avg", "count"],
},
});
+// rows: [{ statusKey: "pending", totalSum: 500, totalAvg: 100, totalCount: 5 }, ...]
```
+### Available operations
+
+| Operation | Applies to | Description |
+|-----------|-----------|-------------|
+| `key` | All types | Group by this value |
+| `count` | All types | Count of rows |
+| `sum` | Numbers | Sum of values |
+| `avg` | Numbers | Average of values |
+| `max` | Numbers, strings, dates | Maximum value |
+| `min` | Numbers, strings, dates | Minimum value |
+| `unique` | All types | Array of unique values |
+
## Reference
+### Table props
+
- Unique name for the table. Must be 30 characters or less, cannot start with a number, can contain only letters, numbers, and underscores, and must end with 'Table'.
+ Unique name for the table. Must start with a letter or underscore, be 35 characters or less, contain only letters, numbers, and underscores, and end with `Table`.
- Optional factor for table operations. Defaults to 1.
+ Search weighting factor (1–30). Higher values increase the table's relevance in semantic search results. Defaults to 1.
+
+
+ Name of a column to use as the key for upsert operations. Must reference an existing column.
+
+
+ Optional metadata tags for the table. Keys must be 3–50 characters, values 1–255 characters.
Object mapping column names to their definitions. Each key is a column name. Values can be either a simple Zod schema or an object with advanced column configuration options.
-
+
-
+
- Whether this column is computed from other columns. Defaults to false.
+ Whether this column should be searchable via the `search` parameter in `findRows()`. Defaults to false.
- Whether this column should be searchable. Defaults to false.
+ Whether this column is computed from other columns. Defaults to false.
- Array of column names this computed column depends on. Required for computed columns.
+ Array of column names this computed column depends on. Required when `computed` is true.
- Async function that computes the column value from the row data. Required for computed columns.
+ Async function that computes the column value from the row data. Required when `computed` is true.
+### Table methods
+
+
+
+ Fetch a single row by its ID.
+
+
+ Find rows with optional `filter`, `search`, `group`, `orderBy`, `orderDirection`, `limit`, and `offset`.
+
+
+ Insert one or more rows. Set `waitComputed` to true to wait for computed columns to be calculated before returning.
+
+
+ Update one or more rows. Each row must include an `id` field. Only changed fields need to be provided.
+
+
+ Insert or update rows based on a `keyColumn`. Defaults to `id` if not specified.
+
+
+ Delete rows matching a filter.
+
+
+ Delete rows by their IDs.
+
+
+ Delete all rows in the table.
+
+
+ Get table metadata including row count and indexing status.
+
+
+
Tables are automatically synced with Botpress Cloud. When you deploy your agent, table schemas are created or updated to match your definitions.
diff --git a/adk/concepts/tools.mdx b/adk/concepts/tools.mdx
index d0ba53d0..d6e051ae 100644
--- a/adk/concepts/tools.mdx
+++ b/adk/concepts/tools.mdx
@@ -6,7 +6,7 @@ Tools are functions that can be called by the AI model during conversations. The
## Creating a tool
-Create a tool in `src/tools/`:
+Create a tool anywhere in your agent's source code:
```typescript
import { Autonomous, z } from "@botpress/runtime";
diff --git a/adk/concepts/workflows/overview.mdx b/adk/concepts/workflows.mdx
similarity index 56%
rename from adk/concepts/workflows/overview.mdx
rename to adk/concepts/workflows.mdx
index 58f1cfee..84a75bb7 100644
--- a/adk/concepts/workflows/overview.mdx
+++ b/adk/concepts/workflows.mdx
@@ -1,6 +1,5 @@
---
title: Workflows
-sidebarTitle: Overview
---
Workflows are long-running processes that handle complex, multi-step operations or scheduled tasks. Unlike [conversations](/adk/concepts/conversations), workflows can run independently on a schedule or be triggered by events.
@@ -19,7 +18,7 @@ import { Workflow } from "@botpress/runtime";
export default new Workflow({
name: "my-workflow",
description: "A workflow that processes data",
- handler: async ({}) => {
+ handler: async ({ step }) => {
// Workflow logic
},
});
@@ -44,7 +43,7 @@ export default new Workflow({
## Steps
-By default, workflows time out after 2 minutes—if you need to run a workflow for longer, you should use the `step` function. This lets you break the workflow up into a series of steps that each take only a few seconds to run individually:
+By default, workflows time out after 5 minutes. For longer workflows, use the `step` function to break the workflow into a series of persisted steps:
```typescript highlight={4-17}
export default new Workflow({
@@ -68,23 +67,44 @@ export default new Workflow({
});
```
-Steps are *persisted*—if a workflow is interrupted, it can resume from the last completed step. This provides better handling when errors occur in complex, long-running workflows.
+Steps are *persisted* — if a workflow is interrupted, it can resume from the last completed step. This provides better handling when errors occur in complex, long-running workflows.
- You can nest steps inside other steps—each step will complete when each of its sub-steps complete.
+ You can nest steps inside other steps — each step will complete when each of its sub-steps complete.
-### Reference
+### Step parameters
+
+```typescript
+const data = await step("fetch-data", async ({ attempt }) => {
+ return await fetchDataFromAPI();
+}, { maxAttempts: 5 });
+```
-The `step` object also provides many additional methods for workflow control:
+
+
+ Unique identifier for this step within the workflow.
-
- Learn about all available step methods
-
+
+ Make sure you set a unique identifier for each step. Otherwise, the second step won't execute and will reuse the output of the first step.
+
+
+
+ Function to execute. Receives the current attempt number.
+
+
+ Optional configuration object.
+
+
+ Maximum number of retry attempts if the step fails. Defaults to 5.
+
+
+
+
### Handling failing steps
-If a step (or a [step method](/adk/concepts/workflows/steps#methods)) fails, it'll throw a rejected promise. This will fail not only the current step, but the *entire workflow*.
+If a step fails, it throws a rejected promise. This will fail not only the current step, but the *entire workflow*.
To avoid this, make sure you catch errors gracefully:
@@ -96,7 +116,7 @@ try {
}
```
-If the method you're using has an `options.maxAttempts` field, it'll throw an error after the maximum number of retries has been exceeded. You can track the retries using the `attempt` parameter and catch any errors:
+If the method you're using has an `options.maxAttempts` field, it throws an error after the maximum number of retries has been exceeded:
```ts highlight={5, 8, 10-12}
try {
@@ -113,6 +133,146 @@ try {
}
```
+## Step methods
+
+The `step` object provides methods for workflow control:
+
+### `step.listen`
+
+Put the workflow into listening mode, waiting for external events to resume:
+
+```typescript
+await step.listen("wait-for-approval");
+// Workflow pauses here until triggered
+```
+
+### `step.sleep`
+
+Pause workflow execution for a specified duration:
+
+```typescript
+await step.sleep("wait-5-min", 5 * 60 * 1000);
+```
+
+### `step.sleepUntil`
+
+Sleep until a specific date:
+
+```typescript
+await step.sleepUntil("wait-until-noon", new Date("2025-01-15T12:00:00Z"));
+```
+
+### `step.fail`
+
+Mark the workflow as failed and stop execution:
+
+```typescript
+if (!user.isVerified) {
+ await step.fail("User verification required");
+}
+```
+
+### `step.abort`
+
+Immediately abort the workflow execution without marking it as failed:
+
+```typescript
+if (shouldPause) {
+ step.abort();
+}
+```
+
+### `step.progress`
+
+Record a progress checkpoint without performing any action:
+
+```typescript
+await step.progress("Started processing");
+// ... do work ...
+await step.progress("Finished processing");
+```
+
+### `step.waitForWorkflow`
+
+Wait for another workflow to complete before continuing:
+
+```typescript
+const childWorkflow = await childWorkflowInstance.start({});
+const result = await step.waitForWorkflow("wait-for-child", childWorkflow.id);
+```
+
+### `step.executeWorkflow`
+
+Start another workflow and wait for it to complete:
+
+```typescript
+import ProcessingWorkflow from "../workflows/processing";
+
+const result = await step.executeWorkflow(
+ "process-data",
+ ProcessingWorkflow,
+ { data: inputData }
+);
+```
+
+### `step.map`
+
+Process an array of items in parallel with controlled concurrency:
+
+```typescript
+const results = await step.map(
+ "process-users",
+ users,
+ async (user, { i }) => await processUser(user),
+ { concurrency: 5, maxAttempts: 3 }
+);
+```
+
+### `step.forEach`
+
+Process an array of items without collecting results:
+
+```typescript
+await step.forEach(
+ "notify-users",
+ users,
+ async (user) => await sendNotification(user),
+ { concurrency: 10 }
+);
+```
+
+### `step.batch`
+
+Process items in sequential batches:
+
+```typescript
+await step.batch(
+ "bulk-insert",
+ records,
+ async (batch) => await database.bulkInsert(batch),
+ { batchSize: 100 }
+);
+```
+
+### `step.request`
+
+Request data from a conversation and wait for a response. Requires defining `requests` in the workflow:
+
+```typescript
+export default new Workflow({
+ name: "order-workflow",
+ requests: {
+ orderId: z.object({
+ orderId: z.string(),
+ }),
+ },
+ handler: async ({ step }) => {
+ const data = await step.request("orderId", "Please provide the order ID");
+ // data is typed based on the request schema
+ },
+});
+```
+
## Output
Workflows can return an output that matches the output schema:
@@ -132,6 +292,145 @@ export default new Workflow({
});
```
+## Workflow methods
+
+Workflows can be started and managed programmatically.
+
+### `workflow.start()`
+
+Start a new workflow instance:
+
+```typescript
+import ProcessingWorkflow from "../workflows/processing";
+
+const instance = await ProcessingWorkflow.start({ orderId: "12345" });
+console.log("Started workflow:", instance.id);
+```
+
+### `workflow.getOrCreate()`
+
+Get an existing workflow or create a new one with deduplication:
+
+```typescript
+const instance = await ProcessingWorkflow.getOrCreate({
+ key: "order-12345", // Unique key for deduplication
+ input: { orderId: "12345" },
+ statuses: ["pending", "in_progress"], // Only match workflows with these statuses
+});
+```
+
+### `Workflow.get()`
+
+Load any workflow instance by its ID:
+
+```typescript
+import { Workflow } from "@botpress/runtime";
+
+const instance = await Workflow.get("workflow-id");
+```
+
+### `workflow.asTool()`
+
+Convert a workflow into a tool for use with `execute()`:
+
+```typescript highlight={2, 9}
+import { Conversation } from "@botpress/runtime";
+import ProcessingWorkflow from "../workflows/processing";
+
+export default new Conversation({
+ channel: "*",
+ handler: async ({ execute }) => {
+ await execute({
+ instructions: "You are a helpful assistant.",
+ tools: [ProcessingWorkflow.asTool()],
+ });
+ },
+});
+```
+
+### `workflow.provide()`
+
+Provide data in response to a workflow data request (used with `step.request()`):
+
+```typescript highlight={1, 6-9}
+import { isWorkflowDataRequest } from "@botpress/runtime";
+import OrderWorkflow from "../workflows/order";
+
+export default new Conversation({
+ channel: "*",
+ handler: async ({ type, request }) => {
+ if (type === "workflow_request") {
+ await OrderWorkflow.provide(request, { orderId: "12345" });
+ }
+ },
+});
+```
+
+## Workflow instance
+
+When you start a workflow, you get a workflow instance with additional methods:
+
+### `instance.cancel()`
+
+Cancel a running workflow:
+
+```typescript
+const instance = await ProcessingWorkflow.start({ orderId: "12345" });
+await instance.cancel();
+```
+
+### `instance.setTimeout()`
+
+Extend the timeout of a running workflow:
+
+```typescript
+// Relative duration
+await instance.setTimeout({ in: "30m" });
+
+// Absolute timestamp
+await instance.setTimeout({ at: "2025-01-15T12:00:00Z" });
+```
+
+### `instance.complete()`
+
+Complete a workflow early with output (only available inside the handler):
+
+```typescript
+export default new Workflow({
+ name: "my-workflow",
+ output: z.object({ result: z.string() }),
+ handler: async ({ workflow }) => {
+ workflow.complete({ result: "done early" });
+ },
+});
+```
+
+## Helper functions
+
+### `isWorkflowDataRequest()`
+
+Check if an event is a workflow data request:
+
+```typescript
+import { isWorkflowDataRequest } from "@botpress/runtime";
+
+if (isWorkflowDataRequest(event)) {
+ // Handle the workflow data request
+}
+```
+
+### `isWorkflowCallback()`
+
+Check if an event is a workflow callback:
+
+```typescript
+import { isWorkflowCallback } from "@botpress/runtime";
+
+if (isWorkflowCallback(event)) {
+ // Handle the workflow callback
+}
+```
+
## Reference
### Workflow props
@@ -197,22 +496,20 @@ export default new Workflow({
### Handler parameters
-The handler function receives workflow context and provides step management:
-
- The validated input object matching the workflow's input schema. Typed based on your input schema.
+ The validated input object matching the workflow's input schema.
- Workflow state object that persists across workflow executions and steps. Typed based on your state schema.
+ Workflow state object that persists across workflow executions and steps.
- Botpress client for making API calls (tables, events, etc.).
+ Botpress client for making API calls.
+
+
+ Function to execute autonomous AI logic. See [Execute props](/adk/concepts/conversations#execute-props) for the full reference.
- Function to execute autonomous AI logic. Used to run the agent with instructions, tools, knowledge bases, etc.
-
-
- Instructions for the AI agent. Can be a string or a function that returns a string.
-
-
- Optional array of tools the agent can use.
-
-
- Optional array of objects the agent can interact with.
-
-
- Optional record of exit handlers.
-
-
- Optional abort signal to cancel execution.
-
-
- Optional hooks for customizing behavior (`onBeforeTool`, `onAfterTool`, `onTrace`, etc.).
-
-
- Optional temperature for the AI model. Defaults to 0.7.
-
-
- Optional model name(s) to use. Can be a string, array of strings, or a function that returns either.
-
-
- Optional array of knowledge bases to use.
-
-
- Optional maximum number of iterations (loops). Defaults to 10.
-
-
+ The current workflow instance. Provides access to `id`, `name`, `tags`, and methods like `cancel()`, `complete()`, and `setTimeout()`.
-
-## Methods
-
-Workflows can be started and managed programmatically:
-
-### `workflow.start()`
-
-Start a new workflow instance:
-
-```typescript
-import ProcessingWorkflow from "../workflows/processing";
-
-const instance = await ProcessingWorkflow.start({ orderId: "12345" });
-console.log("Started workflow:", instance.id);
-```
-
-### `workflow.getOrCreate()`
-
-Get an existing workflow or create a new one with deduplication:
-
-```typescript
-const instance = await ProcessingWorkflow.getOrCreate({
- key: "order-12345", // Unique key for deduplication
- input: { orderId: "12345" },
- statuses: ["pending", "in_progress"], // Only match workflows with these statuses
-});
-```
-
-### `workflow.asTool()`
-
-Convert a workflow into a tool for use with `execute()`:
-
-```typescript highlight={2, 9}
-import { Conversation } from "@botpress/runtime";
-import ProcessingWorkflow from "../workflows/processing";
-
-export default new Conversation({
- channel: "*",
- handler: async ({ execute }) => {
- await execute({
- instructions: "You are a helpful assistant.",
- tools: [ProcessingWorkflow.asTool()],
- });
- },
-});
-```
-
-### `workflow.provide()`
-
-Provide data in response to a workflow data request (used with `step.request()`):
-
-```typescript highlight={1, 6-9}
-import { isWorkflowDataRequest } from "@botpress/runtime";
-import OrderWorkflow from "../workflows/order";
-
-export default new Conversation({
- channel: "*",
- handler: async ({ type, request }) => {
- if (type === "workflow_request") {
- await OrderWorkflow.provide(request, { orderId: "12345" });
- }
- },
-});
-```
-
-## Helper functions
-
-### `isWorkflowDataRequest()`
-
-Check if an event is a workflow data request:
-
-```typescript
-import { isWorkflowDataRequest } from "@botpress/runtime";
-
-if (isWorkflowDataRequest(event)) {
- // Handle the workflow data request
-}
-```
-
-### `isWorkflowCallback()`
-
-Check if an event is a workflow callback:
-
-```typescript
-import { isWorkflowCallback } from "@botpress/runtime";
-
-if (isWorkflowCallback(event)) {
- // Handle the workflow callback
-}
-```
-
diff --git a/adk/concepts/workflows/steps.mdx b/adk/concepts/workflows/steps.mdx
deleted file mode 100644
index f6486a6d..00000000
--- a/adk/concepts/workflows/steps.mdx
+++ /dev/null
@@ -1,341 +0,0 @@
----
-title: Steps
----
-
-
-This page contains a full reference for the [`step` function](/adk/concepts/workflows/overview#steps).
-
-```typescript
-const data = await step("fetch-data", async () => {
- return await fetchDataFromAPI();
-});
-```
-
-## Parameters
-
-
- Unique identifier for this step within the workflow.
-
-
- Make sure you set a unique identifier for each step. Otherwise, the second step won't execute and will reuse the output of the first step.
-
-
-
-
- Function to execute. Receives the current attempt number.
-
-
- Optional configuration object.
-
-
- Maximum number of retry attempts if the step fails. Defaults to 5.
-
-
-
-
----
-
-## Methods
-
-The following methods are available on each step:
-
-### `listen`
-
-Put the workflow into listening mode, waiting for external events to resume:
-
-```typescript
-await step.listen("wait-for-approval");
-// Workflow pauses here until triggered
-```
-
-**Parameters**:
-
-
- The name of the step.
-
-
----
-
-### `sleep`
-
-Pause workflow execution for a specified duration:
-
-```typescript
-// Sleep for 5 minutes
-await step.sleep("wait-5-min", 5 * 60 * 1000);
-```
-
-**Parameters**:
-
-
- The name of the step.
-
-
- Duration to sleep in milliseconds.
-
-
----
-
-### `sleepUntil`
-
-Sleep until a specific date:
-
-```typescript
-await step.sleepUntil("wait-until-noon", new Date("2025-01-15T12:00:00Z"));
-```
-
-**Parameters**:
-
-
- The name of the step.
-
-
- The date to sleep until.
-
-
----
-
-### `fail`
-
-Mark the workflow as failed and stop execution:
-
-```typescript
-if (!user.isVerified) {
- await step.fail("User verification required");
-}
-```
-
-**Parameters**:
-
-
- Description of why the workflow failed.
-
-
----
-
-### `abort`
-
-Immediately abort the workflow execution without marking it as failed:
-
-```typescript
-if (shouldPause) {
- step.abort();
-}
-```
-
-**Parameters**:
-
-No parameters.
-
----
-
-### `progress`
-
-Record a progress checkpoint without performing any action:
-
-```typescript
-await step.progress("Started processing");
-// ... do work ...
-await step.progress("Finished processing");
-```
-
-**Parameters**:
-
-
- The name of the progress checkpoint.
-
-
----
-
-### `waitForWorkflow`
-
-Wait for another workflow to complete before continuing:
-
-```typescript
-const childWorkflow = await childWorkflowInstance.start({});
-const result = await step.waitForWorkflow("wait-for-child", childWorkflow.id);
-```
-
-**Parameters**:
-
-
- The name of the step.
-
-
- ID of the workflow to wait for.
-
-
----
-
-### `executeWorkflow`
-
-Start another workflow and wait for it to complete:
-
-```typescript
-import ProcessingWorkflow from "../workflows/processing";
-
-const result = await step.executeWorkflow(
- "process-data",
- ProcessingWorkflow,
- { data: inputData }
-);
-```
-
-**Parameters**:
-
-
- The name of the step.
-
-
- The workflow instance to execute.
-
-
- Input data for the workflow (typed based on workflow's input schema).
-
-
----
-
-### `map`
-
-Process an array of items in parallel with controlled concurrency:
-
-```typescript
-const results = await step.map(
- "process-users",
- users,
- async (user, { i }) => await processUser(user),
- { concurrency: 5, maxAttempts: 3 }
-);
-```
-
-**Parameters**:
-
-
- The name of the map operation.
-
-
- Array of items to process.
-
-
- Function to process each item. Receives the item and its index.
-
-
- Optional configuration object.
-
-
- Maximum retry attempts per item. Defaults to 5.
-
-
- Maximum number of concurrent operations. Defaults to 1.
-
-
-
-
----
-
-### `forEach`
-
-Process an array of items without collecting results:
-
-```typescript
-await step.forEach(
- "notify-users",
- users,
- async (user) => await sendNotification(user),
- { concurrency: 10 }
-);
-```
-
-**Parameters**:
-
-
- The name of the forEach operation.
-
-
- Array of items to process.
-
-
- Function to process each item. Receives the item and its index.
-
-
- Optional configuration object.
-
-
- Maximum retry attempts per item. Defaults to 5.
-
-
- Maximum number of concurrent operations. Defaults to 1.
-
-
-
-
----
-
-### `batch`
-
-Process items in sequential batches:
-
-```typescript
-await step.batch(
- "bulk-insert",
- records,
- async (batch) => await database.bulkInsert(batch),
- { batchSize: 100 }
-);
-```
-
-**Parameters**:
-
-
- The name of the batch operation.
-
-
- Array of items to process.
-
-
- Function to process each batch. Receives the batch array and starting index.
-
-
- Optional configuration object.
-
-
- Number of items per batch. Defaults to 20.
-
-
- Maximum retry attempts per batch. Defaults to 5.
-
-
-
-
----
-
-### `request`
-
-Request data from a conversation and wait for a response. Requires defining `requests` in the workflow:
-
-```typescript
-export default new Workflow({
- name: "order-workflow",
- requests: {
- orderId: z.object({
- orderId: z.string(),
- }),
- },
- handler: async ({ step }) => {
- const data = await step.request("orderId", "Please provide the order ID");
- // data is typed based on the request schema
- },
-});
-```
-
-**Parameters**:
-
-
- The name of the request (must be defined in workflow's `requests` field).
-
-
- Message to display to the user describing what data is needed.
-
-
- Optional custom name for the step. Defaults to the request name.
-
diff --git a/adk/introduction.mdx b/adk/introduction.mdx
index 7fd6ceee..3e771410 100644
--- a/adk/introduction.mdx
+++ b/adk/introduction.mdx
@@ -10,7 +10,7 @@ sidebarTitle: Introduction
export default new Conversation({
channel: "*",
- handler: async ({ execute, message }) => {
+ handler: async ({ execute }) => {
await execute({
instructions: "You are a helpful assistant.",
});
@@ -24,7 +24,7 @@ sidebarTitle: Introduction
export default new Conversation({
channel: "*",
- handler: async ({ execute, message }) => {
+ handler: async ({ execute }) => {
await execute({
instructions: "You are a helpful assistant.",
knowledge: [WebsiteKB]
diff --git a/adk/project-structure.mdx b/adk/project-structure.mdx
index d64e2db1..5f9eb64d 100644
--- a/adk/project-structure.mdx
+++ b/adk/project-structure.mdx
@@ -8,16 +8,13 @@ ADK projects follow a consistent structure that organizes your agent's code, con
-
+
-
-
-
-
+
-
+
@@ -26,13 +23,18 @@ ADK projects follow a consistent structure that organizes your agent's code, con
-
+
+
+
+
+
+
@@ -120,7 +122,7 @@ Links the agent to a bot in your Workspace:
{
"botId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"workspaceId": "wkspace_xxxxxxxxxxxxxxxxxxxxxxxxxx",
- "devId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
+ "apiUrl": "https://api.botpress.cloud"
}
```
@@ -233,24 +235,6 @@ export default new Action({
});
```
-### `src/tools/`
-
-Tools that the AI model can call during conversations. Tools are provided to the `execute()` function.
-
-```typescript
-import { Autonomous, z } from "@botpress/runtime";
-
-export default new Autonomous.Tool({
- name: "myTool",
- description: "A tool that does something useful",
- input: z.object({ query: z.string() }),
- output: z.object({ result: z.string() }),
- handler: async ({ query }) => {
- return { result: "Tool output" };
- },
-});
-```
-
### `src/tables/`
Table schemas that define data storage structures. Tables are automatically synced with Botpress Cloud.
@@ -356,13 +340,15 @@ const maxRetries = configuration.maxRetries;
## Generated files
-The ADK generates TypeScript types in `.adk/bot/.botpress`:
+When you run `adk dev` or `adk build`, the ADK generates files in the `.adk/` directory:
-- `.botpress/types/` - Type definitions for integrations and interfaces
-- `.botpress/dist/` - Compiled output (created during build)
+- `.adk/*.d.ts` — Type definitions for actions, conversations, workflows, tables, triggers, and more
+- `.adk/client.ts` — Generated client wrapper
+- `.adk/integrations/` — Integration type definitions
+- `.adk/bot/` — Generated Botpress bot project and compiled output
-Don't edit files in the `.botpress` directory manually. They are automatically generated and will be overwritten.
+Don't edit files in the `.adk/` directory manually. They are automatically generated and will be overwritten.
## Next steps
diff --git a/adk/quickstart.mdx b/adk/quickstart.mdx
index 22842d83..81272e0d 100644
--- a/adk/quickstart.mdx
+++ b/adk/quickstart.mdx
@@ -46,54 +46,61 @@ adk init my-agent
▄▀█ █▀▄ █▄▀ Botpress ADK
█▀█ █▄▀ █░█ Initialize New Agent
+ Step 1 of 2
+
Select a template:
-› Blank
- Start with an empty agent project
+› blank
+ Empty project with just the folder structure
- Hello World
- Start with a conversation agent project
+ hello-world
+ A working chatbot with chat and webchat integrations
```
-Select the `Hello World` template, then follow the prompts to set up the agent. This creates a new `my-agent` directory with:
+Select the `hello-world` template. Next, choose your preferred package manager:
-- `agent.config.ts` - Agent configuration and integration dependencies
-- `agent.json` - Configuration for your linked bot
-- `package.json` - Project configuration
-- `tsconfig.json` - TypeScript configuration
-- `src/` - Source code directory structure
+```txt
+ ▄▀█ █▀▄ █▄▀ Botpress ADK
+ █▀█ █▄▀ █░█ Initialize New Agent
-## Install dependencies
+ Step 2 of 2
-Once you've finished the setup, navigate into your project:
-```bash
-cd my-agent
+Select a package manager:
+
+● Bun
+ Install with bun
+○ pnpm
+ Install with pnpm
+○ Yarn
+ Install with yarn
+○ npm
+ Install with npm
```
-Install the dependencies:
+If you are logged in, the CLI will also prompt you to select a workspace to link your agent to.
-
+The CLI automatically installs dependencies and creates a `my-agent` directory with:
-```bash bun
-bun install
-```
+- `agent.config.ts` — Agent configuration, state schemas, and integration dependencies
+- `agent.json` — Workspace and bot IDs for your linked agent
+- `package.json` — Project configuration with `dev`, `build`, and `deploy` scripts
+- `tsconfig.json` — TypeScript configuration
+- `src/` — Source code with subdirectories for `conversations`, `actions`, `tables`, `triggers`, `workflows`, and `knowledge`
+- `.mcp.json` — MCP configuration for AI coding assistants (Claude Code, Cursor, etc.)
+- `CLAUDE.md` / `AGENTS.md` — AI assistant instructions for your project
-```bash pnpm
-pnpm install
-```
+
+ The CLI generates `.mcp.json` for Claude Code by default. To add MCP configuration for other supported editors, run `adk mcp:init --all` inside your project.
+
-```bash yarn
-yarn install
-```
+Navigate into your project:
-```bash npm
-npm install
+```bash
+cd my-agent
```
-
-
## Test your agent
Now, you're ready to test your agent.
@@ -129,9 +136,9 @@ Type "exit" or press ESC key to quit
>>
```
-### View the ADK console
+### View the Control Panel
-Visit [`http://localhost:3001/`](http://localhost:3001/) to access the interactive ADK console. This gives you an intuitive, visual breakdown of your agent project and lets you [configure its integration's settings](/adk/managing-integrations#integration-configuration).
+While `adk dev` is running, visit [`http://localhost:3001/`](http://localhost:3001/) to access the Control Panel. This gives you a visual breakdown of your agent project — browse actions, tools, workflows, tables, and [configure integration settings](/adk/managing-integrations#integration-configuration).
## Build your agent
@@ -141,37 +148,20 @@ Compile your agent for production:
adk build
```
-```txt
-📦 Loaded project: my-agent
-📝 Generating assets types...
-✅ Assets types generated
-🔨 Generating Botpress bot project...
-✅ Bot project generated
-🏗️ Building agent...
-
-✅ Build completed successfully!
-📦 Output directory: /.adk/bot/.botpress/dist
-```
-
This creates an optimized build in the `.adk/bot/.botpress/dist` directory.
## Deploy your agent
-When you're ready, you can deploy your agent to Botpress Cloud:
+
+ If you skipped workspace selection during `adk init`, run `adk login` then `adk link` to connect your agent before deploying.
+
+
+When you're ready, deploy your agent to Botpress Cloud:
```bash
adk deploy
```
-```txt
-📤 Deploying bot to Botpress...
-
-✅ Agent deployed successfully!
-🌐 Your agent is now live on Botpress
-🤖 Bot ID: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
-🏢 Workspace ID: wkspace_xxxxxxxxxxxxxxxxxxxxxxxxxx
-```
-
This uploads your agent to your Botpress workspace and makes it available for use.
diff --git a/docs.json b/docs.json
index 079060fc..caf9e275 100644
--- a/docs.json
+++ b/docs.json
@@ -96,10 +96,7 @@
"group": "Concepts",
"pages": [
"/adk/concepts/conversations",
- {
- "group": "Workflows",
- "pages": ["/adk/concepts/workflows/overview", "/adk/concepts/workflows/steps"]
- },
+ "/adk/concepts/workflows",
"/adk/concepts/actions",
"/adk/concepts/tables",
"/adk/concepts/triggers",