Skip to main content

Overview

This specification defines an agent-based DSL that compiles to an intermediate representation (AgentIR) executed by the platform runtime. Unlike the step-based approach (which creates dialog flows), this DSL defines agents that reason about goals, use tools intelligently, and manage complex workflows.

Implementation Status Legend

Features in this spec are marked with their implementation status:
  • No mark — Fully implemented and production-ready
  • ⚡ — Partial implementation — core functionality works but some sub-features are pending
  • 🗺️ — Roadmap — type definitions exist but runtime execution is not yet implemented

1. Design Philosophy

1.1 Step-Based vs Agent-Based

AspectStep-Based (Current)Agent-Based (New)
Flow ControlExplicit numbered stepsGoal-driven reasoning
TransitionsON_SUCCESS -> 5LLM decides next action
ResponsesTemplate stringsLLM-generated contextual
Tool UsageScripted callsAgent decides when/what
FlexibilityRigid, predictableAdaptive, intelligent

1.2 Core Principles

  1. Goal-Oriented: Agents work toward defined goals, not through scripts
  2. Constraint-Guarded: Business rules enforced via constraints, not step order
  3. Memory-Enabled: Agents remember user preferences across sessions
  4. Composable: Agents can delegate to sub-agents or handoff entirely
  5. Human-in-the-Loop: Clear escalation paths when needed

2. Document Structure

AGENT: <name>

# Identity
GOAL: <string or multiline block>
PERSONA: <multiline string>
LIMITATIONS: <list>

# Capabilities
TOOLS: <tool definitions>
GATHER: <information requirements>

# State Management
MEMORY:
  session: <list>
  persistent: <list>
  remember: <triggers>
  recall: <retrieval rules>

# Business Rules
CONSTRAINTS:
  <phase>:
    - REQUIRE <condition>
      ON_FAIL: <response template>
GUARDRAILS: <input/output safety checks>

# Multi-Format Output
TEMPLATES: <named templates with channel-specific formats>

# Context-Dependent Behavior (standalone documents)
# BEHAVIOR_PROFILE: <name>  — defined in separate .behavior_profile.abl files
# Referenced via USE BEHAVIOR_PROFILE: <name> in agent documents

# Flow Control
FLOW: <optional deterministic step sequence>
DELEGATE: <sub-agent calls>
HANDOFF: <agent transfers>
ESCALATE: <human escalation>
COMPLETE: <completion conditions>

# Error Handling
ON_ERROR: <error handlers>

# Lifecycle & Configuration
EXECUTION: <model, timeouts, iteration limits>
HOOKS: <before_turn, after_turn lifecycle handlers>
ON_START: <initial actions when agent activates>
ACTION_HANDLERS: <interactive UI button/action definitions>
Dual Format Support: ABL supports both the traditional uppercase keyword format (.agent.abl) and a YAML-based format (.agent.yaml). The .abl format requires uppercase keywords (AGENT:, GOAL:, TOOLS:). The .yaml format uses lowercase keywords (agent:, goal:, tools:). Keywords are NOT interchangeable between formats — using agent: in a .abl file will produce a parser error.
GOAL: may be a single quoted string or a multiline block (GOAL: |) when the objective needs multiple clauses. Keep the goal declarative: describe the outcome the agent is optimizing for, not step-by-step control flow.

3. Section Specifications

3.1 AGENT Declaration

AGENT: <PascalCase_Name>
Rules:
  • Must be unique within the system
  • PascalCase with underscores allowed
  • Maps to metadata.name in the compiled AgentIR
Examples:
AGENT: Hotel_Search
AGENT: Payment_Processor
AGENT: Customer_Support

3.2 GOAL

Defines what the agent is trying to achieve. Used in system prompt and for completion detection.
GOAL: "<imperative statement describing the agent's purpose>"
Rules:
  • Must be a clear, achievable objective
  • Should be measurable (agent can determine when done)
  • Injected into LLM system prompt
Examples:
GOAL: "Help user find and book a hotel that meets all booking policies"
GOAL: "Process user's refund request and confirm resolution"
GOAL: "Collect issue details and route to appropriate support team"

3.3 PERSONA

Multi-line description of agent’s personality and behavior. Directly injected into system prompt.
PERSONA: |
  <line 1>
  <line 2>
  ...
Rules:
  • Use YAML multi-line syntax (|)
  • Describe tone, style, approach
  • Can reference memory (e.g., “References user’s past preferences”)
Example:
PERSONA: |
  Helpful, knowledgeable hotel booking specialist.
  Friendly but efficient - doesn't waste user's time.
  Asks clarifying questions only when necessary.
  Always explains why if a booking can't be made.
  References user's past preferences when making suggestions.

3.4 LIMITATIONS

Prompt-level boundaries that guide how the agent should respond. Injected into the system prompt; use CONSTRAINTS for deterministic runtime checks.
LIMITATIONS:
  - "<limitation 1>"
  - "<limitation 2>"
Rules:
  • Clear statements of what’s NOT possible
  • Helps the LLM explain scope or decline inappropriate requests
  • Should match actual system capabilities
Example:
LIMITATIONS:
  - "Cannot guarantee room availability until booking is confirmed"
  - "Cannot override blackout dates or minimum stay policies"
  - "Cannot process payments directly - must handoff to Payment agent"
  - "Cannot access bookings made outside this system"

3.5 TOOLS

Defines tools the agent can use. Compiles to ToolDefinition entries in the AgentIR.
TOOLS:
  <tool_name>(<params>) -> <return_type>
  ...
Parameter Syntax:
param_name: type [= default]
Supported Types:
  • string - Text value
  • number - Numeric value (int or float)
  • boolean - True/false
  • date - Date value (ISO 8601 or natural language)
  • array - List of values
  • object - Structured object
  • Hotel[] - Array of typed objects
  • {field: type, ...} - Inline object type
Examples:
TOOLS:
  # Simple tool with typed return
  check_blackout_dates(destination: string, checkin: date, checkout: date) -> {allowed: boolean, reason?: string}

  # Tool with default parameter
  search_hotels(destination: string, checkin: date, checkout: date, guests: number = 2) -> Hotel[]

  # Tool with complex return
  get_hotel_details(hotel_id: string) -> {
    name: string,
    rating: number,
    amenities: string[],
    rooms_available: number,
    price_per_night: number
  }

  # Action tool (no meaningful return)
  create_reservation(hotel_id: string, guest_info: GuestInfo) -> Reservation
Tool implementation note: Agent TOOLS: declarations define the callable contract. The HTTP implementation stored in the Tool Library (.tools.abl) may use either body: | for a static payload or body_template: | for a templated payload that resolves runtime values such as {{session.idCard}} or {{env.API_BASE_URL}}.
Import/apply note: During project bundle import, inline agent TOOLS: signatures are previewed as tool additions and apply auto-creates project tool stubs when no companion .tools.abl file exists in the bundle. The next export materializes those synthesized stubs under tools/<name>.tools.abl. Outside bundle import, referenced tools still need to exist in the project tool registry before compilation/runtime use.

Tool Auth Properties

Tools that access external services can declare auth requirements using indented sub-properties. These compile to fields on ToolDefinition in the AgentIR and are consumed by the runtime auth middleware.
TOOLS:
  gmail_lookup(query: string) -> Result
    auth_profile: "google-creds"
    auth_jit: true
    consent: preflight
    connection: per_user
    description: "Look up Gmail messages"
PropertyIR FieldTypeDescription
auth_profileauth_profile_refstringReference to an auth profile by name or config variable (e.g. "{{config.GOOGLE_AUTH}}")
auth_jitjit_authbooleanWhether this tool requires just-in-time authentication
consentconsent_mode'preflight' | 'inline'preflight prompts for all auth upfront; inline prompts on first tool use
connectionconnection_mode'per_user' | 'shared'per_user requires user-scoped credentials; shared uses tenant-level
Rules:
  • auth_jit: true without auth_profile emits warning AUTH_JIT_WITHOUT_PROFILE
  • consent without auth_profile is ignored (orphan consent)
  • Templated refs ("{{config.X}}") are preserved verbatim for runtime name resolution
  • When multiple tools reference the same auth_profile, requirements are merged: scopes are unioned, preflight wins over inline, per_user wins over shared
After compilation, the runtime collects all auth requirements via collectAuthRequirements() into AuthRequirementIR[] for preflight consent checks and credential resolution.

Tool Confirmation Properties

Tools that can mutate external state should declare confirmation behavior explicitly instead of relying on an implicit runtime default.
TOOLS:
  charge_card(amount: number) -> Result
    description: "Charge the customer's card"
    side_effects: true
    confirm: when_side_effects
    immutable: [amount]
PropertyIR FieldTypeDescription
confirmconfirmation.require'always' | 'never' | 'when_side_effects'When to require user approval before the tool executes
immutableconfirmation.immutable_paramsstring[]Parameters locked after approval so the execution payload cannot drift
  • The compiler emits warning SIDE_EFFECT_TOOL_WITHOUT_CONFIRMATION when side_effects: true is set without an explicit confirm policy.
  • The runtime does not auto-default confirmation behavior. Choose confirm: when_side_effects, confirm: always, or confirm: never deliberately for each side-effecting tool.

3.5.1 ENTITIES (Named Entity Registry)

ENTITIES: defines reusable extraction contracts that GATHER fields can reference with ENTITY_REF. Use it when several fields or agents need the same enum, synonym set, or pattern validation.
ENTITIES:
  request_type:
    TYPE: enum
    VALUES: [REQ_REFUND, REQ_EXCHANGE, REQ_CANCEL]
    SYNONYMS:
      REQ_REFUND: [refund, money back, reimburse]
      REQ_EXCHANGE: [exchange, swap]
      REQ_CANCEL: [cancel, cancellation]

  booking_ref:
    TYPE: pattern
    PATTERN: "^[A-Z]{2}[0-9]{4,6}$"

GATHER:
  request:
    ENTITY_REF: request_type
    PROMPT: "What type of request do you have?"
PropertyApplies ToDescription
TYPEallenum or pattern.
VALUESenumCanonical values stored in session state.
SYNONYMSenumUser-facing phrases mapped to each canonical value.
PATTERNpatternRegex used to validate extracted values.
SENSITIVEenum, patternMarks values as personal or regulated data when referenced by gather fields.
SENSITIVE_DISPLAYsensitive definitionsredact, replace, or mask; follows the same renderer rules as gather privacy fields.
MASK_CONFIGmask modeOptional show_first, show_last, and char configuration.
PII_TYPEmask modeShape hint such as email, phone, ssn, credit_card, address, name, or custom.
Rules:
  • ENTITY_REF must point to an entity declared in ENTITIES:.
  • A GATHER field with ENTITY_REF should not also declare TYPE, OPTIONS, or VALUES; the referenced entity owns that contract.
  • Entity definitions compile into AgentIR.entities; gather fields keep the reference so runtime extraction, validation, and UI surfaces can resolve the canonical definition.
  • Privacy fields on an entity follow the same sensitive rendering behavior as GATHER privacy attributes.

3.5.2 LOOKUP_TABLES (Reference Data)

