or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

bot-controller.mdbot-worker.mdconversations.mddialog-wrapper.mdindex.mdmessage-handling.mdteams-integration.mdtesting.md
tile.json

conversations.mddocs/

Conversations & Dialogs

Multi-turn conversation system with thread management, conditional logic, and state persistence. Supports complex dialog flows with branching, variable collection, and user input validation.

Capabilities

BotkitConversation Constructor

Creates a new conversation dialog with the specified ID and controller reference.

/**
 * Create a new BotkitConversation object
 * @param dialogId A unique identifier for this dialog, used to later trigger this dialog
 * @param controller A pointer to the main Botkit controller
 */
constructor(dialogId: string, controller: Botkit);

Usage Example:

import { BotkitConversation } from "botkit";

const convo = new BotkitConversation("greeting_flow", controller);

Basic Message Flow

Add messages to conversation threads that will be sent sequentially.

/**
 * Add a non-interactive message to the default thread
 * @param message Message template to be sent
 */
say(message: Partial<BotkitMessageTemplate> | string): BotkitConversation;

/**
 * Add a message template to a specific thread
 * @param message Message template to be sent
 * @param thread_name Name of thread to which message will be added
 */
addMessage(message: Partial<BotkitMessageTemplate> | string, thread_name: string): BotkitConversation;

interface BotkitMessageTemplate {
  text: ((template: any, vars: any) => string) | string[];
  action?: string;
  execute?: { script: string; thread?: string; };
  quick_replies?: ((template: any, vars: any) => any[]) | any[];
  attachments?: ((template: any, vars: any) => any[]) | any[];
  blocks?: ((template: any, vars: any) => any[]) | any[];
  attachment?: ((template: any, vars: any) => any) | any;
  attachmentLayout?: string;
  channelData?: any;
  collect: { key?: string; options?: BotkitConvoTrigger[]; };
}

Usage Examples:

// Simple text messages
const convo = new BotkitConversation("welcome", controller);
convo.say("Hello! Welcome to our service.");
convo.say("Let me show you around...");

// Rich message with attachments
convo.say({
  text: "Here's some information:",
  attachments: [{
    title: "Getting Started",
    text: "Follow these steps to begin.",
  }]
});

// Message with quick replies
convo.say({
  text: "Choose an option:",
  quick_replies: [
    { title: "Option 1", payload: "opt1" },
    { title: "Option 2", payload: "opt2" }
  ]
});

// Add to specific thread
convo.addMessage("This is in a different thread", "help_thread");

Question and Response

Ask questions and handle user responses with conditional logic.

/**
 * Add a question to the default thread
 * @param message a message that will be used as the prompt
 * @param handlers one or more handler functions defining conditional actions
 * @param key name of variable to store response in
 */
ask(
  message: Partial<BotkitMessageTemplate> | string,
  handlers: BotkitConvoHandler | BotkitConvoTrigger[],
  key: {key: string} | string | null
): BotkitConversation;

/**
 * Add a question to a specific thread
 * @param message A message that will be used as the prompt
 * @param handlers One or more handler functions defining conditional actions
 * @param key Name of variable to store response in
 * @param thread_name Name of thread to which message will be added
 */
addQuestion(
  message: Partial<BotkitMessageTemplate> | string,
  handlers: BotkitConvoHandler | BotkitConvoTrigger[],
  key: {key: string} | string | null,
  thread_name: string
): BotkitConversation;

interface BotkitConvoHandler {
  (answer: string, convo: BotkitDialogWrapper, bot: BotWorker, message: BotkitMessage): Promise<any>;
}

interface BotkitConvoTrigger {
  type?: string;
  pattern?: string | RegExp;
  handler: BotkitConvoHandler;
  default?: boolean;
}

Usage Examples:

// Simple question with single handler
convo.ask("What is your name?", async (response, convo, bot) => {
  await bot.say(`Hello, ${response}!`);
}, { key: "name" });

