# Agent Script

Agent Script is a programming language purpose-built for AI agents. It gives you deterministic control over workflows while letting the LLM reason where it matters. One file defines your entire agent: subagents, actions, transitions, and guardrails.

### Readable by Anyone

Syntax designed so product managers, architects, and developers can all understand what an agent does at a glance.

### Deterministic Where It Counts

Business-critical logic runs before the LLM. Conditionals, transitions, and action chains execute exactly as written.

### Source-Controlled

`.agent` files live in your repo. Version, diff, review, and deploy agents the same way you ship code.

## Your First Agent in 30 Lines

Here's a complete agent that routes customer requests, verifies identity, and manages orders:

```yaml
config:
  developer_name: "Support_Agent"
  agent_type: "AgentforceServiceAgent"

system:
  instructions: "You are a customer support agent. Be concise and helpful."

start_agent topic_selector:
  description: "Welcome the user and route to the right subagent"

  reasoning:
    instructions: ->
      | Welcome the user and determine what they need help with.
      | Use the available actions to route them appropriately.

    actions:
      go_to_orders: @utils.transition to @subagent.Order_Management
        description: "Handles order lookup, refunds, and updates"

      go_to_faq: @utils.transition to @subagent.General_FAQ
        description: "Answers common questions about products and policies"

      go_to_escalation: @utils.transition to @subagent.Escalation
        description: "Connects user with a human representative"
```

That's it. The agent greets the user, the LLM classifies their intent, and they're routed to the right subagent. No framework, no boilerplate, no deployment pipeline to configure.

## Two Types of Instructions

Agent Script's power comes from separating **what's deterministic** from **what needs AI reasoning**. Two arrow types make this explicit:

#### Logic (Deterministic)

```yaml
# -> means "run this code exactly"
reasoning:
  instructions: ->
    run @actions.lookup_order
      with email=@variables.customer_email
      set @variables.order=@outputs.order_data

    if @variables.order is None:
      | We couldn't find an order with that email. Can you double-check?
    else:
      | Here's what we found for your order: {!@variables.order}
```

Logic instructions execute **before** the LLM sees anything. Actions run, variables are set, conditionals branch — all deterministically.

#### Prompt (AI Reasoning)

```yaml
# | means "send this to the LLM"
reasoning:
  instructions: ->
    | The customer is asking about their order.
    | Review the order details in {!@variables.order} and help them
      with whatever they need — tracking, modifications, or returns.
    | If they seem frustrated, proactively offer to escalate
      using {!@actions.go_to_escalation}.
```

Prompt instructions are natural language sent to the LLM. The AI interprets them, reasons about context, and decides how to respond.

#### Mixed (Both Together)

```yaml
reasoning:
  instructions: ->
    # Deterministic: always fetch the customer's tier
    run @actions.get_loyalty_tier
      with customer_id=@variables.customer_id
      set @variables.tier=@outputs.tier

    # AI reasoning: let the LLM decide what to do with it
    | The customer's loyalty tier is {!@variables.tier}.
    | Adjust your tone and offers accordingly — Platinum members
      get priority treatment and exclusive offers.
```

Mix both freely. Run an action deterministically, then hand the results to the LLM for interpretation.

## Patterns That Ship

#### Action Chaining

Run multiple actions in guaranteed sequence. No hoping the LLM remembers step 2.

```yaml
reasoning:
  instructions: ->
    # Step 1: Get the order
    run @actions.lookup_current_order
      with member_email=@variables.member_email
      set @variables.order_summary=@outputs.order_summary

    # Step 2: Check return eligibility (uses step 1's output)
    run @actions.check_return_eligibility
      with order_id=@variables.order_summary
      set @variables.eligible=@outputs.eligible

    | Show the order summary and return eligibility to the customer.
```

#### Conditional Routing

Route users based on state — not LLM guesswork.

```yaml
reasoning:
  instructions: ->
    if @variables.loyalty_tier == "Platinum VIP":
      transition to @subagent.vip_support

    if @variables.is_verified == False:
      transition to @subagent.identity_verification

    | Welcome back! How can I help you today?
```

Transitions happen before the LLM processes any other instructions.

#### Data Hydration

Load data before the LLM starts thinking. Use `before_reasoning` to hydrate context on every turn:

```yaml
before_reasoning: ->
  if @variables.customer_profile == "":
    run @actions.get_customer_profile
      with user_id=@variables.user_id
      set @variables.customer_profile=@outputs.profile

reasoning:
  instructions: ->
    | You're helping {!@variables.customer_profile}.
    | Reference their history when making recommendations.
```