LOOKUP_TABLES: defines lookup-backed reference sets for validation, normalization, and suggestions. Agent-local lookup tables are supported but experimental; project runtime config lookup tables are the canonical shared source for production reference data.
LOOKUP_TABLES:
  airports:
    source: inline
    values: [LAX, JFK, CDG, LHR]
    case_sensitive: false
    fuzzy_match: true
    fuzzy_threshold: 0.85

  hotels:
    source: collection
    table_name: lookup_hotels
    field: name
FieldDescription
sourceinline, collection, or api.
valuesInline value list when source: inline.
table_nameCollection/runtime-config table name for shared lookup data.
fieldField to read from collection-backed rows.
case_sensitiveWhether matching preserves case.
fuzzy_matchEnables approximate matching.
fuzzy_thresholdSimilarity threshold for fuzzy matching.
  • Use GATHER field semantics to reference a lookup table from a field.
  • Agent-local tables compile into AgentIR.lookup_tables and emit an experimental warning.
  • Prefer project runtime config lookup tables when multiple agents share the same reference set.

3.5.3 NLU, INTENTS, MULTI_INTENT, MESSAGES, and TESTS

These top-level sections are parsed for compatibility with authored and imported agents. Runtime support varies by section; prefer the current HANDOFF, GATHER, DIGRESSIONS, and GUARDRAILS constructs for new behavior unless the section below names a wired runtime contract.
NLU:
  entities:
    - NAME: ssn
      TYPE: pattern
      PATTERN: "\\d{3}-\\d{2}-\\d{4}"
      SENSITIVE: true

INTENTS:
  LEXICAL_FALLBACK: when_unavailable
  location_lookup: "Help users find a nearby service location"

MULTI_INTENT:
  strategy: primary_queue
  max_intents: 5
  confidence_threshold: 0.7
  enabled: true

MESSAGES:
  error_default: "Sorry, something went wrong. Please try again."
  escalation_notice: TEMPLATE(escalation_notice)

TESTS:
  - name: "happy path"
    input: "track my order"
    expect_intent: order_inquiry
SectionRuntime contract
NLUDefines entity extraction metadata. Prefer ENTITIES for reusable field contracts in new agents.
INTENTSDeclares intent labels and lexical fallback behavior for routing/classification.
MULTI_INTENTConfigures multi-intent detection strategy when the runtime classifier supports it.
MESSAGESOverrides named runtime/user-facing message strings; values may reference TEMPLATE(name).
TESTSAuthoring/evaluation metadata. Parsed for tooling; not a runtime execution section.
INSTRUCTIONSAdditional authoring instructions merged with persona/behavior-profile guidance where supported.

3.6 GATHER

Defines information the agent needs to collect. Agent will intelligently gather these through conversation.
GATHER:
  <field_name>:
    prompt: "<question to ask if not provided>"
    type: <type>
    required: <boolean>
    default: <value>
    validate: "<validation rule>"
Rules:
  • Agent will ask for required fields not yet provided
  • Agent extracts from user messages (doesn’t always ask directly)
  • Validation rules checked before proceeding
Example — GATHER in a reasoning agent (no FLOW): In reasoning mode, GATHER fields are collected conversationally. The LLM uses the prompts as guidance but asks naturally based on conversation context — it may collect multiple fields in a single turn if the user volunteers information.
AGENT: Refund_Agent

GOAL: "Help customers process refunds for eligible orders"

TOOLS:
  lookup_order(order_id: string) -> {order_id: string, items: object[], total: number, status: string}
    description: "Look up order details"
  process_refund(order_id: string, item_id: string, reason: string) -> {refund_id: string, amount: number}
    description: "Process refund for an item"

GATHER:
  order_id:
    prompt: "Could you share your order number?"
    type: string
    required: true
  refund_reason:
    prompt: "What's the reason for the refund?"
    type: string
    required: true
    validate: "Must describe a specific issue"

CONSTRAINTS:
  - REQUIRE lookup_order.status != "already_refunded"
    ON_FAIL: "This order has already been refunded."

COMPLETE:
  - WHEN: refund_id IS SET
    RESPOND: "Your refund of {{process_refund.amount}} has been processed. Reference: {{refund_id}}"
In this reasoning agent, the LLM decides when to ask for each field, when to call lookup_order, and when to call process_refund — all guided by the GOAL and CONSTRAINTS. No FLOW section is needed.
Example — GATHER field definitions (standalone):
GATHER:
  destination:
    prompt: "Where would you like to stay?"
    type: string
    required: true
    validate: "Must be a valid city name"

  checkin:
    prompt: "What's your check-in date?"
    type: date
    required: true
    validate: "Must be today or future date"

  checkout:
    prompt: "What's your check-out date?"
    type: date
    required: true
    validate: "Must be after check-in date"

  guests:
    prompt: "How many guests will be staying?"
    type: number
    required: false
    default: 2
    validate: "Must be between 1 and 10"

  room_preference:
    prompt: "Any room preferences? (king bed, ocean view, etc.)"
    type: string
    required: false

Extraction Strategies

The strategy field controls how the runtime extracts field values from user messages. Three strategies are supported:
StrategyDescriptionUse When
patternRegex and JS library extraction only (no LLM calls)High-volume, low-cost — dates, phones, emails
llmLLM-based extraction with tool-useComplex fields — addresses, preferences
hybridPattern extraction first, LLM fallback for unresolved fieldsBalance of cost and accuracy
The strategy field is only effective inside FLOW GATHER blocks (see §3.20.2). In top-level GATHER, the runtime always uses LLM-based extraction; specifying strategy at the top level is accepted but has no effect.
FLOW:
  collect_info:
    REASONING: false
    GATHER:
      - phone: required
      - destination:
          TYPE: string
          REQUIRED: true
      STRATEGY: hybrid   # Try patterns first, LLM fallback
    THEN: next_step

Field Semantics

The optional semantics block provides extraction hints that improve accuracy across all strategies:
PropertyDescriptionExample
formatHigh-level format hintairport_code, currency_amount, address
componentsStructured sub-parts to extract[street, city, state, zip, country]
unitUnit of measurementUSD, kg, celsius
lookupReference table for validationiata_codes, country_names
convert_toAuto-conversion target unitUSD, km
localeFormatting localeen-US, es-MX
enum_setAllowed enumeration values (alias for top-level options; compiler mirrors into enum_values)[small, medium, large]
Precedence when both options and semantics.enum_set are specified: the top-level options list wins and is written into enum_values. semantics.enum_set is retained on the IR semantics block for round-trip / introspection but does not override the top-level list.

Supported Field Types

Agent Platform provides six base storage types and more than 25 semantic-type mappings for migration from AI for Service Platform: Base Types:
TypeExtractionExample Values
stringLLM or pattern"Paris", "John Smith"
numberRegex + JS coercion42, 17.5
dateJS date library (chrono-node)"2026-03-15", "tomorrow"
emailRegex pattern"user@example.com"
phoneRegex + libphonenumber"+1-555-123-4567"
booleanKeyword matching (yes/no/true/false)true, false
Kore Entity Type Mappings (for migration from AI for Service): Kore platform entity types map to ABL’s type + semantics system. For example:
Kore EntityABL TypeSemantics
LOC_AIRPORTstringformat: airport_code, lookup: iata_codes
LOC_ADDRESSstringformat: address, components: [street, city, state, zip]
CURRENCYnumberunit: currency, format: currency_amount
PERSON_NAMEstringformat: person_name, components: [first, middle, last, title]
DATE_PERIODstringformat: date_range, components: [start, end]
PHONEphone(base type)
EMAILemail(base type)

Validation Rules

Fields support typed validation that runs after extraction:
TypeRule FormatExample
patternRegex stringpattern: "^[A-Z]{2}\\d{6}$" (policy number)
rangeNumeric range expressionrange: "1-10" (guest count)
enumComma-separated allowed valuesenum: "economy, business, first"
customExpression evaluated at runtimecustom: "checkout > checkin"
llmNatural language instructionllm: "Must be a valid city name"
GATHER:
  policy_number:
    prompt: "What is your policy number?"
    type: string
    required: true
    validate:
      type: pattern
      rule: "^POL-[A-Z]{2}\\d{6}$"
      error: "Policy number must be in format POL-XX999999"
      max_retries: 3

  cabin_class:
    prompt: "What class would you like to fly?"
    type: string
    required: true
    validate:
      type: enum
      rule: "economy, premium_economy, business, first"
    infer: true
    infer_confirm: true

Field Activation Modes

Fields can be conditionally activated based on other collected data:
ModeBehavior
requiredAlways prompted (default)
optionalCollected if mentioned, never prompted
progressiveBecomes required when depends_on fields are collected
{ when: expr }Activates when a data-driven condition is true
GATHER:
  has_loyalty:
    prompt: "Are you a loyalty program member?"
    type: boolean
    required: true

  loyalty_number:
    prompt: "What is your loyalty number?"
    type: string
    activation: progressive
    depends_on: [has_loyalty]

  upgrade_preference:
    prompt: "Would you like to use points for an upgrade?"
    type: boolean
    activation:
      when: "has_loyalty == true AND loyalty_points > 5000"

3.7 MEMORY

Defines what the agent remembers within the current conversation, across one execution tree, and across broader user/project lifecycles.
MEMORY:
  session:
    - <variable_name>    # Description
    ...

  persistent:
    - PATH: <namespace>.<field>
      SCOPE: user | project | execution_tree
      ACCESS: read | write | readwrite
    ...

  remember:
    - WHEN: <condition>
      STORE: <value> -> <target>
    ...

  recall:
    - ON: <canonical_event>
      ACTION: inject_context | load_memory | prompt_llm
      ...

3.7.1 Session Memory

Temporary state within the current conversation. Session memory is projected into reasoning context, tool gating, and prompt shaping before each LLM turn.
session:
  - search_results
  - selected_hotel
  - reservation_draft
  - clarification_count

3.7.2 Persistent Memory

Durable memory comes in three scopes:
  • user — facts shared across that user’s sessions
  • project — facts shared across the whole project
  • execution_tree — workflow-scoped facts shared across one handoff tree or long-running execution
persistent:
  - PATH: user.preferred_hotel_chains
    SCOPE: user
    ACCESS: readwrite
  - PATH: project.exchange_rates
    SCOPE: project
    ACCESS: read
  - PATH: workflow.current_quote
    SCOPE: execution_tree
    ACCESS: readwrite
Persistent memory entries may also use explicit read/write lists in compatibility-authored agents:
persistent:
  READS:
    - user.preferences
  WRITES:
    - workflow.current_quote
Session memory declarations can include initial values and reset hints:
session:
  - routing_history
  - handoff_count
      INITIAL: 0
      RESET: per_session
Supported RESET values:
  • per_session initializes once at session start and keeps the value for that conversation.
  • per_activation resets to INITIAL every time the agent is activated, including repeated activations of the same agent.
  • per_step is reserved for step-scoped reset semantics.
  • never avoids runtime reset; prefer persistent memory for durable data.
ACCESS: readwrite grants both read and write capability for a persistent path. Prefer the narrowest access that lets the agent complete its job.
Reserved system identifiers: user_id, project_id, tenant_id, and other system-owned context fields are populated by the platform. Treat them as immutable in public ABL authoring.