// Question with conditional responses
convo.ask("Do you like pizza?", [
  {
    pattern: "yes",
    type: "string",
    handler: async (response, convo, bot) => {
      await convo.gotoThread("pizza_lover");
    }
  },
  {
    pattern: "no",
    type: "string", 
    handler: async (response, convo, bot) => {
      await convo.gotoThread("pizza_hater");
    }
  },
  {
    default: true,
    handler: async (response, convo, bot) => {
      await bot.say("I don't understand. Please answer yes or no.");
      await convo.repeat();
    }
  }
], { key: "likes_pizza" });

// Question with regex pattern
convo.ask("Enter your email:", [
  {
    pattern: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
    type: "regex",
    handler: async (response, convo, bot) => {
      await bot.say("Thanks! Email saved.");
    }
  },
  {
    default: true,
    handler: async (response, convo, bot) => {
      await bot.say("Please enter a valid email address.");
      await convo.repeat();
    }
  }
], { key: "email" });

Thread Management

Control conversation flow with threads and actions.

/**
 * Add an action to the conversation timeline
 * @param action An action or thread name
 * @param thread_name The name of the thread to which this action is added
 */
addAction(action: string, thread_name?: string): BotkitConversation;

Usage Examples:

// Create multiple threads
convo.say("Welcome!");
convo.addAction("choose_path");

// Choose path thread
convo.addMessage("What would you like to do?", "choose_path");
convo.ask("Type 'help' or 'start':", [
  {
    pattern: "help",
    handler: async (response, convo, bot) => {
      await convo.gotoThread("help_thread");
    }
  },
  {
    pattern: "start",
    handler: async (response, convo, bot) => {
      await convo.gotoThread("start_thread");
    }
  }
], null, "choose_path");

// Help thread
convo.addMessage("Here's how to use this bot...", "help_thread");
convo.addAction("complete", "help_thread");

// Start thread
convo.addMessage("Let's get started!", "start_thread");
convo.addAction("complete", "start_thread");

// Actions available:
// - Thread names: Jump to that thread
// - 'complete': End dialog successfully
// - 'stop': End dialog as cancelled
// - 'repeat': Repeat last message
// - 'timeout': End dialog due to timeout

Dialog Composition

Combine multiple dialogs for complex conversation flows.

/**
 * Call a child dialog, wait for completion, and store results
 * @param dialog_id the id of another dialog
 * @param key_name the variable name to store results (defaults to dialog_id)
 * @param thread_name the name of thread to add this call to
 */
addChildDialog(dialog_id: string, key_name?: string, thread_name?: string): BotkitConversation;

/**
 * Handoff to another dialog (parent will not resume)
 * @param dialog_id the id of another dialog
 * @param thread_name the name of thread to add this call to
 */
addGotoDialog(dialog_id: string, thread_name?: string): BotkitConversation;

Usage Examples:

// Create profile collection dialog
const profileDialog = new BotkitConversation("profile_dialog", controller);
profileDialog.ask("What is your name?", async (res, convo, bot) => {}, { key: "name" });
profileDialog.ask("What is your age?", async (res, convo, bot) => {}, { key: "age" });
controller.addDialog(profileDialog);

// Main onboarding dialog that uses profile dialog
const onboardDialog = new BotkitConversation("onboard", controller);
onboardDialog.say("Welcome! Let's collect your profile.");
onboardDialog.addChildDialog("profile_dialog", "profile");
onboardDialog.say("Thanks {{vars.profile.name}}! Onboarding complete.");

// Handoff example
const emergencyDialog = new BotkitConversation("emergency", controller);
emergencyDialog.say("Transferring to emergency procedures...");
emergencyDialog.addGotoDialog("emergency_handler");

Event Handlers

Bind functions to conversation lifecycle events.

/**
 * Register a handler function that will fire before a given thread begins
 * @param thread_name A valid thread defined in this conversation
 * @param handler A handler function
 */
before(thread_name: string, handler: (convo: BotkitDialogWrapper, bot: BotWorker) => Promise<any>): void;

/**
 * Bind a function to run after the dialog has completed
 * @param handler handler function with results and bot worker
 */
after(handler: (results: any, bot: BotWorker) => void): void;

/**
 * Bind a function to run whenever a user answers a specific question
 * @param variable name of the variable to watch for changes
 * @param handler a handler function that will fire when variable changes
 */
onChange(variable: string, handler: (response, convo, bot) => Promise<any>): void;

