Language Guide

Complete reference for AgentScript syntax — blocks, types, procedures, expressions, and built-in utilities. For the formal grammar, see SPEC.md.

Blocks & Indentation

AgentScript is indentation-sensitive. A block is a key followed by :. Nesting is determined by indentation level — a child must be indented further than its parent. Spaces are standard; tabs are not recommended.

config:
    agent_name: "My Agent"
    description: "Handles support"

# Named block: block-type + instance-name
subagent billing:
    description: "Billing subagent"

Comments begin with # and extend to end of line. There is no multi-line comment syntax.

Value forms

FormExample
Scalar string"hello"
Scalar number42, 3.14
Scalar booleanTrue, False
Template string| on next indented line
Procedure-> or auto-detected
Sequence (list)YAML-style - item
Sub-blockNested indented block

Type System

Scalar types

TypeDescriptionExample
stringQuoted string"hello"
numberInteger or float42, 3.14
booleanBoolean — capitalizedTrue, False
objectUntyped structured valueNone, {}

List types

Lists are parameterized with an element type using bracket notation:

list[string]
list[number]
list[object]

Variable declarations

Variables are declared with a modifier, name, type, and optional default. They appear inside variables: blocks.

variables:
    order_id: mutable string = ""
        description: "The order ID being discussed"
    item_count: mutable number = 0
        description: "Number of items"
    is_verified: mutable boolean = False
        description: "Whether customer is verified"
    session_id: linked string
        description: "Session ID from runtime (read-only)"
    tags: mutable list[object] = []
        description: "List of tags"
ModifierDescription
mutableRead and write from within the agent via set, @utils.setVariables, or with parameters.
linkedRead-only. Bound to an external value provided by the runtime. Cannot be assigned by any mechanism.
💡

Variable description fields are used by the LLM to understand what a variable holds. They're required for @utils.setVariables slot-filling to work correctly.

Expressions

Expressions follow Python-like syntax. Operators by precedence (lowest to highest):

OperatorDescription
orLogical OR
andLogical AND
notLogical NOT (unary)
== !=Equality, inequality
< <= > >=Numeric comparison
is is notIdentity / None check
+ -Addition, subtraction
* /Multiplication, division
expr.fieldMember access
expr[index]Subscript / index
a if c else bTernary (Python-style)
@variables.score >= 80
@variables.name != "" and @variables.verified == True
@outputs.result is not None
@outputs.items[0].id
@variables.count + 1
"prefix_" + @variables.label
score if score > 0 else 0

@ References

The @ symbol denotes a namespace lookup. @namespace.member resolves the namespace to a registered scope and retrieves the member:

@variables.order_id       # agent state variable
@outputs.temperature      # action output (scoped to callback)
@actions.LookupOrder      # declared action
@subagent.billing         # named subagent block
@topic.order_details      # agentforce alias for @subagent
@utils.transition         # built-in utility
@system_variables.user_input  # runtime-provided value
NamespaceResolves to
@variablesAgent state variables (declared in variables:)
@outputsAction outputs — scoped strictly to the callback block
@actionsAction definitions declared in actions:
@subagent / @topicNamed subagent/topic blocks
@start_agentAlias for @subagent
@utilsBuilt-in utilities injected by the dialect
@system_variablesRuntime-provided read-only values

system

System-level instructions and default messages. Appears at the top level. Can also appear inside a subagent block to override instructions for that subagent only.

system fixed-field block

instructions optionalstring or template. Global system prompt inherited by all subagents. Supports {!...} interpolation.

messages optional — block of named string messages (welcome, error, etc.) shown by the runtime at specific moments.

system:
    instructions: |
        You are a helpful support agent.
        Always verify the customer before accessing account details.
    messages:
        welcome: "Hello! How can I help you?"
        error: "Sorry, something went wrong."

config

High-level agent configuration. The base dialect provides description; individual dialects extend this with additional fields.

config:
    agent_name: "SupportBot"
    description: "Customer support assistant"
    default_agent_user: "agent@example.com"

variables

A variadic block of typed variable declarations. Variables are global across the entire script — state persists across turns and transitions between subagents. See Type System for declaration syntax.

variables:
    customer_email: mutable string = ""
        description: "Customer's email address"
    order_found: mutable boolean = False
        description: "Whether the order was located"
    session_id: linked string
        description: "Provided by the runtime (read-only)"

start_agent

The entry-point execution block. Exactly one start_agent is required per script. Its instance name serves as the agent identifier. It is functionally identical to subagent@start_agent is an alias for @subagent.

start_agent router:
    description: "Welcome user and route to the right subagent"
    reasoning:
        instructions: ->
            | Welcome the user and understand their request.
        actions:
            go_orders: @utils.transition to @subagent.Orders
                description: "Handle order inquiries"
            go_billing: @utils.transition to @subagent.Billing
                description: "Handle billing questions"