3.7.3 Remember Triggers

Remember triggers store new information into durable memory when a condition is met.
remember:
  - WHEN: booking.confirmed == true
    STORE: {destination, hotel_chain, room_type, price, dates} -> user.past_bookings

  - WHEN: user.mentions_loyalty_program == true
    STORE: {program, tier} -> user.loyalty_programs

  - WHEN: quote_ready == true
    STORE: quoted_price -> workflow.current_quote

3.7.4 Recall Instructions

Recall rules load stored facts back into the session at canonical lifecycle events.
recall:
  - ON: session:start
    ACTION: inject_context
    PATHS: [user.preferred_hotel_chains, user.loyalty_programs]
  - ON: tool:search_hotels:after
    ACTION: prompt_llm
    INSTRUCTION: "Prefer hotels aligned with the user's known room and loyalty preferences"
  - ON: agent:*:after
    ACTION: load_memory
    DOMAIN: "travel_preferences"

3.8 CONSTRAINTS

Deterministic runtime checks over session state and execution checkpoints. When a check fails, the runtime executes ON_FAIL.
CONSTRAINTS:
  <label>:
    - REQUIRE|WARN|LIMIT|RESTRICT <condition> [IMPLIES <condition>] [BEFORE calling <tool>|BEFORE returning results]
      WHEN: <condition>                # optional applicability gate
      ON_FAIL: <response or action>
    ...

3.8.1 Constraint Labels

Constraint labels are organizational groupings for related constraints. All constraints are evaluated every turn in declaration order, regardless of label.
LabelTypical authoring use
search_rulesSearch-related checks
booking_rulesBooking-related checks
payment_rulesPayment-related checks
alwaysGeneral checks (common label)
Note: Labels are arbitrary user-defined strings. The compiler flattens all labeled blocks into a single constraint list, and the runtime evaluates them in declaration order every turn. Use labels for readability and organization, not for execution control. Use WHEN for contextual gating and structural BEFORE only for supported checkpoints.

3.8.2 Condition Syntax

condition = operand comparator operand
          | operand "IS SET"
          | operand "IS NOT SET"
          | "NOT" condition
          | condition "AND" condition
          | condition "OR" condition
          | condition "IMPLIES" condition

constraint_rule = ("REQUIRE" | "WARN" | "LIMIT" | "RESTRICT") condition
                  [before_clause]
                  [when_clause]

before_clause = "BEFORE" checkpoint_target
when_clause = "WHEN:" condition

checkpoint_target = "calling" identifier
                  | "returning results"
IMPLIES lowers to implication semantics. LIMIT and RESTRICT are retained as distinct constraint kinds while initially reusing the standard runtime handling path. Non-structural BEFORE forms are still accepted for compatibility, but they compile with a warning and have no runtime effect.

3.8.3 ON_FAIL Actions

Inline form (single action):
ON_FAIL: "<response template with {variables}>"
ON_FAIL: ESCALATE
ON_FAIL: HANDOFF <agent>
ON_FAIL: BLOCK
ON_FAIL: HANDOFF <agent> is executed through the shared runtime violation handler on the active flow and reasoning paths. In practice, checkpointed failures such as BEFORE calling ... and BEFORE returning results can perform a handoff instead of returning a placeholder signal. Structured block form (multiple directives): When ON_FAIL: is followed by an empty value, the parser reads a structured block with these directives:
- REQUIRE some_condition
  ON_FAIL:
    RESPOND: "Please provide the required information."
    COLLECT: [field_a, field_b]
    RETRY: true
    GOTO: previous_step
    THEN: continue
DirectiveTypeDescription
RESPONDstringMessage shown to the user when the constraint fails
COLLECTstring[]Fields to re-collect (comma-separated list or [a, b])
RETRYbooleanWhether to retry the current step (true / false)
GOTOstringJump to a named FLOW step
THENstringControl flow: continue, retry, or custom action
CLEAR is not currently a structured ON_FAIL directive for global constraints. If a failed constraint needs to reset variables before retrying, route to a flow step that performs CLEAR explicitly.
In reasoning mode: Constraints are runtime checks that the platform evaluates around agent actions. The LLM does not need to remember or simulate them itself — the runtime decides when to respond, block, hand off, escalate, or continue based on the compiled rule set.
Example:
CONSTRAINTS:
  booking_requirements: # label only; runtime gating comes from WHEN / BEFORE
    - REQUIRE selected_hotel IS SET BEFORE calling reserve_hotel
      ON_FAIL: "Pick a hotel before I try to reserve it."

    - REQUIRE user.email IS SET
      WHEN: selected_hotel IS SET
      ON_FAIL: "I'll need your email address to send the confirmation. What's your email?"

    - REQUIRE dispute_type == "card" IMPLIES card_unique_id IS SET
      ON_FAIL: "Card disputes require the card unique ID."

  risk_controls:
    - LIMIT clarification_count < 5
      ON_FAIL: ESCALATE

    - RESTRICT beneficiary_country IN ["CU", "IR", "KP", "SY"]
      ON_FAIL: BLOCK

    - REQUIRE fraud_review_complete == true BEFORE returning results
      ON_FAIL: HANDOFF Fraud_Review_Team

3.9 GUARDRAILS

GUARDRAILS define safety and quality validation rules checked at various execution points. Unlike CONSTRAINTS (which check business logic conditions against session data), GUARDRAILS validate content — user inputs, agent outputs, tool parameters, tool results, and handoff context.

Guardrail Kinds

KindWhen EvaluatedPurpose
inputBefore LLM processingBlock harmful/malicious user inputs
outputAfter LLM responseValidate response quality/safety
tool_inputBefore tool executionValidate tool parameters
tool_outputAfter tool executionValidate tool results
handoffBefore agent handoffValidate handoff context
bothInput + OutputShorthand — expands to separate input and output guardrails at compile time

3-Tier Evaluation

Guardrails are evaluated in a tiered architecture for performance.
TierMethodLatencyExamples
Tier 1: LocalCEL expressions, regex patterns<5 msPattern matching, length limits, blocklists
Tier 2: ModelExternal classifier APIs10-200msOpenAI Moderation, AWS Bedrock Guardrails
Tier 3: LLMLLM-as-judge evaluation100-500msSemantic checks, tone analysis

DSL Syntax

GUARDRAILS:
  no_pii_output:
    kind: output
    check: "contains_pii(content)"
    action: redact
    msg: "PII detected in response"

  abusive_input_review:
    kind: input
    llm_check: "Does this input contain abusive, threatening, or harassing language?"
    action: block
    msg: "Inappropriate content detected"

  tool_param_validation:
    kind: tool_input
    check: "abl.word_count(tool_input) >= 200"
    action: block
    msg: "Tool input payload is too large"
both expansion: When kind: both is specified, the compiler creates two guardrails — one input and one output — with identical configuration. This is a convenience for rules that should apply in both directions.
GUARDRAILS:
  no_competitor_mentions:
    kind: both
    check: "abl.matches_pattern(input, '(?i)acme travel|globex bookings')"
    action: filter
    msg: "Competitor mention detected"
    # Compiles to two guardrails:
    #   no_competitor_mentions_input (kind: input)
    #   no_competitor_mentions_output (kind: output)
Important: Local check: expressions are violation predicates: true means the guardrail fires and false means the content passes. Use documented CEL helpers such as abl.contains_pii, abl.matches_pattern, abl.word_count, abl.sentence_count, abl.contains_url, and abl.contains_email. Tone or safety judgments like empathy/toxicity should use llm_check or a provider-backed guardrail rather than undocumented pseudo-check names.

Guardrail Fields

FieldRequiredDescription
kindyesEvaluation point: input, output, tool_input, tool_output, handoff, or both.
checknoLocal CEL-style violation predicate. Use for deterministic checks.
llm_checknoNatural-language evaluator instruction for LLM-as-judge checks.
actionyesAction to apply when the rule fires. See the action table below.
msgnoUser-safe message or trace summary associated with the violation. Do not include secrets or internal remediation.

Actions

ActionBehavior
blockPrevent processing, return message
warnLog warning, continue processing
redactReplace matched content with [REDACTED]
fixApply an automatic repair strategy
reaskAsk the model to regenerate
filterRemove violating portions while preserving content
escalateRoute to human agent
Implementation Status: Guardrails are fully implemented in the runtime. Input guardrails are evaluated pre-message, and the runtime filters by kind to evaluate guardrails at their respective execution points (tool_input, tool_output, handoff, output). See GUARDRAILS_SPEC.md for the full technical specification.

3.10 DELEGATE

Call a sub-agent for a specific task, get result, continue processing. In reasoning mode, the LLM decides when delegation conditions are met — you don’t need to wire delegation into explicit flow steps.
DELEGATE:
  - AGENT: <agent_name>
    WHEN: <condition>
    PURPOSE: "<description>"
    INPUT: {<fields to pass>}
    RETURNS: {<expected return fields>}
    USE_RESULT: "<how to use the result>"
Behavior:
  • Current agent pauses
  • Sub-agent executes with provided input
  • Sub-agent returns result
  • Current agent continues with result
Example:
DELEGATE:
  - AGENT: Loyalty_Lookup
    WHEN: user.mentions_loyalty OR booking.ready
    PURPOSE: "Check loyalty status and available rewards"
    INPUT: {user_id, hotel_chain}
    RETURNS: {loyalty_tier: string, points_balance: number, available_rewards: Reward[]}
    USE_RESULT: "Offer to apply rewards if available and beneficial"

  - AGENT: Price_Optimizer
    WHEN: search_results.count > 0 AND user.flexible_dates == true
    PURPOSE: "Find better prices on nearby dates"
    INPUT: {hotel_id, checkin, checkout, flexibility_days: 3}
    RETURNS: {best_price: number, best_dates: DateRange, savings: number}
    USE_RESULT: "Suggest alternative dates if savings > 15%"

  - AGENT: Availability_Checker
    WHEN: user.selects_hotel
    PURPOSE: "Verify real-time availability"
    INPUT: {hotel_id, room_type, dates}
    RETURNS: {available: boolean, alternative_rooms?: Room[]}
    USE_RESULT: "Proceed with booking or offer alternatives"

3.10 HANDOFF

Transfer control to another agent permanently (or until that agent hands back).
HANDOFF:
  - TO: <agent_name>
    WHEN: <condition>
    CONTEXT:
      pass: [<fields to pass>]
      summary: "<context summary template>"
    RETURN: <boolean>
Parameters:
  • TO: Target agent
  • WHEN: Condition triggering handoff
  • CONTEXT.pass: Data fields to transfer
  • CONTEXT.summary: Human-readable summary for target agent
  • RETURN: If true, control can return; if false, permanent transfer