Usage Examples:

// Before thread handler
convo.before("payment_thread", async (convo, bot) => {
  // Set up payment variables
  convo.setVar("payment_method", "credit_card");
  convo.setVar("currency", "USD");
});

// After dialog completion
convo.after(async (results, bot) => {
  console.log("Dialog completed with results:", results);
  
  // Save to database
  await saveUserData({
    name: results.name,
    email: results.email,
    preferences: results.preferences
  });
  
  // Send confirmation
  await bot.say("Your information has been saved!");
});

// Variable change handler
convo.onChange("email", async (response, convo, bot) => {
  // Validate email format
  if (!response.includes("@")) {
    await bot.say("That doesn't look like a valid email.");
    await convo.repeat();
  }
});

Dialog Control Wrapper

The BotkitDialogWrapper provides conversation control methods within handlers.

class BotkitDialogWrapper {
  /** Variables and user responses from this conversation */
  vars: { [key: string]: any };
  
  /** Jump immediately to the first message in a different thread */
  gotoThread(thread: string): Promise<void>;
  
  /** Repeat the last message sent on the next turn */
  repeat(): Promise<void>;
  
  /** Stop the dialog */
  stop(): Promise<void>;
  
  /** Set the value of a variable available to messages */
  setVar(key: string, val: any): void;
}

Usage Examples:

// Use dialog wrapper in handlers
convo.ask("Continue?", async (response, convo, bot) => {
  if (response.toLowerCase() === "yes") {
    convo.setVar("confirmed", true);
    await convo.gotoThread("confirmed_thread");
  } else {
    await convo.stop();
  }
}, { key: "continue" });

// Access variables
convo.say("Hello {{vars.name}}! You are {{vars.age}} years old.");

// Before handler using wrapper
convo.before("summary", async (convo, bot) => {
  const userData = {
    name: convo.vars.name,
    email: convo.vars.email,
    score: calculateScore(convo.vars)
  };
  convo.setVar("summary", userData);
});

Managing Conversations

Add conversations to the bot and handle completion.

// Add dialog to controller (from bot-controller)
controller.addDialog(dialog: Dialog): void;

// Handle dialog completion (from bot-controller)  
controller.afterDialog(dialog: Dialog | string, handler: BotkitHandler): void;

Usage Examples:

// Create and add dialog
const surveyDialog = new BotkitConversation("customer_survey", controller);
// ... define dialog structure ...
controller.addDialog(surveyDialog);

// Handle completion
controller.afterDialog("customer_survey", async (bot, results) => {
  await bot.say("Thanks for completing the survey!");
  
  // Process results
  await processSurveyResults(results);
});

// Start dialog from message handler
controller.hears("survey", "message", async (bot, message) => {
  await bot.beginDialog("customer_survey");
});

Advanced Features

Variable Templating

Use Mustache templating in messages to insert variables:

// Set variables and use in templates
convo.ask("What's your name?", async (response, convo, bot) => {
  convo.setVar("user_name", response);
}, { key: "name" });

convo.say("Nice to meet you, {{vars.user_name}}!");
convo.say("Your name has {{vars.user_name.length}} characters.");

Dynamic Content

Use functions to generate dynamic content:

convo.say({
  text: (template, vars) => {
    const hour = new Date().getHours();
    const greeting = hour < 12 ? "Good morning" : "Good afternoon";
    return `${greeting}, ${vars.name}!`;
  }
});

convo.say({
  quick_replies: (template, vars) => {
    return vars.user_type === "admin" ? 
      [{ title: "Admin Panel", payload: "admin" }] :
      [{ title: "User Dashboard", payload: "user" }];
  }
});

Error Handling

Handle conversation errors and edge cases:

// Timeout handling
convo.addAction("timeout", "main_thread");

// Error recovery
convo.ask("Enter a number:", [
  {
    pattern: /^\d+$/,
    handler: async (response, convo, bot) => {
      convo.setVar("number", parseInt(response));
    }
  },
  {
    default: true,
    handler: async (response, convo, bot) => {
      await bot.say("Please enter a valid number.");
      await convo.repeat();
    }
  }
], { key: "number" });