The `before_reasoning` block runs before every reasoning turn — the LLM always has fresh data.

#### Filtered Visibility

Control which tools the LLM can see based on state. Unverified users can't access order management.

```yaml
reasoning:
  actions:
    go_to_identity: @utils.transition to @subagent.Identity
      description: "Verifies user identity"
      available when @variables.verified == False

    go_to_order: @utils.transition to @subagent.Order_Management
      description: "Handles order management"
      available when @variables.verified == True
```

#### Action + Transition

Define follow-up actions that fire automatically when the LLM calls a tool:

```yaml
reasoning:
  instructions: ->
    | If the user wants information, use {!@actions.my_action}.

  actions:
    my_action: @actions.my_action
      with foo=@variables.Foo
      set @variables.status = @outputs.status
      run @actions.other_action
        set @variables.some_other_result=@outputs.data
```

Whenever the LLM calls `my_action`, `other_action` fires automatically afterward. No extra prompt needed.

## Building Blocks

#### Variables

Variables track state across subagents and conversation turns. Three types cover every use case:

```yaml
variables:
  # Linked: populated from external context (messaging session, API)
  user_id: linked string
    description: "User identifier from the messaging session"

  # Mutable: your agent's working memory
  verified: mutable boolean = False
    description: "Whether identity verification passed"

  order_data: mutable string
    description: "Current order details"

  # Slot-fill: LLM extracts from conversation
  customer_email: mutable string = ...
    description: "Customer's email address"
```

The `...` token tells the LLM to extract the value from conversation. When the user says "my email is jane@example.com", the agent captures it automatically.

#### Subagents

Each subagent is a self-contained unit with its own instructions, actions, and transitions. Think of them as microservices for conversation:

```yaml
subagent Identity_Verification:
  description: "Verify the customer's identity before accessing account data"

  reasoning:
    instructions: ->
      | Ask the customer for their email address.
      | Then use {!@actions.send_code} to send a verification code.
      | Once they provide the code, use {!@actions.verify_code}.

    actions:
      send_code: @actions.Send_Verification_Code
        with email=@variables.customer_email
        set @variables.auth_key=@outputs.auth_key

      verify_code: @actions.Verify_Code
        with code=@variables.customer_code
        with auth_key=@variables.auth_key
        set @variables.verified=@outputs.is_verified

  after_reasoning: ->
    if @variables.verified == True:
      transition to @subagent.topic_selector
```

The `after_reasoning` block runs after the LLM completes its turn — perfect for cleanup, state updates, and automatic transitions.

#### System & Personas

Override global behavior with per-subagent system instructions. VIP customers get a completely different personality without affecting other subagents:

```yaml
system:
  instructions: "You are a helpful customer support agent for Acme Corp."

subagent VIP_Support:
  description: "Premium support for VIP customers"

  system:
    instructions: ->
      | You are a premium concierge for Acme Corp's VIP program.
      | Address the customer by name. Offer proactive solutions.
      | You have authority to issue credits up to $500.

  reasoning:
    instructions: ->
      | Provide white-glove service to {!@variables.customer_name}.
```

#### Actions

Actions are how your agent interacts with the world. Agent Script supports many target protocols:

```yaml
actions:
  # Call an Apex class
  get_profile: @actions.Get_Customer_Profile
    target: "apex://c__CustomerActions.getProfile"

  # Trigger a Flow
  process_refund: @actions.Issue_Refund
    target: "flow://Issue_Refund"

  # Call an external API via External Services
  lookup_orders: @actions.Lookup_Orders
    target: "externalService://OrdersAPI.listOrders"

  # Use a Prompt Template
  summarize: @actions.Summarize_Case
    target: "prompt://Case_Summary_Template"
```

Apex, Flows, External Services, Prompt Templates, and more — all invoked with the same `@actions` syntax. Your agent doesn't care where the logic lives.

## Deploy from Your IDE

Agent Script files are standard source — build, test, and deploy using the tools you already know:

#### CLI

```bash
# Publish your agent to a Salesforce org
sf agent generate -d my-agent.agent
sf agent publish -d force-app

# Run automated tests
sf agent test run --name "My_Agent_Tests"
```

#### Claude Code

```bash
# Install the Agentforce lifecycle plugin
claude plugin install agentforce-adlc@claude-plugins-official

# Build, publish, and test — all from natural language
claude "Build a support agent that handles order tracking
       and returns. Deploy it to my org and run tests."

# Or run the CLI directly
sf agent publish authoring-bundle --api-name Support_Agent
sf agent test run --api-name Support_Agent_Tests --wait 10
```

