VOOZH about

URL: https://dev.to/conor_61d358cde152bb5800a/agent-script-salesforces-open-language-for-deterministic-agent-orchestration-2h1o

⇱ Agent Script: Salesforce's Open Language for Deterministic Agent Orchestration - DEV Community


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: "SupportBot"
 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: "Generalsupportentrypoint"

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: "ThesupportcaseIDcollectedfromtheuser"
 customer_verified: mutable boolean = False
 description: "Trueonceidentitycheckhaspassed"
 priority: mutable string = "standard"
 description: "Casepriority:standard,high,orcritical"

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: "Handlebillingdisputesandpaymentquestions"

 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.name or @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: "CaseTriageAgent"
 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: "Initialcontactcollectcasedetailsandroute"

 variables:
 product_area: mutable string = ""
 description: "Detectedproduct:SalesCloud,ServiceCloud,DataCloud,MuleSoft"
 customer_tier: mutable string = ""
 description: "Successplantier: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

  1. Install the VS Code extension from the Agent Script GitHub repo.[2] It takes 2 minutes and gives you a working local environment.
  2. Open the playground at the repo's web UI. The preloaded examples cover the most common patterns: verification gates, multi-topic routing, subagent delegation.
  3. 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.
  4. Map your invocable Apex to actions:3 any @InvocableMethod in your org can be wired as an @actions reference. 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.



  1. Agentforce Agent Topics — Salesforce Developer docs. Covers topic definition, instructions, and action assignment within Agentforce. 

  2. Agent Script GitHub repository — Salesforce open-source repo. Contains the language specification, parser, linter, LSP implementation, VS Code extension, and Monaco playground. 

  3. Invocable Actions in Agentforce — Salesforce Developer docs. Covers how Apex, Flow, and external actions are registered and invoked within Agentforce agents. 

  4. @InvocableMethod annotation reference — Salesforce Apex Developer Guide. Full reference for declaring Apex methods as invocable actions, including input/output variable typing. 

  5. Language Server Protocol specification — Microsoft. The open protocol that Agent Script's LSP implements for editor tooling integration.