Skip to content

Scripting

For situations where templates and built-in effects aren't enough, Bonsai lets you write custom JavaScript that runs during a conversation. Scripts are used via Script tools invoked with the call_tool effect.

What Scripts Can Do

Scripts are useful for:

  • Conditional logic — If the customer has retried 3 times, escalate.
  • Data processing — Parse a webhook response, reformat a phone number, calculate a value.
  • Flow control — Decide which stage to go to next based on complex conditions.
  • Input manipulation — Clean up or enrich what the AI sees as the user's message.

Safety

Scripts run in a secure sandbox with strict limits:

  • 5-second time limit — Long-running code is automatically stopped.
  • 16 MB memory limit — Prevents runaway memory usage.
  • No internet access — Scripts cannot make network requests (use a webhook tool for that).
  • No file access — Scripts cannot read or write files on the server.

This means scripts are safe to use — they can only read and modify conversation data.

What's Available in Scripts

Data You Can Read and Modify

VariableAccessDescription
varsRead & WriteStage variables — the main place to store conversation data
userProfileRead & WriteThe end user's profile
userInputRead & WriteThe current user message (modify to change what the AI sees)

Data You Can Read Only

VariableDescription
conversationIdCurrent conversation's unique ID
projectIdCurrent project ID
stageIdCurrent stage ID
stageFull stage information (name, available actions, metadata)
historyAll messages in the conversation so far
eventsComplete event log (messages, actions, stage changes, tool calls)
actionsClassification results — which actions were matched
resultsResults from webhooks and tool calls
timeCurrent date/time with timezone (same fields as in templates)
originalUserInputThe unmodified user input before any scripts changed it
userInputSourceHow the user communicated: 'text', 'voice', or null
stageVarsVariables from all stages (keyed by stage ID)
constsProject-level constants defined in Design > Global Memory > Constants
projectProject-level settings: project.timezone, project.languageCode, project.language
copySelected sample copy content (string) with any decorator applied; empty string if no copy was matched
copyContentRaw selected sample copy content (string) before the decorator is applied
sampleCopyAll sample copies active for this stage (array of objects with name, trigger, and content fields)

Common Patterns

Setting Variables

javascript
vars.retryCount = (vars.retryCount || 0) + 1;

vars.order = {
  id: "ORD-123",
  status: "pending"
};

Reading Constants

Project constants (defined in Design > Global Memory > Constants) are available as consts:

javascript
const maxRetries = consts.maxRetries || 3;
const companyName = consts.companyName;

if (vars.retryCount >= maxRetries) {
  vars.needsEscalation = true;
}

Conditional Logic

javascript
if (vars.retryCount >= 3) {
  vars.needsEscalation = true;
  vars.escalationReason = "Max retries exceeded";
}

Processing Tool Results

javascript
const response = results.tools?.orderLookup;
if (response) {
  vars.customerName = response.firstName + " " + response.lastName;
  vars.accountTier = response.subscription?.tier || "free";
}

Time-Based Decisions

javascript
const hour = parseInt(time.hour, 10);
const isWeekend = time.dayOfWeek === 'Saturday' || time.dayOfWeek === 'Sunday';
vars.isBusinessHours = !isWeekend && hour >= 9 && hour < 17;

Flow Control Functions

These functions let a script control the conversation flow. They take effect after the script finishes running.

FunctionWhat It Does
goToStage(stageId)Navigate to a different stage
endConversation(reason?)Gracefully end the conversation
abortConversation(reason?)Immediately terminate the conversation
prescriptResponse(text)Send a specific message instead of generating one with the AI
suppressResponse()Don't generate any response for this turn

goToStage is ignored in lifecycle actions

goToStage() has no effect when called from a script running inside an On Enter or On Leave lifecycle action. If you need stage navigation, use a regular user-triggered action.

Example — Smart Routing

javascript
if (vars.retryCount >= 3) {
  goToStage('escalation-stage');
} else if (vars.taskComplete) {
  prescriptResponse('All done! Is there anything else I can help with?');
}

History Helpers

Convenient functions for working with conversation history:

FunctionWhat It Does
lastMessage(role?)Get the text of the last message, optionally filtered by 'user' or 'assistant'
messageCount(role?)Count total messages, optionally by role
historyText(opts?)Get formatted conversation transcript (options: n for limit, role to filter)
historyContains(text, role?)Check if any message contains specific text (case-insensitive)
stageMessages(role?)Get only messages from the current stage

Example — Detecting Patterns

javascript
if (historyContains('cancel', 'user')) {
  vars.wantsToCancel = true;
}

if (messageCount('user') >= 5) {
  vars.longConversation = true;
}

Utility Functions

FunctionWhat It Does
uuid()Generate a unique ID
formatDate(iso, locale?, options?)Format a date string (e.g., formatDate(time.iso, 'en-US', { dateStyle: 'long' }))

Debugging

Use console.log(), console.warn(), and console.error() in scripts. The output is captured in the conversation's event log, which you can view in the Monitor section.

javascript
console.log("Processing order:", vars.orderId);
console.log("Retry count:", vars.retryCount);

Limitations to Keep in Mind

  • Scripts are synchronous — no async/await or promises.
  • No external modules or require() / import.
  • No setTimeout or setInterval.
  • For network calls, create a Webhook tool and invoke it via the Call Tool effect instead of scripts.
  • Always check if variables exist before using them to avoid errors.

Released under the Apache-2.0 License.