Runtime behavior when RETURN: true: When a child agent is invoked with RETURN: true, the runtime automatically injects a __return_to_parent__ system tool into the child’s tool set. This allows the child to explicitly return control to the parent supervisor when it encounters a request outside its capabilities (digression handling). The child’s thread is set to waiting status (not completed), preserving its conversation history and gathered data. If the supervisor later re-routes to the same child agent, the runtime resumes the existing waiting thread instead of creating a new one — the child’s prior context, gathered fields, and conversation history are fully preserved. Example:
HANDOFF:
  - TO: Payment_Agent
    WHEN: reservation.ready_for_payment == true
    CONTEXT:
      pass: [reservation, selected_hotel, user.loyalty_programs, user.email]
      summary: |
        User booking {selected_hotel.name} in {destination}
        Dates: {checkin} to {checkout} ({nights} nights)
        Total: ${reservation.total}
        Loyalty: {user.loyalty_programs}
    RETURN: false

  - TO: Flight_Search
    WHEN: user.intent == "also_need_flight"
    CONTEXT:
      pass: [destination, checkin, checkout]
      summary: "User also needs flights to {destination}, arriving by {checkin}"
    RETURN: true  # May return after flight is booked

  - TO: Support_Agent
    WHEN: user.intent == "complaint" OR user.sentiment == "frustrated"
    CONTEXT:
      pass: [conversation_history, booking_reference, user.past_bookings]
      summary: |
        User issue: {detected_issue}
        Booking ref: {booking_reference}
        Sentiment: {user.sentiment}
    RETURN: false
Advanced HANDOFF Features: History Strategy — Controls how conversation history is passed to the target agent:
HANDOFF:
  - TO: Specialist_Agent
    WHEN: needs_specialist == true
    CONTEXT:
      pass: [user_id, query]
      summary: "User needs specialist help"
      history: full          # Pass full conversation history
      # history: none        # Fresh conversation (default)
      # history: summary_only # Pass only the summary
      # history: {last_n: 5} # Pass last 5 messages
StrategyDescription
noneTarget agent starts fresh (default)
summary_onlyPass only the summary field, no message history
fullPass the complete parent conversation
{last_n: N}Pass the last N messages from the parent
Return Mapping — Structure the data returned from a child agent:
HANDOFF:
  - TO: Authentication_Agent
    WHEN: user.is_authenticated == false
    CONTEXT:
      pass: [session_context]
      summary: "User needs authentication"
      grant_memory: [user.last_verified_at]  # 🗺️ Roadmap — not yet implemented
    RETURN: true
    ON_RETURN:
      action: "route_to_booking"
      map:
        user_id: auth_result.user_id
        auth_token: auth_result.token
Async Handoff ⚡ — For long-running operations with push notifications (timeout config is parsed and stored; async completion mechanism is partially implemented):
HANDOFF:
  - TO: Background_Processor
    WHEN: needs_processing == true
    CONTEXT:
      pass: [document_id]
      summary: "Process uploaded document"
    async: true
    asyncTimeout: 300  # 5 minutes
    ON_RETURN: "notify_user"
Remote Agent — Handoff to agents in different services:
HANDOFF:
  - TO: External_Service_Agent
    WHEN: needs_external == true
    remote:
      service_url: "https://other-service.example.com"
      auth_header: "Bearer {{service_token}}"
    CONTEXT:
      pass: [query, user_context]
      summary: "Route to external service"

3.11 ESCALATE

Transfer to human agent with full context.
ESCALATE:
  triggers:
    - WHEN: <condition>
      REASON: "<reason for escalation>"
      PRIORITY: <low|medium|high|critical>
    ...

  context_for_human:
    - <context_item>
    ...

  on_human_complete:
    - IF <condition>: <action>
    ...

3.11.1 Triggers

triggers:
  # Technical failures
  - WHEN: tool_failures > 3
    REASON: "Repeated technical failures"
    PRIORITY: medium

  # Policy issues
  - WHEN: constraint_failures > 2 AND user.sentiment == "frustrated"
    REASON: "User unable to book due to policy restrictions"
    PRIORITY: high

  # High-value transactions
  - WHEN: booking.total > 5000
    REASON: "High-value booking requires human approval"
    PRIORITY: low

  # User request
  - WHEN: user.wants_human_agent == true
    REASON: "User explicitly requested human agent"
    PRIORITY: high

  # Complex situations
  - WHEN: user.has_special_request AND NOT agent.can_handle
    REASON: "Special accommodation request"
    PRIORITY: medium

  # Compliance
  - WHEN: user.mentions_legal OR user.mentions_lawsuit
    REASON: "Potential legal issue"
    PRIORITY: critical

3.11.2 Context for Human

context_for_human:
  - conversation_transcript
  - extracted_requirements:
      destination: {destination}
      dates: {checkin} to {checkout}
      guests: {guests}
      budget: {user.average_budget}
  - attempted_actions:
      tools_called: {tool_call_history}
      results: {tool_results}
  - failure_reasons: {constraint_failures}
  - user_sentiment: {detected_sentiment}
  - suggested_resolution: "<agent's recommendation>"
  - relevant_policies: {applicable_policies}

3.11.3 Post-Human Actions

on_human_complete:
  - IF human.resolved == true:
      STORE: {resolution_type, resolution_details} -> user.support_history
      RESPOND: "Thanks for your patience! {human.resolution_summary}"
      COMPLETE: true

  - IF human.handed_back == true:
      CONTINUE: with human.instructions
      CONTEXT: human.additional_context

  - IF human.escalated_further == true:
      RESPOND: "Your case has been escalated to our specialist team. Reference: {case_id}"
      COMPLETE: true

3.12 COMPLETE

Defines when the agent’s job is done.
COMPLETE:
  - WHEN: <condition>
    RESPOND: "<completion message>"
    STORE: <optional memory update>
  ...
Example:
COMPLETE:
  - WHEN: reservation.confirmed == true
    RESPOND: |
      Your reservation is confirmed!
      Confirmation #: {reservation.confirmation_number}
      Hotel: {selected_hotel.name}
      Dates: {checkin} to {checkout}
      Total: ${reservation.total}

      A confirmation email has been sent to {user.email}.
    STORE: {destination, hotel: selected_hotel.name, dates: {checkin, checkout}, price: reservation.total} -> user.past_bookings

  - WHEN: user.intent == "cancel" OR user.intent == "nevermind"
    RESPOND: "No problem! Your search has been saved - just say 'continue my search' anytime to pick up where we left off."
    STORE: {search_state} -> user.saved_searches

  - WHEN: handoff.completed == true
    # Silent completion - handoff agent takes over

  - WHEN: escalate.completed == true
    # Completion handled by escalation flow

3.13 ON_ERROR

Error handling and recovery strategies.
ON_ERROR:
  <error_type>:
    RESPOND: "<user message>"
    RETRY: <count>
    THEN: <action>
  ...
Error Types:
  • tool_timeout - Tool didn’t respond in time
  • tool_error - Tool returned an error
  • invalid_input - User input couldn’t be parsed
  • validation_error - Gathered data failed validation
  • api_error - External API failure
  • unknown_error - Unexpected error
Example:
ON_ERROR:
  tool_timeout:
    RESPOND: "I'm having a bit of trouble connecting. Let me try that again..."
    RETRY: 2
    THEN: ESCALATE with REASON: "Service unavailable"

  tool_error:
    LOG: {tool_name, error_message, input_params}
    RESPOND: "Something went wrong while {action_description}. Let me try a different approach."
    RETRY: 1
    THEN: DELEGATE -> Fallback_Agent

  invalid_input:
    RESPOND: "I didn't quite understand that. {clarification_prompt}"
    RETRY: 3
    THEN: ESCALATE with REASON: "Unable to understand user"

  validation_error:
    RESPOND: "That doesn't seem quite right - {validation_message}. Could you double-check?"
    RETRY: 2
    THEN: CONTINUE

  api_error:
    LOG: {api_name, status_code, response_body}
    RESPOND: "Our booking system is experiencing issues. Would you like me to keep trying, or shall I connect you with an agent?"
    RETRY: 0
    THEN: ASK_USER {retry, escalate}

  unknown_error:
    LOG: {error_type, error_message, stack_trace, conversation_state}
    RESPOND: "I apologize, but something unexpected happened. Let me connect you with a team member who can help."
    ESCALATE: PRIORITY: high

3.13.1 TEMPLATES (Named Response Templates)

Templates define reusable response content with channel-specific format variants. Each template has a DEFAULT text and optional overrides for specific output channels. Syntax:
TEMPLATES:
  <template_name>:
    DEFAULT: "<text with {{variable}} interpolation>"
    MARKDOWN: "<markdown formatted variant>"
    HTML: "<html formatted variant>"
    VOICE INSTRUCTIONS: "<instructions for TTS rendering>"
    ADAPTIVE_CARD: "<Adaptive Card JSON>"
    SLACK: "<Slack Block Kit JSON>"
    WHATSAPP: "<WhatsApp message format>"
    AG_UI: "<AG-UI event format>"
