Skip to content

Actions & Effects

Actions are the primary mechanism for the AI to perform behaviors beyond generating text. Each stage defines a set of actions, and each action contains an ordered list of effects that execute when the action is triggered.

Action Structure

Each action in the actions map has a key (the action ID) and a value with these fields:

FieldDescription
nameDisplay name
conditionOptional JavaScript expression evaluated to determine if the action is active
triggerOnUserInputWhether this action can be triggered by user speech/text (default: true)
triggerOnClientCommandWhether this action can be triggered by a client command
triggerOnTransformationWhether this action runs after context transformation
classificationTriggerDescriptive label shown to the classifier LLM that tells it when this action should fire; the LLM matches user intent against this label and returns the action ID in its response
overrideClassifierIdUse a specific classifier instead of the stage default
parametersParameters extracted by the classifier when triggering
effectsOrdered array of effects to execute
examplesExample user phrases (included in classifier prompt)
watchedVariablesMap of variable paths to trigger conditions (new, changed, removed, any)
metadataArbitrary JSON

Stage Lifecycle Actions

Stages support three reserved lifecycle actions with special names (prefixed with __):

__on_enter

Runs when the conversation enters this stage — either at the start of a conversation or via a go_to_stage effect. Executes before the enterBehavior (generate response or await input).

Restricted effects: cannot use end_conversation, abort_conversation, or go_to_stage. Calling goToStage() inside a script tool is also silently ignored.

__on_leave

Runs when the conversation is about to leave this stage (before loading the new stage). Useful for cleanup or persisting state.

Restricted effects: cannot use go_to_stage or generate_response. Calling goToStage() inside a script tool is also silently ignored.

__on_fallback

Runs when the classifier found no matching user-triggered action. Acts as the default behavior for unrecognized input. Has no effect restrictions.

Conversation Lifecycle Actions

In addition to stage-level lifecycle hooks, global actions with reserved IDs fire at conversation-level lifecycle events, independent of which stage is active. See Global Actions — Conversation Lifecycle Hooks for the full reference, restrictions, and use cases.

Reserved Global Action IDWhen it fires
__conversation_startOnce, after the conversation and first stage are initialised
__conversation_resumeWhen a previously-interrupted conversation is resumed
__conversation_endWhen the conversation is gracefully ended
__conversation_abortWhen the conversation is aborted (immediate stop)
__conversation_failedWhen the conversation encounters a fatal error

There is also a moderation hook:

Reserved Global Action IDWhen it fires
__moderation_blockedUser input is blocked by content moderation

See Global Actions — Content Moderation Hook for details.

Trigger Modes

Actions can be triggered in multiple ways:

  • User input (triggerOnUserInput: true) — The classifier analyzes the user's text and matches it to the action's classificationTrigger.
  • Client command (triggerOnClientCommand: true) — The client application sends a run_action WebSocket command.
  • Transformation (triggerOnTransformation: true) — A context transformer modifies stage variables, and the action's watchedVariables matches the change.

Conditions

The condition field accepts a JavaScript expression that evaluates to a boolean. If the condition evaluates to false, the action is excluded from the classifier's consideration set. Variables are accessible in conditions:

javascript
vars.retryCount < 3 && userProfile.tier === 'premium'

Action Parameters

Actions can define parameters that the classifier extracts from user input:

json
"parameters": [
  {
    "name": "productName",
    "type": "string",
    "description": "The name of the product the user is asking about",
    "required": true
  },
  {
    "name": "quantity",
    "type": "number",
    "description": "How many units",
    "required": false
  }
]

Extracted parameters are available in effects via context.results.actions.<actionId>.<paramName>.

Effects

Effects are the building blocks of action behavior. They execute in order within an action.

end_conversation

Gracefully ends the conversation. Optionally generates a final AI response.

json
{ "type": "end_conversation", "reason": "User's issue has been resolved" }

abort_conversation

Immediately ends the conversation without generating any AI response.

json
{ "type": "abort_conversation", "reason": "Session timeout" }

go_to_stage

Navigates to a different stage. Triggers __on_leave on the current stage and __on_enter on the target stage.

json
{ "type": "go_to_stage", "stageId": "troubleshooting" }

modify_user_input

Replaces the user's input text using a Handlebars template. This modifies what the LLM sees as the user's message.

json
{
  "type": "modify_user_input",
  "template": "The user wants to know about {{vars.currentTopic}}: {{userInput}}"
}

modify_variables

Performs operations on stage variables:

