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
| Form | Example |
|---|---|
| Scalar string | "hello" |
| Scalar number | 42, 3.14 |
| Scalar boolean | True, False |
| Template string | | on next indented line |
| Procedure | -> or auto-detected |
| Sequence (list) | YAML-style - item |
| Sub-block | Nested indented block |
Type System
Scalar types
| Type | Description | Example |
|---|---|---|
string | Quoted string | "hello" |
number | Integer or float | 42, 3.14 |
boolean | Boolean — capitalized | True, False |
object | Untyped structured value | None, {} |
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"
| Modifier | Description |
|---|---|
mutable | Read and write from within the agent via set, @utils.setVariables, or with parameters. |
linked | Read-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):
| Operator | Description |
|---|---|
or | Logical OR |
and | Logical AND |
not | Logical NOT (unary) |
== != | Equality, inequality |
< <= > >= | Numeric comparison |
is is not | Identity / None check |
+ - | Addition, subtraction |
* / | Multiplication, division |
expr.field | Member access |
expr[index] | Subscript / index |
a if c else b | Ternary (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
| Namespace | Resolves to |
|---|---|
@variables | Agent state variables (declared in variables:) |
@outputs | Action outputs — scoped strictly to the callback block |
@actions | Action definitions declared in actions: |
@subagent / @topic | Named subagent/topic blocks |
@start_agent | Alias for @subagent |
@utils | Built-in utilities injected by the dialect |
@system_variables | Runtime-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.
instructions optional — string 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.
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).
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
| Scheme | Description |
|---|---|
flow://FlowApiName | Salesforce Flow |
apex://ClassName | Apex class |
externalService://endpoint | External service |
standardInvocableAction://Name | Standard invocable action |
agentforce://AgentName | Another 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.
| Utility | Description |
|---|---|
@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.
| Variable | Description |
|---|---|
@system_variables.user_input | The 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