Supported Formats:
FormatChannelDescription
ADAPTIVE_CARDTeams, WebMicrosoft Adaptive Card JSON schema
AG_UIAG-UI SDKAG-UI protocol event stream
DEFAULTAllPlain text fallback used when no channel-specific format matches
HTMLWeb, EmailFull HTML with styling and interactive elements
MARKDOWNWeb, SDKMarkdown with tables, headers, bold, lists
SLACKSlackSlack Block Kit JSON
VOICE INSTRUCTIONSVoiceTTS rendering instructions (pacing, emphasis, pauses)
WHATSAPPWhatsAppWhatsApp message template format
Variable Interpolation: Templates support Handlebars-style interpolation:
  • {{variable}} — simple value substitution
  • {{#each items}}...{{/each}} — array iteration
  • {{#if condition}}...{{/if}} — conditional blocks
  • {{this.field}} — access fields within iteration context
Using Templates:
# In flow steps
RESPOND: TEMPLATE(greeting)

# In ON_START
ON_START:
  RESPOND: TEMPLATE(welcome)

# In COMPLETE
COMPLETE:
  - WHEN: order_confirmed == true
    RESPOND: TEMPLATE(checkout_confirmation)
Example — Retail Cart Summary:
TEMPLATES:
  cart_summary:
    DEFAULT: |
      Your Cart:
      {{#each items}}
      - {{this.name}} x{{this.quantity}} — {{this.price}} {{currency}}
      {{/each}}
      Total: {{total}} {{currency}}
    MARKDOWN: |
      ## Your Cart

      | Item | Qty | Price |
      |------|-----|-------|
      {{#each items}}
      | {{this.name}} | {{this.quantity}} | {{this.price}} {{currency}} |
      {{/each}}

      **Total: {{total}} {{currency}}**
    HTML: |
      <div class="cart-summary">
        <h2>Your Cart</h2>
        <table>
          {{#each items}}
          <tr><td>{{this.name}}</td><td>{{this.quantity}}</td><td>{{this.price}} {{currency}}</td></tr>
          {{/each}}
          <tr class="total"><td colspan="2">Total</td><td>{{total}} {{currency}}</td></tr>
        </table>
      </div>
    VOICE INSTRUCTIONS: "Read each item name and price. Then state the total clearly."
Compilation: Templates compile to AgentIR.templates: Record<string, string> for the DEFAULT text, and to AgentIR.rich_content: Record<string, RichContentIR> for channel-specific variants.

3.13.2 BEHAVIOR_PROFILES (Context-Dependent Behavior)

Behavior profiles allow an agent to adapt its behavior based on runtime context — such as the communication channel, user preferences, or conversation state. Each profile activates conditionally and overrides specific aspects of the agent’s base behavior. Syntax:
BEHAVIOR_PROFILES:
  - NAME: <profile_name>
    PRIORITY: <number>
    WHEN: <CEL expression>
    INSTRUCTIONS: "<additional instructions>"
    VOICE:
      provider: <tts_provider>
      voice_id: <voice_identifier>
      speed: <playback_speed>
    RESPONSE_RULES:
      max_buttons: <number>
      fallback_format: <plain_text | markdown | html>
      max_response_length: <number>
    TOOLS_HIDE: [<tool_names_to_remove>]
    TOOLS_ADD:
      - <tool_definition>
    CONSTRAINTS:
      - REQUIRE <condition>
        ON_FAIL: "<message>"
    GATHER_OVERRIDES:
      validation_style: <strict | lenient>
      confirmation: <always | never | on_change>
      field_overrides:
        <field_name>:
          prompt: "<channel-specific prompt>"
    FLOW_MODIFICATIONS:
      skip: [<step_names>]
      override:
        <step_name>: <replacement_config>
      insert_before:
        <step_name>: <new_step>
    FLOW_REPLACE: <alternative_flow_name>
Fields:
FieldTypeDescription
NAMEstringProfile identifier
PRIORITYnumberHigher priority wins when multiple profiles match
WHENCEL expressionActivation condition evaluated at runtime
INSTRUCTIONSstringAdditional instructions merged with base persona
VOICEobjectVoice configuration overrides (provider, voice_id, speed)
RESPONSE_RULESobjectChannel-specific response formatting constraints
TOOLS_HIDEstring[]Tool names to remove from available tools
TOOLS_ADDToolDefinition[]Additional tools available only in this profile
CONSTRAINTSConstraint[]Additional constraints active only in this profile
GATHER_OVERRIDESobjectModify gather behavior per field
FLOW_MODIFICATIONSobjectSkip, override, or insert flow steps
FLOW_REPLACEstringReplace the entire base flow with an alternative
Example — Voice Channel Optimization:
BEHAVIOR_PROFILES:
  - NAME: voice-optimized
    PRIORITY: 10
    WHEN: context.channel == "voice"
    INSTRUCTIONS: "Keep responses under 3 sentences. Use natural speech patterns."
    VOICE:
      provider: elevenlabs
      voice_id: aria
      speed: 1.1
    RESPONSE_RULES:
      max_buttons: 0
      fallback_format: plain_text
      max_response_length: 200
    TOOLS_HIDE: [show_map, render_chart]
    CONSTRAINTS:
      - REQUIRE len(response) < 500
        ON_FAIL: "Please provide a shorter response for voice."
    GATHER_OVERRIDES:
      validation_style: lenient
      confirmation: always
Example — SDK Rich Experience:
BEHAVIOR_PROFILES:
  - NAME: sdk-rich
    PRIORITY: 5
    WHEN: context.channel == "sdk" OR context.channel == "web"
    RESPONSE_RULES:
      max_buttons: 5
      fallback_format: markdown
      media_types: [image, video, carousel]
    TOOLS_ADD:
      - show_carousel(items: object[]) -> {displayed: boolean}
        description: "Display a product carousel in the chat"
Profile Resolution: At runtime, all profiles whose WHEN condition evaluates to true are collected and merged by priority (highest wins). Conflicts in the same field are resolved by priority. The merged result is applied on top of the agent’s base configuration. Compilation: Profiles compile to AgentIR.behavior_profiles: BehaviorProfileIR[].

3.13.3 Voice Configuration ⚡

Voice properties control text-to-speech (TTS) rendering when agents operate on voice channels. Voice config can be set at multiple levels: agent-wide, per behavior profile, per template, or per flow step.
⚡ Partial: SSML, instructions, and plain_text fields are interpolated at runtime and passed to clients. However, provider and voice_id fields are not yet resolved from agent IR — voice provider selection is configured externally (Jambonz/ElevenLabs API provisioning), not driven by the DSL. Voice channel transfer via transfer_to_agent tool is fully implemented.
Agent-Level Voice Config:
EXECUTION:
  voice:
    provider: elevenlabs
    voice_id: aria
    speed: 1.0
Per-Template Voice Instructions:
TEMPLATES:
  greeting:
    DEFAULT: "Welcome to our service."
    VOICE INSTRUCTIONS: "Speak warmly with a slight pause after 'Welcome'."
Per-Step Voice Override:
confirm_booking:
  REASONING: false
  RESPOND: "Your booking is confirmed."
  VOICE:
    ssml: "<speak><prosody rate='slow'>Your booking is confirmed.</prosody></speak>"
VoiceConfigIR Fields:
FieldTypeDescription
providerstringTTS provider (elevenlabs, azure, google, openai)
voice_idstringVoice identifier from the provider
speednumberPlayback speed multiplier (0.5 - 2.0)
ssmlstringSSML markup for fine-grained speech control
instructionsstringNatural language instructions for TTS rendering
plain_textstringPlain text override (strip formatting before TTS)
Voice Channel Agent Transfer: Voice channels have special transfer semantics. Unlike chat handoffs, voice transfers involve the telephony gateway:
TOOLS:
  transfer_to_agent:
    description: "Transfer call to human agent via telephony gateway"
    params:
      provider: string       # "kore", "genesys", "twilio"
      skills: string[]       # Agent skills required
      queueId: string        # Queue identifier
      priority: number       # Queue priority (1-10)
      postAgentAction: string  # "end" (hang up) or "return" (come back to AI)
      metadata: object       # Channel, department, caller context
    returns: object

# In flow:
transfer_call:
  REASONING: false
  CALL: transfer_to_agent
    call_with:
      provider: "kore"
      skills: ["{{department}}"]
      queueId: "{{department}}_queue"
      priority: 5
      postAgentAction: "end"
  ON_SUCCESS:
    - IF: transfer_to_agent.status == "waiting"
      RESPOND: "Connecting you now. Please hold."
      THEN: complete

3.13.4 Attachments & File Collection ⚡

Agents can collect file attachments from users as part of the GATHER process. Attachment fields support type validation, size limits, and automated processing (OCR, transcription, key frame extraction).
⚡ Partial: Attachment upload, storage, and processing (OCR via Docling, transcription) are fully implemented via the get_attachment and list_attachments system tools and the multimodal service pipeline. However, the GATHER-level AttachmentFieldIR with field-specific ocr_enabled/transcription_enabled flags is not yet wired — attachment collection currently works through generic tool calls rather than declarative GATHER fields.
Syntax (within GATHER):
GATHER:
  - <field_name>: required
    type: attachment
    category: <image | document | audio | video>
    prompt: "<request message>"
    allowed_mime_types: [<mime_types>]
    max_file_size: <bytes>
    processing:
      ocr_enabled: <boolean>
      transcription_enabled: <boolean>
      key_frame_extraction: <boolean>
Example — Insurance Claim with Photo:
GATHER:
  - damage_photo: required
    type: attachment
    category: image
    prompt: "Please upload a photo of the damage."
    allowed_mime_types: [image/jpeg, image/png, image/heic]
    max_file_size: 10485760  # 10MB
    processing:
      ocr_enabled: true

  - claim_document: optional
    type: attachment
    category: document
    prompt: "If you have a police report or repair estimate, please upload it."
    allowed_mime_types: [application/pdf, image/jpeg]
    max_file_size: 20971520  # 20MB
    processing:
      ocr_enabled: true
Processing Options:
OptionApplies ToDescription
ocr_enabledimage, documentExtract text from images/PDFs via OCR
transcription_enabledaudio, videoTranscribe speech to text
key_frame_extractionvideoExtract representative frames for analysis
Compilation: Attachment fields compile to AttachmentFieldIR in the gather section of the AgentIR.

3.13.5 Interactive Actions ⚡

Agents can include interactive elements (buttons, dropdowns, text inputs) in responses. Actions are rendered by the client SDK and trigger handler logic when users interact with them.
⚡ Partial: Interactive actions (BUTTON, SELECT, INPUT) are fully implemented with channel-specific rendering (Slack Block Kit, Teams Adaptive Cards, WhatsApp Interactive, Messenger Quick Replies). However, the explicit ACTION_HANDLERS DSL block is not yet implemented — action routing is handled implicitly by the runtime when user interactions arrive. The ActionSetIR and ActionElementIR types are used directly.
Syntax:
RESPOND: "Choose your preferred option:"
  ACTIONS:
    - BUTTON: "Option A"
      ID: option_a
      VALUE: "a"
    - BUTTON: "Option B"
      ID: option_b
      VALUE: "b"
    - SELECT: "Departure City"
      ID: departure
      OPTIONS:
        - { id: "NYC", label: "New York" }
        - { id: "LAX", label: "Los Angeles" }
        - { id: "ORD", label: "Chicago" }
    - INPUT: "Special requests"
      ID: special_requests
      TYPE: text
      PLACEHOLDER: "Any dietary requirements?"

ACTION_HANDLERS:
  option_a:
    SET: user_choice = "a"
    RESPOND: "Great choice!"
    THEN: process_selection
  option_b:
    SET: user_choice = "b"
    THEN: process_selection
Action Element Types:
TypeFieldsDescription
BUTTONid, label, valueClickable button that submits a value
SELECTid, label, options[]Dropdown with selectable options
INPUTid, label, type, placeholder, requiredText/number/date input field
Input Types: text, number, date, time, email Compilation: Actions compile to ActionSetIR with ActionElementIR[] elements. Handlers compile to ActionHandlerIR[].

3.14 FLOW (Flow-Based Execution)

Execution Style: Agents with a FLOW: section use flow-based execution. Agents without FLOW: use reasoning-only execution. The legacy MODE: declaration is deprecated — use per-step REASONING: true | false within FLOW steps to enable LLM reasoning on specific steps.
When an agent has a FLOW section, it follows a deterministic state machine defined by the FLOW.

3.14.1 Basic Syntax

FLOW:
  step1 -> step2 -> step3          # Step sequence

  step1:
    RESPOND: "Welcome!"
    THEN: step2

  step2:
    COLLECT: destination
    PROMPT: "Where would you like to go?"
    THEN: step3

  step3:
    CALL: search_hotels(destination)
    RESPOND: "Found {{result.total}} hotels!"
    THEN: COMPLETE

3.14.2 Enhanced GATHER within FLOW

FLOW steps can use GATHER for multi-field collection with LLM or pattern-based extraction:
FLOW:
  collect_details:
    PRESENT: "Let me gather your booking details."
    GATHER:
      - destination: required
      - checkin:
          TYPE: date
          REQUIRED: true
          PROMPT: "When do you want to check in?"
      - checkout:
          TYPE: date
          REQUIRED: true
      - guests:
          TYPE: number
          DEFAULT: 2
      STRATEGY: hybrid
      PROMPT: "Please provide destination, dates, and number of guests."
    CORRECTIONS: true
    COMPLETE_WHEN: destination AND checkin AND checkout
    THEN: search
GATHER Properties:
  • fields - List of fields with type, required, default, prompt, validation
  • strategy - Extraction method: llm, pattern, or hybrid
  • prompt - Prompt template for collecting
Step Properties:
  • PRESENT - Template shown before collection (can include data from previous steps)
  • CORRECTIONS - Allow natural corrections like “actually 4 guests not 3”
    • When CORRECTIONS: true: If the user says “actually 4 guests”, the runtime detects this as a correction to an already-collected field (via regex patterns and LLM fallback), updates the value, and invalidates dependent fields.
    • When CORRECTIONS: false (default): The same message is treated as new input for the current step. Use this for:
      • Single-field collection steps (nothing to correct)
      • Strict sequential forms where backtracking is not allowed (e.g., regulatory compliance)
      • Performance-sensitive flows (correction detection adds regex matching and potentially an LLM call)
      • Automated/deterministic pipelines where natural-language corrections don’t apply
  • COMPLETE_WHEN - Condition for when the step is complete

3.14.3 Conditional Branching (ON_INPUT)

FLOW:
  confirm:
    RESPOND: "Would you like to proceed?"
    ON_INPUT:
      - IF: input == "yes" OR yes
        RESPOND: "Great! Processing..."
        THEN: process
      - IF: input == "no" OR no
        RESPOND: "No problem."
        THEN: cancelled
      - IF: input.contains("change")
        RESPOND: "What would you like to change?"
        THEN: modify
      - ELSE:
        RESPOND: "Please say yes or no."
        THEN: confirm
ON_INPUT Branch Properties:
  • IF / ELSE - Condition for the branch
  • RESPOND - Optional response message
  • SET - Variable assignments (SET: var = value)
  • CALL - Tool call before transition
  • THEN - Target step

3.14.4 DIGRESSIONS (Intent-Based Escapes)

Digressions allow users to break out of the current flow based on detected intents:
FLOW:
  # Global digressions available in all steps
  global_digressions:
    - INTENT: "cancel"
      RESPOND: "Canceling your request."
      GOTO: cancelled
    - INTENT: "speak_to_agent"
      DELEGATE: Human_Support

  collect_info:
    GATHER: destination, checkin, checkout
    # Step-specific digressions
    DIGRESSIONS:
      - INTENT: "help"
        RESPOND: "Just tell me where and when you want to travel."
        RESUME: true              # Return to this step after responding
      - INTENT: "weather"
        DELEGATE: Weather_Agent   # Delegate to another agent
        RESUME: true
        CLEAR: [destination]      # Clear fields before resuming
    THEN: search
Digression Properties:
PropertyDescription
INTENTIntent pattern to match
CONDITIONOptional additional condition
RESPONDResponse before handling
GOTOTarget step to go to
DELEGATEAgent to delegate to
CALLTool to call
RESUMEReturn to current step (default: false)
CLEARVariables to clear before resuming

3.14.5 SUB_INTENTS (Scoped Intents)

Sub-intents are scoped to a specific step and don’t leave the step:
FLOW:
  select_room:
    RESPOND: "Select a room type."
    SUB_INTENTS:
      - INTENT: "change dates"
        RESPOND: "Let's update your dates."
        CLEAR: [checkin, checkout]
      - INTENT: "more details"
        CALL: get_room_details(hover_room_id)
        RESPOND: "{{result.description}}"
      - INTENT: "price breakdown"
        RESPOND: "{{room.price}} per night, {{total}} total including taxes."
    ON_INPUT:
      - IF: input matches /room\s*\d+/
        SET: selected_room = extracted_room_id
        THEN: confirm
      - ELSE:
        THEN: select_room
Sub-Intent Properties:
PropertyDescription
INTENTIntent pattern to match
RESPONDResponse message
CLEARVariables to clear (triggers re-collection)
SETVariables to set
CALLTool to call
RESUMEStay in step (default: true for sub-intents)

3.14.6 ON_SUCCESS / ON_FAIL Blocks

For CALL steps, define separate handling for success and failure:
FLOW:
  book_hotel:
    CALL: create_reservation(hotel_id, guest_info)
    ON_SUCCESS:
      RESPOND: "Booking confirmed! Reference: {{result.confirmation_id}}"
      THEN: send_confirmation
    ON_FAIL:
      RESPOND: "Sorry, the booking failed: {{result.error}}"
      THEN: retry_or_cancel

3.14.7 SET (Variable Assignment)

Assign computed values to variables within flow steps. Supports both inline (single) and block (multiple) forms. Inline form (in ON_INPUT branches):
SET: transfer_amount = TO_NUMBER(REPLACE(raw_amount, "$", ""))
Block form (step-level, multiple assignments):
start:
  SET:
    preferred_currency = COALESCE(preferred_currency, "USD")
    request_timestamp = NOW()
    transfer_id = UNIQUE_ID(10)
  THEN: next_step
Expressions can use any built-in function (see Section 8) and reference session variables or tool result fields via dot notation.

3.14.8 CLEAR (Variable Deletion)

Remove variables from session state. Used to reset state when looping or changing context.
CLEAR: from_date, to_date, txnResult, filtered_transactions
Commonly used in ON_INPUT branches to reset state before re-collecting:
ON_INPUT:
  - IF: input.contains("change")
    CLEAR: transfer_amount, raw_amount, limitsResult, feeResult
    THEN: collect_amount

3.14.9 CALL WITH/AS (Explicit Tool Parameters and Result Binding)

Enhanced tool calling with explicit parameter mapping (WITH:) and result variable binding (AS:).
fetch_balance:
  CALL: get_balance
    WITH:
      account_id: selected_account.id
      currency: preferred_currency
    AS: balanceResult
  • WITH maps named parameters to expressions (variables, dot paths, or built-in function calls)
  • AS binds the tool result to a named variable for subsequent use in ON_RESULT branches or SET expressions

3.14.10 ON_RESULT (Multi-Way Result Branching)

Branch on tool call results with multiple conditions. Replaces the simpler ON_SUCCESS/ON_FAIL pattern when more than two outcomes are possible.
validate_recipient_step:
  CALL: validate_recipient
    WITH:
      routing_number: recipient_routing
      account_number: recipient_account
    AS: recipientResult
  ON_RESULT:
    - IF: recipientResult.status == "valid"
      SET:
        recipient_bank = recipientResult.bank_name
        recipient_name = recipientResult.account_holder
      THEN: collect_amount
    - IF: recipientResult.status == "INVALID_ROUTING"
      RESPOND: "The routing number is invalid. Please double-check."
      THEN: collect_recipient
    - IF: recipientResult.status == "ACCOUNT_CLOSED"
      RESPOND: "That account appears to be closed."
      THEN: collect_recipient
    - ELSE:
      RESPOND: "We couldn't verify the recipient details."
      THEN: collect_recipient
ON_RESULT branches support the same properties as ON_INPUT branches: IF/ELSE, SET, CLEAR, RESPOND, and THEN.

3.14.11 TRANSFORM (Array Data Pipeline)

Process arrays through a declarative pipeline with filter, map, sort, and limit operations.
apply_filters:
  TRANSFORM: txnResult.transactions AS txn INTO filtered_transactions
    FILTER: filter_type == "all" OR txn.type == filter_type
    MAP:
      id: txn.id
      date: FORMAT_DATE(txn.date, "MMM DD")
      description: COALESCE(txn.merchant, txn.description)
      display_amount: FORMAT_CURRENCY(ABS(txn.amount), "USD")
      direction: UPPER(SUBSTRING(txn.type, 0, 1))
      category: UPPER(txn.category)
    SORT_BY: date DESC
    LIMIT: page_size
  THEN: display_transactions
Pipeline stages:
  • FILTER: — Boolean expression; items where the condition is true are kept
  • MAP: — Object with field mappings; each value is an expression evaluated per item
  • SORT_BY: — Field name with optional ASC/DESC direction (default: ASC)
  • LIMIT: — Maximum number of items to keep (expression or literal)
All stages are optional. MAP expressions can use any built-in function and reference the item variable (txn in the example above) via dot notation.

3.15 Execution Pipeline (Supervisor Pre-Classification)

Supervisors can enable an opt-in classification pipeline that runs before the main reasoning LLM. A smaller, faster model classifies user intent and optionally short-circuits routing — avoiding the cost of the full reasoning call for obvious routing decisions.

Configuration

SUPERVISOR: Support_Router
  EXECUTION:
    model: claude-sonnet-4-5-20250929
    pipeline:
      enabled: true
      mode: sequential          # 'parallel' | 'sequential'
      model: qwen3-30b          # Smaller/faster classifier model
      shortCircuit:
        enabled: true
        confidenceThreshold: 0.85
      toolFilter:
        enabled: true
        maxTools: 6
      keywordVeto:
        enabled: true
        keywords: [reset, cancel, undo]

  AGENTS:
    billing: Billing_Agent
    technical: Tech_Support
    general: General_Inquiry

Pipeline Options

OptionDefaultDescription
enabledfalseEnable the pre-classification pipeline
modeparallelparallel — classifier and main LLM run simultaneously; sequential — classifier runs first, main LLM only if needed
modelqwen3-30bModel for classification (should be fast/cheap)
shortCircuit.enabledtrueAllow direct routing when classifier confidence is high
shortCircuit.confidenceThreshold0.85Minimum confidence to skip the reasoning loop
toolFilter.enabledtrueFilter tools to only relevant ones before reasoning
toolFilter.maxTools6Maximum tools to pass to the reasoning loop
keywordVeto.enabledtruePrevent short-circuit when user mentions local tool keywords
keywordVeto.keywords[]Additional keywords that veto short-circuit routing

Execution Flow

User message

Pipeline enabled? ──no──→ Reasoning loop (full tools)
    ↓ yes
Classify intent (fast model, 300 tokens max, 10s timeout)

Short-circuit? ─────────→ Single intent + high confidence + no keyword veto
    ↓ yes                     ↓ no
Route directly via       Filter tools → Reasoning loop (reduced tool set)
HANDOFF (skip reasoning)

Configuration Resolution

Pipeline config resolves through a 3-level hierarchy:
  1. Agent IR (execution.pipeline block) — highest priority
  2. Project config — project-level defaults
  3. System defaults — hardcoded fallback values

Sequential vs Parallel Mode

  • Sequential: Classifier runs first. If it short-circuits, the main LLM is never called — saving the full cost of a reasoning iteration. Best for supervisors where most messages route cleanly.
  • Parallel: Classifier and main LLM run simultaneously. Short-circuit still works, but if it doesn’t fire, the classifier only contributes tool filtering. You pay for both calls regardless.
Cost note: For pure routing supervisors, sequential mode with shortCircuit.enabled: true provides the best cost savings. In parallel mode, the classifier adds latency protection but no cost savings.

8. Built-in Functions Reference

35 built-in functions are available in SET expressions, TRANSFORM MAP/FILTER, CALL WITH values, and RESPOND templates.

8.1 Math Functions

FunctionSignatureDescription
ADDADD(a, b) → numberAddition
SUBSUB(a, b) → numberSubtraction
MULMUL(a, b) → numberMultiplication
DIVDIV(a, b) → number|nullDivision (returns null on divide-by-zero)
ROUNDROUND(n, decimals?) → numberRound to N decimal places (default: 0)
ABSABS(n) → numberAbsolute value
MINMIN(a, b) → numberMinimum of two values
MAXMAX(a, b) → numberMaximum of two values

8.2 String Functions

FunctionSignatureDescription
UPPERUPPER(s) → stringConvert to uppercase
LOWERLOWER(s) → stringConvert to lowercase
TRIMTRIM(s) → stringRemove leading/trailing whitespace
SUBSTRINGSUBSTRING(s, start, end?) → stringExtract substring
REPLACEREPLACE(s, find, replacement) → stringReplace all occurrences
SPLITSPLIT(s, delimiter) → arraySplit string into array
JOINJOIN(arr, delimiter) → stringJoin array into string
PAD_STARTPAD_START(s, length, char?) → stringPad start to target length
PAD_ENDPAD_END(s, length, char?) → stringPad end to target length
REPEATREPEAT(s, count) → stringRepeat string N times

8.3 Formatting Functions

FunctionSignatureDescription
MASKMASK(s, pattern, char?) → stringMask string (e.g., MASK(acct, "last4")****1234)
FORMAT_CURRENCYFORMAT_CURRENCY(n, currency, locale?) → stringFormat as currency (e.g., $1,234.56)
FORMAT_DATEFORMAT_DATE(d, format, tz?) → stringFormat date (e.g., "MMM DD, YYYY")
ORDINALORDINAL(n) → stringOrdinal suffix (e.g., 1"1st")

8.4 Type Functions

FunctionSignatureDescription
IS_ARRAYIS_ARRAY(x) → booleanCheck if value is an array
IS_NUMBERIS_NUMBER(x) → booleanCheck if value is a number
IS_STRINGIS_STRING(x) → booleanCheck if value is a string
TO_NUMBERTO_NUMBER(x) → number|nullConvert to number (null if NaN)
TO_STRINGTO_STRING(x) → stringConvert to string

8.5 Array Functions

FunctionSignatureDescription
LENGTHLENGTH(x) → numberArray length or string length
ARRAY_FINDARRAY_FIND(arr, field, value) → object|nullFind first item where item[field] == value
ARRAY_FIND_INDEXARRAY_FIND_INDEX(arr, field, value) → numberFind index of first match (-1 if not found)

8.6 Object Functions

FunctionSignatureDescription
OBJECT_KEYSOBJECT_KEYS(obj) → arrayGet object keys
OBJECT_VALUESOBJECT_VALUES(obj) → arrayGet object values
OBJECT_MERGEOBJECT_MERGE(...objs) → objectMerge objects (later values override)

8.7 Utility Functions

FunctionSignatureDescription
COALESCECOALESCE(...args) → anyReturn first non-null, non-undefined value
NOWNOW() → stringCurrent timestamp (ISO 8601)
UNIQUE_IDUNIQUE_ID(length?) → stringGenerate random alphanumeric ID

8.8 System-Assigned Variables

The following variables are managed by the runtime and available in WHEN conditions, SET expressions, and CONSTRAINT checks. Do not use these names for user-defined variables.

Pattern Match Variable

VariableAssigned ByContents
matchmatches operator in IF conditionsRegex capture groups: match.0 (full match), match.1 (first group), match.room_id (named group)
Warning: If you use SET: match = value, the variable will be overwritten by the next successful matches operation. The compiler emits a warning for reserved variable names.

Gather & Extraction Variables

VariableTypeSet WhenDescription
_clarification_countnumberSession init (0), incremented on re-askHow many times the agent re-prompted for a field
_validation_retriesRecord<string, number>Validation failurePer-field count of failed validation attempts
_pending_inferencesobjectLLM infers a field valueInferred values waiting for user confirmation
all_fields_gatheredbooleanAll required GATHER fields collectedTrue when gather is complete — use in COMPLETE/WHEN
COMPLETE:
  - WHEN: all_fields_gathered == true
    RESPOND: "Great, I have everything I need."

Tool Result Variables

VariableTypeSet WhenDescription
last_<tool_name>_resultobjectAfter tool executionFull result of the most recent call to the named tool
CONSTRAINTS:
  always:
    - REQUIRE last_search_hotels_result.total > 0
      ON_FAIL: "No hotels found matching your criteria. Try different dates?"

Intent & Sentiment Variables

VariableTypeSet WhenLegal Values
user.intentstringIntent classification on each user messageAgent-specific (detected by NLU)
user.sentimentstringSentiment analysis on each user messagevery_negative, negative, neutral, positive, very_positive, frustrated
sentiment_trajectorystringComputed across conversation turnsimproving, declining, stable, volatile
ESCALATE:
  - WHEN: user.sentiment == "very_negative" AND sentiment_trajectory == "declining"
    PRIORITY: high
    CONTEXT: [conversation_history, user.sentiment]
    RESPOND: "I understand your frustration. Let me connect you with a specialist."

Constraint & Error Variables

VariableTypeSet WhenDescription
_constraint_warningsstring[]Constraint evaluation produces warningsWarning messages from soft constraint failures
tool_failuresnumberTool execution failsCount of consecutive tool failures
constraint_failuresnumberConstraint check failsCount of constraint violations
_disambiguation_intentsstring[]Multi-intent detectionPossible intents when disambiguation needed

Session & Channel Variables

VariableTypeSet WhenLegal Values / Description
channelstringSession initweb, slack, teams, whatsapp, voice, api
languagestringSession init / detectISO 639 code: en, es, fr, de, etc.
ROUTING:
  - TO: Spanish_Support
    WHEN: language == "es"
  - TO: General_Support
    WHEN: channel == "voice"
    PASS: [user_id, sentiment_trajectory]

Orchestration Variables

VariableTypeSet WhenDescription
handoff.completedbooleanChild agent completes after handoffTrue when a handed-off agent finishes
escalate.completedbooleanHuman agent resolves escalationTrue when escalation is resolved
COMPLETE:
  - WHEN: handoff.completed == true
    RESPOND: "Is there anything else I can help with?"

Reserved Variable Prefixes

Variables beginning with _ are reserved for runtime use. The following prefixes have special meaning:
PrefixPurpose
_summaryConversation summary state
_stored_*Persistent memory values
_errorError state from last failed operation
_correctionCorrection detection state
_current_step_for_resetFlow step tracking (resets validation counts on step change)

8.9 MASK Patterns

Built-in patterns for the MASK() function:
PatternBehaviorExample
last4Show only last 4 charactersMASK("4111111111111111", "last4")"************1111"
first4Show only first 4 charactersMASK("4111111111111111", "first4")"4111************"
N*NShow N chars at start and endMASK("4111111111111111", "4*4")"4111********1111"
Pattern names are string literals passed as the second argument to MASK(), not variable references.

8.10 Events & Lifecycle Hooks

ABL supports lifecycle events for triggering actions at specific execution points.

RECALL Events

Used in the MEMORY section to trigger recall of stored information:
Event PatternFires WhenExample
session:startNew session beginsLoad user preferences
session:endSession terminatesSave conversation summary
agent:<name>:beforeBefore a specific agent startsagent:Payment_Agent:before
agent:<name>:afterAfter a specific agent completesagent:Billing_Agent:after
agent:*:beforeBefore any agent startsGlobal pre-agent hook
agent:*:afterAfter any agent completesGlobal post-agent hook
tool:<name>:afterAfter a specific tool executestool:search_hotels:after
tool:*:afterAfter any tool executesGlobal post-tool hook
entity:<field>:extractedAfter a gather field is extractedentity:destination:extracted
step:enter:<name>When a flow step is enteredstep:enter:Collect_Payment
step:exit:<name>When a flow step is exitedstep:exit:Verify_Identity
MEMORY:
  recall:
    - ON: session:start
      RETRIEVE: [user.preferences, user.loyalty_tier]
    - ON: tool:search_hotels:after
      RETRIEVE: [user.hotel_preferences]
    - ON: entity:destination:extracted
      RETRIEVE: [user.destination_history]
Legacy syntax: ON_<event>: (e.g., ON_SESSION_START:) is deprecated. Use ON: session:start instead. Legacy names are auto-normalized: session_startsession:start, agent_enteragent:*:after.

Lifecycle Hooks

Hooks execute actions at agent and conversation turn boundaries:
HookFires When
before_agentBefore the agent begins processing (session init)
after_agentAfter the agent completes
before_turnBefore each conversation turn is processed
after_turnAfter each conversation turn completes
HOOKS:
  before_turn:
    CALL: audit_logger
    SET:
      _turn_start: NOW()
  after_turn:
    CALL: metrics_reporter
    RESPOND: ""  # Silent — no user-facing message

Flow Step Events (ON_SUCCESS / ON_FAIL / ON_RESULT / ON_INPUT)

These events are documented in Section 3.14.6 (ON_SUCCESS/ON_FAIL), 3.14.10 (ON_RESULT), and 3.14.3 (ON_INPUT). They fire at specific points within a flow step:
EventFires WhenUse For
ON_SUCCESSStep’s CALL or GATHER completes successfullyHappy-path branching
ON_FAILStep’s CALL or GATHER failsError recovery, retry, escalation
ON_RESULTAfter CALL returns — multi-way branch on resultRoute based on tool return values
ON_INPUTAfter user input — conditional branch on contentDeterministic routing by response
Evaluation order: ON_INPUT is evaluated on a frozen state snapshot. Mutations (SET, TRANSITION) are collected and applied after all conditions are evaluated. This prevents side effects from affecting sibling branches.
Determinism: ON_INPUT IF: predicates are pure boolean expressions over input and session/flow variables. No LLM reasoning, no tool invocations, no intent classification runs inside the predicate — if you need LLM-based routing, use DIGRESSIONS with INTENT: (§ 3.20.4). Treat ON_INPUT as a first-match boolean dispatcher so replays and eval snapshots are reproducible.

7.11 Runtime Defaults & Limits

The following defaults apply when not overridden in the agent’s EXECUTION block or project configuration:

Timeouts

SettingDefaultOverride ViaDescription
Tool execution timeout30,000 msexecution.timeouts.tool_timeout_msMax time for a single tool call
LLM call timeout30,000 msexecution.timeouts.llm_timeout_msMax time for a single LLM request
Session idle timeout1,800,000 msexecution.timeouts.session_timeout_msSession expires after 30 min idle
Voice latency target(none)execution.timeouts.voice_latency_target_msTarget response time for voice

Iteration Limits

SettingDefaultOverride ViaDescription
Reasoning max iterations10execution.max_iterationsMax tool-use loops before forced stop
Flow max iterations100execution.max_flow_iterationsMax flow step transitions per session
AGENT: Complex_Workflow
  EXECUTION:
    model: claude-sonnet-4-5-20250929
    max_iterations: 20          # Allow more reasoning loops
    max_flow_iterations: 200    # Only raise above default 100 for audited flows with many deterministic steps
    timeouts:
      tool_timeout_ms: 60000    # Some tools are slow
      session_timeout_ms: 3600000  # 1 hour sessions

Size Limits

SettingDefaultDescription
Tool parameters max size512 KBMax serialized size of tool call arguments

Pipeline Defaults

When execution.pipeline is enabled but specific fields are omitted, these defaults apply:
SettingDefault
pipeline.modeparallel
pipeline.modelqwen3-30b
pipeline.shortCircuit.enabledtrue
pipeline.shortCircuit.confidenceThreshold0.85
pipeline.toolFilter.enabledtrue
pipeline.toolFilter.maxTools6
pipeline.keywordVeto.enabledtrue
pipeline.keywordVeto.keywords[]

4. Compilation to AgentIR

4.1 Compilation Pipeline

ABL source files are compiled to a typed intermediate representation (AgentIR) at deploy time. The runtime loads and executes the IR directly — there is no code generation step.
ABL Source (.agent.abl or .agent.yaml)


┌──────────────────────────┐
│   Parser (@abl/core)     │
│   parseAgentBasedDSL()   │
│   → AgentBasedDocument   │
└──────────────────────────┘


┌──────────────────────────┐
│  Compiler (@abl/compiler)│
│  compileDSLtoIR()        │
│  → CompilationOutput     │
│    { agents, entry_agent }│
└──────────────────────────┘


┌──────────────────────────┐
│   Runtime Executors      │
│  (apps/runtime/src/      │
│   services/execution/)   │
│  • ReasoningExecutor     │
│  • FlowStepExecutor      │
│  • RoutingExecutor       │
│  • ConstraintChecker     │
└──────────────────────────┘
The only compilation target is 'ir'. The compiler produces a CompilationOutput containing a map of AgentIR instances (one per agent, including supervisors) and identifies the entry agent.

4.2 Generated IR Structure

For the Hotel_Search agent example, the compiler produces an AgentIR with:
  • identity: Built from GOAL, PERSONA, LIMITATIONS — includes a generated system_prompt.template
  • tools: ToolDefinition[] from the TOOLS section, with typed parameters and return schemas
  • gather: GatherConfig with field definitions from the GATHER section
  • constraints: ConstraintConfig with a flattened ordered constraint list plus guardrails from CONSTRAINTS
  • coordination: CoordinationConfig with handoffs, delegates, escalation from HANDOFF/DELEGATE/ESCALATE
  • completion: CompletionConfig from COMPLETE conditions
  • memory: MemoryConfig from MEMORY section (session variables, persistent paths, remember triggers, recall instructions)
At runtime, the executor loads the IR, builds the system prompt, wires tools to the LLM provider, and manages the conversation loop. Constraint checking, tool execution, handoff/delegate routing, and escalation are handled by dedicated executor modules in the runtime.

5. Multi-Agent Orchestration

5.1 Supervisor for Agent-Based Agents (Unified AgentIR)

When using multiple agent-based agents, a supervisor coordinates them. Supervisors compile to the same AgentIR type as regular agents — they are simply agents with routing and available_agents fields populated. All agents (including supervisors) live in a single CompilationOutput.agents registry. Supervisors can hand off to other supervisors, enabling hierarchical composition:
SUPERVISOR: Travel_Assistant

AGENTS:
  hotel: Hotel_Search
  flight: Flight_Search
  payment: Payment_Agent
  support: Support_Agent

ROUTING:
  - INTENT(hotel, stay, room, accommodation) -> hotel
  - INTENT(flight, fly, plane, airline) -> flight
  - INTENT(pay, checkout, purchase) -> payment
  - INTENT(help, problem, issue, complaint) -> support
  - DEFAULT -> hotel  # Most common use case

HANDOFF_PROTOCOL:
  # How agents communicate
  - FROM hotel TO payment:
      CONTEXT: [reservation, user.loyalty_programs]
      TRIGGER: reservation.ready_for_payment

  - FROM any TO support:
      CONTEXT: [conversation_history, active_agent, current_state]
      TRIGGER: user.wants_human_agent OR user.sentiment == "frustrated"

5.2 Delegate vs Handoff Execution

DELEGATE (synchronous, returns):
┌─────────────────────────────────────────────────┐
│  Hotel_Search                                    │
│                                                  │
│  1. User asks about loyalty points               │
│  2. DELEGATE -> Loyalty_Lookup                   │
│     └──────────────────────────┐                │
│                                 ▼                │
│                    ┌─────────────────────┐      │
│                    │  Loyalty_Lookup     │      │
│                    │  - Check points     │      │
│                    │  - Return balance   │      │
│                    └─────────────────────┘      │
│                                 │                │
│     ┌───────────────────────────┘                │
│     ▼                                            │
│  3. Use loyalty info in booking                  │
│  4. Continue with reservation                    │
└─────────────────────────────────────────────────┘

HANDOFF (asynchronous, transfers control):
┌──────────────────┐         ┌──────────────────┐
│  Hotel_Search    │         │  Payment_Agent   │
│                  │         │                  │
│  1. Find hotel   │         │                  │
│  2. User books   │ ──────► │  3. Process pay  │
│  3. HANDOFF      │ context │  4. Confirm      │
│                  │         │  5. COMPLETE     │
│  [DONE]          │         │                  │
└──────────────────┘         └──────────────────┘

HANDOFF RETURN:true with digression (thread resume):
┌──────────────┐    ┌──────────────┐    ┌──────────────┐    ┌──────────────┐
│  Supervisor  │    │ CreditCard   │    │ AccountInfo  │    │ CreditCard   │
│              │    │              │    │              │    │ (RESUMED)    │
│ 1. Route to  │──► │ 2. Gather    │    │              │    │              │
│    CC agent  │    │    payment   │    │              │    │              │
│              │    │ 3. User asks │    │              │    │              │
│              │ ◄──│    "balance" │    │              │    │              │
│ 4. Re-route  │──► │ (return_to_  │    │ 5. Check     │    │              │
│    to AcctInfo    │  parent)     │    │    balance   │    │              │
│              │ ◄──│ [WAITING]    │    │ 6. COMPLETE  │    │              │
│ 7. Re-route  │────┼──────────────┼────┼──────────────┼──► │ 8. Resume    │
│    to CC     │    │              │    │              │    │    payment   │
│              │    │              │    │              │    │    (context  │
│              │    │              │    │              │    │    preserved)│
└──────────────┘    └──────────────┘    └──────────────┘    └──────────────┘

6. Complete Example

AGENT: Hotel_Search

GOAL: "Help user find and book a hotel that meets all booking policies"

PERSONA: |
  Helpful, knowledgeable hotel booking specialist.
  Friendly but efficient - doesn't waste user's time.
  Always explains policies clearly when they affect the booking.
  References user's preferences to make personalized suggestions.

LIMITATIONS:
  - "Cannot guarantee availability until booking is confirmed"
  - "Cannot override blackout dates or minimum stay policies"
  - "Cannot process payments - must transfer to payment agent"

TOOLS:
  check_blackout_dates(destination: string, checkin: date, checkout: date) -> {allowed: boolean, reason?: string}
  validate_minimum_stay(destination: string, checkin: date, checkout: date) -> {valid: boolean, minimum: number, nights: number}
  search_hotels(destination: string, checkin: date, checkout: date, guests: number = 2) -> Hotel[]
  get_hotel_details(hotel_id: string) -> HotelDetails
  check_availability(hotel_id: string, room_type: string, dates: DateRange) -> {available: boolean, price: number}
  create_reservation(hotel_id: string, room_type: string, dates: DateRange, guest: GuestInfo) -> Reservation

GATHER:
  destination:
    prompt: "Where would you like to stay?"
    type: string
    required: true
  checkin:
    prompt: "What's your check-in date?"
    type: date
    required: true
  checkout:
    prompt: "What's your check-out date?"
    type: date
    required: true
  guests:
    prompt: "How many guests?"
    type: number
    default: 2

MEMORY:
  session:
    - search_results
    - selected_hotel
    - reservation_draft

  persistent:
    - user.preferred_chains
    - user.preferred_room_type
    - user.loyalty_programs
    - user.past_bookings
    - user.average_budget

  remember:
    - WHEN booking.confirmed
      STORE: {hotel: selected_hotel.name, chain: selected_hotel.chain, destination, price: reservation.total} -> user.past_bookings

  recall:
    - ON_START: "Load user's preferred chains and room types"
    - ON_SEARCH: "Prioritize hotels matching preferences"

CONSTRAINTS:
  pre_search:
    - REQUIRE check_blackout_dates.allowed == true
      ON_FAIL: |
        Those dates fall within a blackout period ({reason}).
        We cannot book during Dec 24-26 or Dec 31-Jan 1.
        Would you like to try different dates?

    - REQUIRE validate_minimum_stay.valid == true
      ON_FAIL: |
        {destination} requires a minimum of {minimum} nights.
        You've selected {nights} nights. Would you like to extend?

DELEGATE:
  - AGENT: Loyalty_Lookup
    WHEN: booking.ready AND user.loyalty_programs IS SET
    PURPOSE: "Check for applicable rewards"
    INPUT: {user_id, hotel_chain: selected_hotel.chain}
    RETURNS: {points: number, rewards: Reward[]}
    USE_RESULT: "Offer to apply rewards"

HANDOFF:
  - TO: Payment_Agent
    WHEN: reservation.confirmed_pending_payment
    CONTEXT:
      pass: [reservation, selected_hotel, user.email]
      summary: "Booking {selected_hotel.name}, {nights} nights, ${reservation.total}"
    RETURN: false

  - TO: Support_Agent
    WHEN: user.sentiment == "frustrated" OR user.wants_human_agent
    CONTEXT:
      pass: [conversation_history, current_state]
      summary: "User needs assistance with hotel booking"
    RETURN: false

ESCALATE:
  triggers:
    - WHEN: tool_failures > 3
      REASON: "Technical issues"
      PRIORITY: medium

    - WHEN: user.wants_human_agent
      REASON: "User requested human"
      PRIORITY: high

  context_for_human:
    - conversation_transcript
    - gathered: {destination, checkin, checkout, guests}
    - search_results
    - failure_reasons

COMPLETE:
  - WHEN: handoff.completed
    # Silent - payment agent takes over

  - WHEN: user.intent == "cancel"
    RESPOND: "No problem! Feel free to come back anytime."

ON_ERROR:
  tool_timeout:
    RESPOND: "Having trouble connecting. Retrying..."
    RETRY: 2
    THEN: ESCALATE

  unknown_error:
    RESPOND: "Something went wrong. Connecting you with support."
    ESCALATE: PRIORITY: high

7. Appendix: Type Definitions

7.1 Built-in Types

type string = string;
type number = number;
type boolean = boolean;
type date = string; // ISO 8601 or natural language
type array = any[];
type object = Record<string, any>;

7.2 Domain Types (Examples)

interface Hotel {
  id: string;
  name: string;
  chain?: string;
  rating: number;
  price_per_night: number;
  amenities: string[];
  location: string;
}

interface HotelDetails extends Hotel {
  description: string;
  rooms_available: number;
  room_types: RoomType[];
  cancellation_policy: string;
  images: string[];
}

interface Reservation {
  id: string;
  confirmation_number: string;
  hotel: Hotel;
  room_type: string;
  checkin: date;
  checkout: date;
  guests: number;
  total: number;
  status: 'pending' | 'confirmed' | 'cancelled';
}

interface GuestInfo {
  name: string;
  email: string;
  phone?: string;
  loyalty_number?: string;
}