json
{
  "type": "modify_variables",
  "modifications": [
    { "variableName": "status", "operation": "set", "value": "verified" },
    { "variableName": "retryCount", "operation": "reset" },
    { "variableName": "history", "operation": "add", "value": "step completed" },
    { "variableName": "pendingItems", "operation": "remove", "value": "item-1" }
  ]
}

Operations:

  • set — Set a variable to a value
  • reset — Clear a variable
  • add — Append a value to an array
  • remove — Remove a value from an array

modify_user_profile

Same operations as modify_variables, but applied to the user's profile instead.

json
{
  "type": "modify_user_profile",
  "modifications": [
    { "fieldName": "preferredLanguage", "operation": "set", "value": "es" }
  ]
}

call_tool

Invokes a tool. The tool's type determines both its execution behaviour and when it runs relative to other effects (see Effect Execution Priority). See Tools.

json
{
  "type": "call_tool",
  "toolId": "sentiment-analyzer",
  "parameters": { "text": "{{userInput}}" }
}

Results are stored differently depending on the tool type:

  • smart_function and script tools — stored under context.results.tools.<toolId>
  • webhook tools — stored under context.results.webhooks.<toolId>

generate_response

Explicitly triggers AI response generation. Two modes:

Generated (LLM produces the response):

json
{ "type": "generate_response", "responseMode": "generated" }

Prescripted (predefined text, no LLM call):

json
{
  "type": "generate_response",
  "responseMode": "prescripted",
  "prescriptedResponses": ["Welcome! How can I help?", "Hi there! What can I do for you?"],
  "prescriptedSelectionStrategy": "random"
}

Selection strategies: random (pick randomly) or round_robin (cycle through).

change_visibility

Sets the visibility of the current turn's messages (both the user input and the AI response). This controls whether those messages are included when building the conversation history sent to the LLM, templates and scripts on future turns.

json
{
  "type": "change_visibility",
  "target": "action",
  "id": "collect-payment-info",
  "visibility": "never"
}
FieldDescription
target"action" or "stage" — what the id refers to
idID of the action or stage this effect belongs to
visibility"always", "stage", "never", or "conditional" (see Message Visibility)
conditionJavaScript expression evaluated against the conversation context — required when visibility is "conditional"

The visibility is recorded on the message event and evaluated when building history on subsequent turns. See Message Visibility for how each value is interpreted.

Effect Execution Priority

Effects from all triggered actions are gathered into a single global list, sorted by priority, and then conflict-resolved before execution. Effects within the same priority tier run in the order they appeared across all actions.

PriorityEffect type
1call_tool (webhook tools)
2call_tool (smart_function tools)
3modify_variables
4modify_user_profile
5modify_user_input
6call_tool (script tools)
50change_visibility
100generate_response
200end_conversation
201abort_conversation
202go_to_stage

call_tool effects are assigned a priority at runtime based on the referenced tool's type: webhook tools run at priority 1, script tools at priority 6, and smart_function tools at priority 2.

Conflict Resolution

  • Multiple go_to_stage — only the first one (lowest priority index) is kept; the rest are discarded
  • abort_conversation + end_conversationabort_conversation wins; end_conversation is removed
  • Multiple modify_user_input — all are applied in sequence, each receiving the output of the previous
  • Multiple change_visibility — the last one applied wins (highest priority index)

Execution Flow

When a user sends input, the system runs classifiers and context transformers in parallel, merges results, and executes effects:

Effects within a single action run in order, and their results can be used by subsequent effects. If any effect triggers end_conversation, abort_conversation, or go_to_stage, it takes effect after all current effects complete.

Message Visibility

Every message event (user input and AI response) can carry a visibility setting that controls whether it appears in the conversation history sent to the LLM on future turns. Visibility is set by the change_visibility effect and evaluated each time history is built.

ValueBehaviour
alwaysAlways included in history (default when no visibility is set)
neverNever included in history
stageIncluded only when the current stage matches the stage the message was recorded in
conditionalIncluded only when a JavaScript condition expression evaluates to truthy

The conditional value supports an arbitrary JavaScript expression evaluated against the full conversation context. It can be set via the change_visibility effect (using the condition field) or directly on the message event data:

json
{
  "type": "change_visibility",
  "target": "action",
  "id": "my-action",
  "visibility": "conditional",
  "condition": "vars.includeHistory === true"
}

Visibility is evaluated lazily: it is recorded on the message event when the turn completes and re-evaluated on every subsequent turn when history is assembled. This means a stage or conditional message can move in and out of the visible history as the conversation progresses.

Released under the Apache-2.0 License.