DEV Community

Conor
Conor

Posted on

Agent Script: Salesforce's Open Language for Deterministic Agent Orchestration

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"
Enter fullscreen mode Exit fullscreen mode

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"
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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: "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
Enter fullscreen mode Exit fullscreen mode

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;
    }
}
Enter fullscreen mode Exit fullscreen mode

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. 

Top comments (0)