subagent

A named execution block defining logic for a specific conversation area. Multiple subagents can be defined. The agent transitions between them via transition to.

topic vs. subagent: topic is an Agentforce-dialect alias for subagent and is being deprecated. New scripts should use subagent.

subagent <name> named block

description required — Used by the runtime to determine when to transition to this subagent.

label optional — Display label in UI. String literals only (no templates). Not shown to the LLM.

system optional — Per-subagent override for instructions only.

actions optional — Action definitions available in this subagent.

before_reasoning optional — Procedure that runs before each LLM reasoning turn.

reasoning optional — Reasoning loop block with instructions and actions.

after_reasoning optional — Procedure that runs after each LLM reasoning turn.

subagent Order_Management:
    description: "Handles order lookups and status updates"
    system:
        instructions: "Focus on helping the user with their order."
    before_reasoning:
        if @variables.verified is not True:
            transition to @subagent.Identity
    reasoning:
        instructions: ->
            | Help the user with their order.
        actions:
            lookup: @actions.LookupOrder
                with order_number=@variables.order_id
                set @variables.status = @outputs.status
    after_reasoning:
        set @variables.turn_count = @variables.turn_count + 1

actions

Named collection of action definitions. Each action declares an external tool the agent can invoke. Actions can be declared at the subagent level (scoped) or at the top level (global).

actions: <Name> named block

description required — What the action does.

label optional — Display name.

inputs optional — Variadic typed input parameter declarations.

outputs optional — Variadic typed output parameter declarations.

target optional — URI identifying the external implementation.

actions:
    LookupOrder:
        description: "Retrieve order details by order number"
        inputs:
            order_number: string
                description: "The order number to look up"
                is_required: True
        outputs:
            status: string
                description: "Current order status"
            items: list[object]
                description: "Items in the order"
        target: "flow://Lookup_Order_By_Number"

Target URI schemes

SchemeDescription
flow://FlowApiNameSalesforce Flow
apex://ClassNameApex class
externalService://endpointExternal service
standardInvocableAction://NameStandard invocable action
agentforce://AgentNameAnother Agentforce agent (via connected_subagent)

before_reasoning

A procedure that runs once per turn, before the LLM reasoning loop. Use it for guards, pre-fetching data, or setting up state. Template strings (|) are not permitted — this block is deterministic.

Valid statements: set, run (with callbacks), transition, if/else.

before_reasoning:
    # Guard: require verification before proceeding
    if @variables.verified is not True:
        transition to @subagent.Identity

    # Pre-fetch data when condition is met
    if @variables.order_number != "" and @variables.order_status == "":
        run @actions.GetOrderDetails
            with order_number=@variables.order_number
            set @variables.order_status = @outputs.status
            set @variables.return_eligible = @outputs.return_eligible

reasoning

The reasoning loop block. Drives the LLM's behavior for a given turn. The loop iterates until the LLM produces a response (rather than calling another action), then after_reasoning runs.

reasoning.instructions

A procedure with string return type. Re-evaluated on every iteration of the reasoning loop, so it always reflects the latest variable state. Supports | append semantics, {!...} interpolation, and conditional logic.

reasoning:
    instructions: ->
        | Help the customer with their order.
        if @variables.verified:
            | Customer: {!@variables.customer_name}
            | Order: {!@variables.order_number}
        else:
            | Ask the customer to provide their order number.
        | Always be concise and professional.

reasoning.actions

Action bindings available to the LLM during reasoning. Each binding references a declared action and can include a description, available when guard, with clauses, and callback statements.

reasoning:
    actions:
        # Reference a declared action with inputs and callback
        lookup_order: @actions.LookupOrder
            description: "Look up an order by number"
            available when @variables.order_number != ""
            with order_number=@variables.order_number
            set @variables.order_status = @outputs.status

        # Transition action
        go_returns: @utils.transition to @subagent.Returns
            description: "Route to returns when user wants to return items"

        # Slot-fill utility
        capture_order: @utils.setVariables
            description: "Capture order number from conversation"
            with order_number=...

        # Escalation
        escalate: @utils.escalate
            description: "Hand off to a human agent"

after_reasoning

A procedure that runs once per turn, after the LLM reasoning loop completes. Same constraints as before_reasoning — deterministic, no template strings. Use it for post-turn state updates, condition-based transitions, or triggering actions based on what the LLM decided.

after_reasoning:
    if @variables.escalation_required:
        transition to @subagent.Escalation
    set @variables.turn_count = @variables.turn_count + 1

    # Trigger action based on state set during reasoning
    if @variables.issue_type != "":
        run @actions.ReportIssue
            with issue_description=@variables.issue_type
            set @variables.case_number = @outputs.case_number

connected_subagent

A reference to an externally-deployed agent identified by URI. Used to invoke agents that exist outside the current script.