## What's Coming Next

> **ℹ️ On the Roadmap**: Agent Script is evolving fast. Here's what's shipping soon:
>
> - **MCP Integration** — Connect agents directly to Model Context Protocol servers. Your agent will be able to call external tools, databases, and APIs through the standard MCP interface, making it a first-class citizen in the broader AI tooling ecosystem.
> - **Enhanced Flexibility** — More expressive control flow, richer variable types, and deeper integration with Salesforce data models. Write less configuration, get more capability.
> - **Cross-Agent Orchestration** — Agents that can invoke other agents as tools, enabling multi-agent architectures for complex enterprise workflows.

## Quick Reference

| Symbol | Meaning | Example |
|--------|---------|---------|
| `->` | Logic instructions (deterministic) | `instructions: ->` |
| `\|` | Prompt instructions (sent to LLM) | `\| Help the customer with their order` |
| `@variables.` | Reference a variable | `@variables.customer_email` |
| `@actions.` | Reference an action | `@actions.Get_Order` |
| `@subagent.` | Delegate to another subagent | `@subagent.Order_Management` |
| `@outputs.` | Reference action output | `@outputs.order_data` |
| `@utils.` | Utility functions | `@utils.transition to @subagent.FAQ` |
| `{!expr}` | Interpolate in prompts | `{!@variables.name}` |
| `...` | LLM slot-fill token | `with order_id = ...` |
| `#` | Comment | `# This is a comment` |

## Language Reference

### Variable Types

| Type | Notes |
|------|-------|
| `string` | Alphanumeric text |
| `number` | IEEE 754 double-precision (integers and decimals) |
| `boolean` | `True` / `False` (case-sensitive, capitalize first letter) |
| `object` | JSON object — regular variables only, not linked |
| `date` | Any valid date format |
| `id` | Salesforce record ID |
| `list[<type>]` | List of any primitive type — regular variables only, not linked |

Variables come in three kinds: **regular** (mutable, with optional default), **linked** (read-only, tied to an external source like `@session.sessionID`), and **system** (predefined, read-only — currently only `@system_variables.user_input`).

### Action Targets

Actions support several target protocols:

| Target | Format | Example |
|--------|--------|---------|
| Apex | `apex://<class>.<method>` | `apex://c__CustomerActions.getProfile` |
| Flow | `flow://<flow_name>` | `flow://Issue_Refund` |
| Prompt | `prompt://<template_name>` | `prompt://Case_Summary_Template` |

Action parameter types: `string`, `number`, `integer`, `long`, `boolean`, `object`, `date`, `datetime`, `time`, `currency`, `id`, `list[<type>]`.

### Operators

| Category | Operators |
|----------|----------|
| Comparison | `==`, `!=`, `<`, `<=`, `>`, `>=`, `is`, `is not` |
| Logical | `and`, `or`, `not` |
| Arithmetic | `+`, `-` |

Conditionals support `if` and `else` only (no `else if`).

### Utils

| Utility | Purpose |
|---------|---------|
| `@utils.transition to @subagent.<name>` | One-way transfer to another subagent. No return to caller. Discards any accumulated prompt from the source subagent. |
| `@utils.setVariables` | Lets the LLM set variable values from conversation context. Use `...` token for LLM-determined values. |
| `@utils.escalate` | Hands off to a human service rep via Omni-Channel. Requires an active `connection messaging` block. |

### Flow of Control

1. Every utterance starts at the `start_agent` subagent (the router).
2. The router classifies intent and transitions to the appropriate `subagent`.
3. Reasoning instructions are parsed sequentially, top-to-bottom.
4. Logic instructions (`->`) execute deterministically — actions run, variables set, conditions evaluate.
5. Prompt instructions (`|`) are concatenated into a single prompt sent to the LLM.
6. `after_reasoning` runs after the LLM responds.
7. The next utterance returns to `start_agent`.

Transitions via `@utils.transition to` are **one-way** — the caller's context is discarded. Use `@subagent.<name>` as a tool reference for **round-trip** delegation that returns to the caller.

## Keep Going

### [Official Language Reference](<https://developer.salesforce.com/docs/ai/agentforce/guide/ascript-reference.html>)

Full syntax reference, operators, and block types.

### [Patterns Library](<https://developer.salesforce.com/docs/ai/agentforce/guide/ascript-patterns.html>)

10+ battle-tested patterns for common agent workflows.

### [Agent Script Recipes](<https://developer.salesforce.com/sample-apps/agent-script-recipes>)

15 official open-source recipes you can clone and modify.