Salesforce open-sourced Agent Script at TDX 2026, and it solves one of the most frustrating problems in production Agentforce deployments: you can't tell an LLM "always do step A before step B, and only proceed to step C if the customer is verified." Prompt instructions don't enforce control flow. Agent Script does.
This post walks through what Agent Script is, how the language works, mnd how to build a realistic support triage agent with hard routing logic.
The Problem Agent Script Solves
Agentforce topics let you describe what an agent should do — but that description goes through an LLM at runtime.1 The LLM interprets your instructions and decides how to act. Most of the time, it works. Sometimes it doesn't: it skips a verification step, invokes the wrong action, or transitions prematurely.
The workaround most teams use is defensive prompt engineering — stacking ALWAYS, NEVER, and IMPORTANT clauses into system instructions. This is fragile, untestable, and hard to audit.
Agent Script approaches the problem differently: it gives you a structured specification language where control flow is deterministic by design. The LLM still handles reasoning, but your code controls when reasoning happens and what happens after.
From the spec: "execution is decoupled from specification. Agent Script describes what the agent is — its state, its available actions, its instructions — not how the runtime executes it."2
Language Fundamentals
Agent Script is block-based and indentation-sensitive (like Python or YAML). Every agent is composed of blocks. Blocks fall into two categories:
-
Configuration blocks (
config,system) — set the agent's identity and static behavior -
Execution blocks (
topic,start_agent) — define runtime behavior, state, and transitions
Here's a minimal valid Agent Script file:
config:
agent_name: "Support Bot"
default_locale: "en_US"
system:
instructions: |
You are a Salesforce support agent.
Your job is to triage cases and route them to the right team.
Never share internal case notes with the customer.
topic default:
description: "General support entry point"
The pipe character (|) denotes multiline strings — keep your system instructions here rather than inline so they're readable and diffable.
Variables: Typed State in Your Agent
One of the more useful features is first-class variable support with explicit types:
variables:
case_id: mutable string = ""
description: "The support case ID collected from the user"
customer_verified: mutable boolean = False
description: "True once identity check has passed"
priority: mutable string = "standard"
description: "Case priority: standard, high, or critical"
Variables use @variables.name syntax throughout the rest of the script. Declaring them at the top of a topic forces you to think about what state the agent actually needs — and makes it visible during reviews and in the audit trail.
Before and After Reasoning Hooks
This is where Agent Script gets powerful. The before_reasoning and after_reasoning blocks let you inject deterministic logic around the LLM call:
topic billing_support:
description: "Handle billing disputes and payment questions"
variables:
customer_verified: mutable boolean = False
account_id: mutable string = ""
before_reasoning:
run @actions.verify_customer_identity
with email=@variables.user_email
set @variables.customer_verified = @outputs.verified
set @variables.account_id = @outputs.account_id
if not @variables.customer_verified:
transition to @subagent.identity_verification
reasoning:
instructions: |
The customer is verified. Account ID is @variables.account_id.
Help them with their billing question. If the dispute exceeds $500,
collect the details and escalate — do not resolve it yourself.
after_reasoning:
if @outputs.escalation_required:
run @actions.create_escalation_case
with account_id=@variables.account_id
with details=@outputs.dispute_details
transition to @topic.escalation_confirmation
Key mechanics:
-
run @actions.action_name— invokes a named Agentforce action (Apex, Flow, MuleSoft, external API) -
with param=value— passes inputs to the action -
set @variables.name = @outputs.field— captures outputs back into state -
transition to @topic.nameor@subagent.name— deterministic routing, no LLM involved
A Full Support Triage Example
Here's a more complete agent that routes inbound support requests by product category and customer tier:
config:
agent_name: "Case Triage Agent"
default_locale: "en_US"
system:
instructions: |
You are a Salesforce case triage agent.
Identify the product area and collect a clear description of the issue.
Do not attempt to solve technical problems directly — triage and route only.
If the customer is on a Premier Success Plan, acknowledge the SLA.
topic case_intake:
description: "Initial contact — collect case details and route"
variables:
product_area: mutable string = ""
description: "Detected product: Sales Cloud, Service Cloud, Data Cloud, MuleSoft"
customer_tier: mutable string = ""
description: "Success plan tier: standard, premier, signature"
case_description: mutable string = ""
before_reasoning:
run @actions.lookup_account_tier
with contact_id=@variables.contact_id
set @variables.customer_tier = @outputs.tier
reasoning:
instructions: |
Greet the customer. Ask which Salesforce product they need help with
and collect a clear description of the issue.
Customer tier: @variables.customer_tier
after_reasoning:
set @variables.product_area = @outputs.detected_product
set @variables.case_description = @outputs.issue_summary
if @variables.product_area == "MuleSoft":
transition to @subagent.mulesoft_triage
if @variables.product_area == "Data Cloud":
transition to @subagent.data_cloud_triage
if @variables.customer_tier == "signature":
run @actions.notify_tam
with case_summary=@variables.case_description
transition to @topic.premium_intake
transition to @topic.standard_routing
Notice that the LLM does the qualitative work — understanding what product the customer is describing, extracting a clean issue summary — but the routing decisions are code. You can unit-test that transition logic. You can audit it. You can change it without touching a prompt.
Salesforce Actions as First-Class Citizens
Agent Script's @actions namespace maps directly to your Agentforce action library: Apex classes, Flows, MuleSoft API calls, external services. This means all the action tooling you already have — invocable Apex, autolaunched Flows — plugs in without changes.3
Example invocable Apex action that Agent Script can call:
public class LookupAccountTier {
@InvocableMethod(label='Lookup Account Tier' description='Returns the Success Plan tier for a contact')
public static List<Result> execute(List<Request> requests) {
List<Result> results = new List<Result>();
for (Request req : requests) {
Contact c = [
SELECT Account.Success_Plan__c
FROM Contact
WHERE Id = :req.contactId
LIMIT 1
];
Result r = new Result();
r.tier = c.Account.Success_Plan__c != null
? c.Account.Success_Plan__c.toLowerCase()
: 'standard';
results.add(r);
}
return results;
}
public class Request {
@InvocableVariable(required=true) public Id contactId;
}
public class Result {
@InvocableVariable public String tier;
}
}
The action name in Agent Script (@actions.lookup_account_tier) maps to the invocable method's API name in your org.4
Tooling: Parser, LSP, VS Code Extension
The full Agent Script toolchain is available at github.com/salesforce/agentscript:2
- Parser — validates syntax, resolves namespaces
- Linter — catches common mistakes (undefined variables, unreachable transitions)
- LSP — Language Server Protocol implementation for editor support5
- VS Code extension — syntax highlighting, autocomplete, inline validation
- Monaco playground — browser-based editor for prototyping without an org
The VS Code extension is the practical starting point. Install it, open a .ascript file, and you get full IntelliSense for block types, namespace references, and action parameters.
Where to Start
- Install the VS Code extension from the Agent Script GitHub repo.[2] It takes 2 minutes and gives you a working local environment.
- Open the playground at the repo's web UI. The preloaded examples cover the most common patterns: verification gates, multi-topic routing, subagent delegation.
- Audit your existing Agentforce topics:1 identify the transitions and verification steps you're currently enforcing through prompt instructions. Those are your first candidates for Agent Script refactoring.
-
Map your invocable Apex to actions:3 any
@InvocableMethodin your org can be wired as an@actionsreference. Start with the actions your most critical agents already call.
Agent Script is in active development — the spec is open, the tooling is moving fast, and the TDX 2026 announcement included it as a pillar of the Headless 360 initiative. Now is the time to get ahead of it before it becomes the expected pattern for any production Agentforce deployment.
-
Agentforce Agent Topics — Salesforce Developer docs. Covers topic definition, instructions, and action assignment within Agentforce. ↩
-
Agent Script GitHub repository — Salesforce open-source repo. Contains the language specification, parser, linter, LSP implementation, VS Code extension, and Monaco playground. ↩
-
Invocable Actions in Agentforce — Salesforce Developer docs. Covers how Apex, Flow, and external actions are registered and invoked within Agentforce agents. ↩
-
@InvocableMethodannotation reference — Salesforce Apex Developer Guide. Full reference for declaring Apex methods as invocable actions, including input/output variable typing. ↩ -
Language Server Protocol specification — Microsoft. The open protocol that Agent Script's LSP implements for editor tooling integration. ↩
This article was originally published by DEV Community and written by Conor.
Read original article on DEV Community