connected_subagent External_Billing:
    target: "agentforce://Billing_Agent"
    description: "Handles complex billing disputes requiring specialist review"
    label: "Billing Specialist"

set

Assigns a value to a mutable variable. Only valid in procedures (before_reasoning, after_reasoning, callbacks). Attempting to set a linked variable is a lint error.

set @variables.status = "active"
set @variables.count = @variables.count + 1
set @variables.verified = @outputs.customer_found
set @variables.tags = []

run / with

run invokes a named action. with binds input parameters to the action. Unlike function calls in expressions, run is a first-class statement — auditable and supports callbacks.

run @actions.LookupOrder
    with order_id=@variables.order_id
    with include_history=False

Multiple parameters in a single with clause (comma-separated):

run @actions.UpdatePreferences
    with temperature_units=..., default_location=...

Use ... (ellipsis) to indicate the LLM should supply the value at runtime:

run @actions.SearchKnowledge
    with query=...
    with category=...

Callbacks

Statements indented beneath a run block (after any with clauses) form a callback. They execute after the action returns and have access to its outputs via @outputs.

Important: @outputs is scoped strictly to the callback block. It is not accessible outside it.

run @actions.VerifyCustomer
    with email=@variables.customer_email
    set @variables.verified = @outputs.customer_found
    set @variables.customer_id = @outputs.customer_id
    transition to @subagent.Verified_Flow

Valid callback statements: set, run (one level deep only), transition. Nested callbacks have their own @outputs scope that shadows the outer one.

transition

Transfers execution to another subagent or execution block. A first-class statement — not a function call. May appear in before_reasoning, after_reasoning, or as a callback statement.

transition to @subagent.billing

In reasoning.actions, the @utils.transition utility exposes this as an LLM-callable action:

go_to_billing: @utils.transition to @subagent.Billing
    description: "Transfer to billing subagent"
    available when @variables.verified is True

if / else

Conditional branching in procedures. Python-style syntax. elif is not supported — use nested if/else.

if @variables.verified:
    set @variables.status = "active"
else:
    transition to @subagent.Identity

In string-return procedures (reasoning.instructions), if/else conditionally appends to the output:

instructions: ->
    | Welcome!
    if @variables.verified:
        | I can see your account details.
    else:
        | Please verify your identity first.
    | How can I help you?

available when

A guard clause on a reasoning.actions binding. When the condition is false, the action is not shown to the LLM during that reasoning turn. Only valid inside reasoning.actions.

reasoning:
    actions:
        create_return: @actions.Create_Return
            available when @variables.return_eligible == True
            with order_id=@variables.order_id
            set @variables.rma_number = @outputs.rma_number

        go_billing: @utils.transition to @subagent.Billing
            available when @variables.verified is True

| Template Strings

The | character introduces a multiline string. The first non-empty line sets the base indentation level. Within a procedure that returns a string, each | line appends to the output.

# Static multiline string
instructions: |
    You are a helpful support agent.
    Always verify the customer before proceeding.

# Dynamic procedure with | append semantics
instructions: ->
    | Welcome to support.
    if @variables.verified:
        | I can see your account details.
    | How can I help you today?

{! } Expression Interpolation

Template strings support runtime expression interpolation with {! expr }. The expression is evaluated and its string representation is inserted inline.

instructions: ->
    | Hello, {!@variables.customer_name}!
    | Your order score is {!@variables.score}/100.
    | Progress: {!@variables.current + 1} of {!@variables.total}
    | Status: {!"verified" if @variables.verified else "unverified"}

Dialect restrictions: some dialects (agentforce) disallow template syntax on certain fields (e.g. before_reasoning). This is enforced as a lint error.

@utils

Built-in utility actions injected as a global namespace by the AgentScript dialect. Used in reasoning.actions bindings or as statements in before_reasoning/after_reasoning.

UtilityDescription
@utils.transition to @subagent.X Transitions to another subagent. Used with the to keyword.
@utils.setVariables Slot-fills one or more variables from the conversation. The LLM uses variable description fields to determine what to extract.
@utils.escalate Hands off the conversation to a human agent. Takes no inputs.
reasoning:
    actions:
        # Transition
        go_billing: @utils.transition to @subagent.Billing
            description: "Route to billing subagent"

        # Slot-fill
        capture_info: @utils.setVariables
            description: "Capture customer name and email"
            with customer_name=...
            with customer_email=...

        # Escalation
        escalate: @utils.escalate
            description: "Hand off to a human agent"

@system_variables

Runtime-provided read-only values injected as a global namespace. Always available without declaration.

VariableDescription
@system_variables.user_inputThe raw text input from the user for the current turn.
before_reasoning:
    if @system_variables.user_input is not None:
        run @actions.LogInput
            with text=@system_variables.user_input