# 005 Tier 013a934a

> Reference for the Agent Script language used in Salesforce Agentforce, covering syntax, deployment methods, and troubleshooting.

- Skill: `tools-only/005-tier-013a934a` (Agent Skill, multi-file: 3 files)
- Install (CLI): `npx skillmds add tools-only/005-tier-013a934a`
- Raw SKILL.md: https://api.skillmd.com/api/skills/tools-only/005-tier-013a934a/raw
- Safety review: PASS (external: skillspector CAUTION)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools, API Design
- Tags: Agent Script, Agentforce, Deployment, Metadata, Salesforce
- Author: tools-only (https://skillmd.com/u/tools-only)
- Updated: 2026-08-22
- Page: https://skillmd.com/skills/tools-only/005-tier-013a934a

---

<!-- TIER: 2 | PRIMARY REFERENCE -->
<!-- Read after: SKILL.md -->
<!-- Read before: Specialized guides (actions-reference.md, prompt-templates.md) -->

# Agent Script Reference

Complete syntax reference for the Agent Script language used in Agentforce.
Includes AiAuthoringBundle compatibility notes, reserved words, and error troubleshooting.

**Updated**: December 2025 - Verified through systematic testing.

---

## File Structure

Agent Script files use the `.agent` extension and contain YAML-like syntax with specific Agent Script keywords.

### ⚠️ CRITICAL: Two Deployment Methods

There are **two deployment methods** with **different capabilities**:

| Aspect | GenAiPlannerBundle | AiAuthoringBundle |
|--------|-------------------|-------------------|
| Deploy Command | `sf project deploy start` | `sf agent publish authoring-bundle` |
| **Visible in Agentforce Studio** | ❌ NO | ✅ YES |
| Flow Actions (`flow://`) | ✅ Supported | ✅ Supported (with exact name matching) |
| Apex Actions (`apex://`) | ✅ Supported | ⚠️ Limited (class must exist) |
| Escalation (`@utils.escalate with reason`) | ✅ Supported (tested Dec 2025) | ❌ NOT Supported (SyntaxError) |
| `run` keyword (action callbacks) | ✅ Supported (tested Dec 2025) | ❌ NOT Supported (SyntaxError) |
| Variables without defaults | ✅ Supported | ✅ Supported (tested Dec 2025) |
| Lifecycle blocks (`before/after_reasoning`) | ✅ Supported | ✅ Supported (tested Dec 2025) |
| Topic transitions (`@utils.transition`) | ✅ Supported | ✅ Supported |
| Basic escalation (`@utils.escalate`) | ✅ Supported | ✅ Supported |
| API Version | v65.0+ required | v65.0+ required |

**Why the difference?** These methods correspond to two authoring experiences:
- **Script View** (GenAiPlannerBundle): Full Agent Script syntax with utility actions (transition, set variables, escalate) inherent to the script
- **Canvas/Builder View** (AiAuthoringBundle): Low-code visual builder where some utility actions are not yet available

Salesforce is working on feature parity - future releases will add more actions and variable management to the Canvas view.

---

### AiAuthoringBundle (Visible in Agentforce Studio)

**Use this when**: You need agents visible in Agentforce Studio UI.

**Required Files**:
```
force-app/main/default/aiAuthoringBundles/[AgentName]/
├── [AgentName].agent           # Agent definition
└── [AgentName].bundle-meta.xml # Metadata XML
```

**bundle-meta.xml content**:
```xml
<?xml version="1.0" encoding="UTF-8"?>
<AiAuthoringBundle xmlns="http://soap.sforce.com/2006/04/metadata">
  <bundleType>AGENT</bundleType>
</AiAuthoringBundle>
```

---

### GenAiPlannerBundle (Full Feature Support)

**Use this when**: You need flow actions, escalation with reasons, or full Agent Script syntax.

**Required Files**:
```
force-app/main/default/genAiPlannerBundles/[AgentName]/
├── [AgentName].genAiPlannerBundle  # XML manifest
└── agentScript/
    └── [AgentName]_definition.agent  # Agent Script file
```

**genAiPlannerBundle content**:
```xml
<?xml version="1.0" encoding="UTF-8"?>
<GenAiPlannerBundle xmlns="http://soap.sforce.com/2006/04/metadata">
    <description>Agent description</description>
    <masterLabel>Agent Label</masterLabel>
    <plannerType>Atlas__ConcurrentMultiAgentOrchestration</plannerType>
</GenAiPlannerBundle>
```

**⚠️ WARNING**: Agents deployed via GenAiPlannerBundle exist in org metadata but do **NOT appear** in Agentforce Studio UI!

### Block Order (CRITICAL)

Blocks MUST appear in this order:
1. `system:` - Instructions and messages
2. `config:` - Agent metadata
3. `variables:` - Linked and mutable variables
4. `language:` - Locale settings
5. `start_agent [name]:` - Entry point topic
6. `topic [name]:` - Additional topics

### Complete Working Example

```agentscript
system:
   instructions: "You are a helpful assistant. Be professional and friendly."
   messages:
      welcome: "Hello! How can I help you today?"
      error: "I apologize, but I encountered an issue."

config:
   agent_name: "My_Agent"
   default_agent_user: "user@org.salesforce.com"
   agent_label: "My Agent"
   description: "A helpful assistant agent"

variables:
   EndUserId: linked string
      source: @MessagingSession.MessagingEndUserId
      description: "Messaging End User ID"
   RoutableId: linked string
      source: @MessagingSession.Id
      description: "Messaging Session ID"
   ContactId: linked string
      source: @MessagingEndUser.ContactId
      description: "Contact ID"
   user_query: mutable string
      description: "User's current question"

language:
   default_locale: "en_US"
   additional_locales: ""
   all_additional_locales: False

start_agent topic_selector:
   label: "Topic Selector"
   description: "Routes users to appropriate topics"

   reasoning:
      instructions: ->
         | Determine user intent and route.
      actions:
         go_help: @utils.transition to @topic.help
         go_farewell: @utils.transition to @topic.farewell

topic help:
   label: "Help"
   description: "Provides help to users"

   reasoning:
      instructions: ->
         | Answer the user's question helpfully.

topic farewell:
   label: "Farewell"
   description: "Ends conversation gracefully"

   reasoning:
      instructions: ->
         | Thank the user and say goodbye.
```

---

## Indentation Rules

**CRITICAL**: Agent Script is whitespace-sensitive (like Python/YAML). Use **CONSISTENT indentation** throughout.

| Rule | Details |
|------|---------|
| **Tabs (Recommended)** | ✅ Use tabs for easier manual editing and consistent alignment |
| **Spaces** | 2, 3, or 4 spaces also work if used consistently |
| **Mixing** | ❌ NEVER mix tabs and spaces (causes parse errors) |
| **Consistency** | All lines at same nesting level must use same indentation |

**⚠️ RECOMMENDED: Use TAB indentation for all Agent Script files.** Tabs are easier to edit manually and provide consistent visual alignment across editors.

```agentscript
# ✅ RECOMMENDED - consistent tabs (best for manual editing)
config:
	agent_name: "My_Agent"
	description: "Description"

# ✅ ALSO CORRECT - consistent spaces (if you prefer)
config:
   agent_name: "My_Agent"
   description: "Description"

# ❌ WRONG - mixing tabs and spaces
config:
	agent_name: "My_Agent"    # tab
   description: "My agent"    # spaces - PARSE ERROR!
```

---

## Blocks

### System Block

Global agent settings and instructions. **Must be first block**.

```agentscript
system:
   instructions: "You are a helpful assistant. Be professional."
   messages:
      welcome: "Hello! How can I help you today?"
      error: "I'm sorry, something went wrong. Please try again."
```

⚠️ **NOTE**: System instructions must be a single quoted string. The `|` pipe multiline syntax does NOT work in the `system:` block (only in `reasoning: instructions: ->`).

```agentscript
# ✅ CORRECT - Single quoted string
system:
   instructions: "You are a helpful customer service agent. Be professional and courteous. Never share confidential information."
   messages:
      welcome: "Hello!"
      error: "Sorry, an error occurred."
```

**✅ Template Expressions Work in System Instructions:**

While pipe syntax doesn't work, template expressions `{!expression}` ARE supported in system.instructions:

```agentscript
# ✅ Template expressions work in system.instructions
system:
   instructions: "Welcome {!@variables.user_name}! You are speaking with {!@variables.agent_persona}. Today's date is {!@variables.current_date}."
   messages:
      welcome: "Hello {!@variables.user_name}!"
      error: "Sorry, an error occurred."
```

This allows dynamic personalization in the system prompt while still using a quoted string.

### Config Block

Defines agent metadata. **Required fields**: agent_name/developer_name, default_agent_user, agent_label, description.

**📝 NOTE: No Separate Config File!** All configuration goes directly in the `.agent` file's `config:` block. There is no separate `.agentscript`, `.agentconfig`, or similar config file format.

```agentscript
config:
   agent_name: "Customer_Support_Agent"
   default_agent_user: "agent.user@company.salesforce.com"
   agent_label: "Customer Support"
   description: "Helps customers with orders and inquiries"
   agent_type: "AgentforceServiceAgent"
   enable_enhanced_event_logs: False
```

**Core Fields:**

| Field | Required | Description |
|-------|----------|-------------|
| `agent_name` | Yes | API name (letters, numbers, underscores only) |
| `developer_name` | Yes | Same as agent_name - use one or the other (not both) |
| `default_agent_user` | Yes | Username for agent execution context |
| `agent_label` | Yes | Human-readable name |
| `description` | Yes | What the agent does |

**Optional Fields:**

| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `agent_type` | String | `"AgentforceServiceAgent"` | Agent type: `"AgentforceServiceAgent"` or `"AgentforceEmployeeAgent"` |
| `enable_enhanced_event_logs` | Boolean | `False` | Enable detailed event logging for debugging |
| `agent_template` | String | `None` | Base template for agent behavior |
| `outbound_flow` | String | `None` | Flow to invoke for outbound messages |
| `additional_parameter__*` | Any | — | Dynamic parameters (prefix with `additional_parameter__`) |

**Example with all fields:**
```agentscript
config:
   developer_name: "Enterprise_Service_Agent"
   default_agent_user: "service.agent@company.salesforce.com"
   agent_label: "Enterprise Service Agent"
   description: "Handles complex enterprise service requests"
   agent_type: "AgentforceServiceAgent"
   enable_enhanced_event_logs: True
   outbound_flow: "Send_Welcome_Message"
   additional_parameter__tier: "enterprise"
   additional_parameter__region: "AMER"
```

**NOTE**: Both `agent_name` and `developer_name` work (they're aliases for the same field). Use one or the other, not both.

### Variables Block

Declares state variables. **Linked variables first, then mutable**.

**Linked Variables** (connect to Salesforce data - REQUIRED):
```agentscript
variables:
   EndUserId: linked string
      source: @MessagingSession.MessagingEndUserId
      description: "Messaging End User ID"
   RoutableId: linked string
      source: @MessagingSession.Id
      description: "Messaging Session ID"
   ContactId: linked string
      source: @MessagingEndUser.ContactId
      description: "Contact ID"
```

**Mutable Variables** (agent state):

```agentscript
variables:
   # Without defaults - works in both deployment methods
   user_name: mutable string
      description: "The customer's name"
   order_count: mutable number
      description: "Number of items in cart"
   is_verified: mutable boolean
      description: "Whether identity is verified"

   # With explicit defaults - also valid (optional)
   status: mutable string = ""
      description: "Current status"
   counter: mutable number = 0
      description: "A counter"
```

**Note**: Both syntaxes (with or without defaults) work in **both** GenAiPlannerBundle and AiAuthoringBundle deployments. Tested December 2025.

### Language Block

Locale settings. **Required for deployment**.

```agentscript
language:
   default_locale: "en_US"
   additional_locales: "en_GB,de,fr"
   all_additional_locales: False
```

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `default_locale` | String | Yes | Primary locale (e.g., "en_US") |
| `additional_locales` | String | No | Comma-separated additional locales (e.g., "en_GB,de,fr") |
| `all_additional_locales` | Boolean | No | Set `True` to support all available locales |

**Locale Format**: Use standard locale codes like `en_US`, `en_GB`, `de`, `fr`, `es`, `ja`, etc.

### Topic Blocks

Define conversation topics. **Each topic requires `label:` and `description:`**.

### start_agent Block (Entry Point)

**Every agent MUST have exactly one `start_agent` block** - this is the entry point where conversations begin.

**Two forms are supported:**

```agentscript
# Named form (recommended for multi-topic agents)
start_agent topic_selector:
   label: "Topic Selector"
   description: "Routes users to appropriate topics"

# Unnamed form (simpler, for single-topic agents)
start_agent:
   label: "Main Topic"
   description: "Handles all user requests"
```

**When to use each:**

| Form | Syntax | Use When |
|------|--------|----------|
| **Named** | `start_agent topic_name:` | Multi-topic agent with routing |
| **Unnamed** | `start_agent:` | Single-topic agent, no routing needed |

**Named form** creates a topic that can be referenced with `@topic.topic_name` for transitions back to the entry point.

**Entry point example** (named, recommended):
```agentscript
start_agent topic_selector:
   label: "Topic Selector"
   description: "Routes users to appropriate topics"

   reasoning:
      instructions: ->
         | Determine user intent and route.
      actions:
         go_orders: @utils.transition to @topic.orders
```

**Regular topic**:
```agentscript
topic orders:
   label: "Order Management"
   description: "Handles order inquiries"

   reasoning:
      instructions: ->
         | Help with order questions.
      actions:
         back: @utils.transition to @topic.topic_selector
```

### Topic-Level System Instruction Overrides

**Topics can override the global `system:` block to change agent behavior per topic.** This enables persona switching, tone changes, or specialized behavior without creating separate agents.

```agentscript
# Global system instructions (default behavior)
system:
   instructions: "You are a versatile assistant that adapts based on context."
   messages:
      welcome: "Welcome! I adapt my behavior based on the conversation."
      error: "I encountered an issue. Please try again."

# Topic with system override - professional mode
topic professional:
   label: "Professional Mode"
   description: "Professional business communication"

   system:
      instructions: "You are a formal business professional. Use professional language, avoid casual expressions, focus on efficiency and clarity."

   reasoning:
      instructions: ->
         | [Professional Mode Engaged]
         | Respond with formal business tone.
      actions:
         return_general: @utils.transition to @topic.general

# Topic with different override - creative mode
topic creative:
   label: "Creative Mode"
   description: "Creative brainstorming assistant"

   system:
      instructions: "You are a creative brainstorming partner. Think outside the box, suggest unconventional ideas, be imaginative and supportive."

   reasoning:
      instructions: ->
         | [Creative Mode Activated]
         | Generate creative solutions and explore possibilities.
      actions:
         return_general: @utils.transition to @topic.general

# Topic without override - uses global system instructions
topic general:
   label: "General"
   description: "General conversation"

   reasoning:
      instructions: ->
         | Respond in the default conversational style.
      actions:
         go_professional: @utils.transition to @topic.professional
         go_creative: @utils.transition to @topic.creative
```

**How Topic-Level Overrides Work:**
- If a topic has a `system:` block → use that topic's instructions
- If a topic has NO `system:` block → inherit the global `system:` instructions
- Overrides apply only while in that topic; transitioning away restores the target topic's behavior

**Use Cases:**
- **Persona Switching**: Switch between professional, casual, technical modes
- **Specialized Domains**: Different expertise per topic (technical support vs billing)
- **Localization**: Different tone for different regions/contexts
- **Compliance**: Strict guidelines for sensitive topics (financial, medical)

---

## Variable Types

### Complete Type Reference

| Type | Description | Example | AiAuthoringBundle |
|------|-------------|---------|-------------------|
| `string` | Text values | `name: mutable string = "John"` | ✅ Supported |
| `number` | Numeric (integers & decimals) | `price: mutable number = 99.99` | ✅ Supported |
| `boolean` | True/False (capitalized!) | `active: mutable boolean = True` | ✅ Supported |
| `date` | YYYY-MM-DD format | `start: mutable date = 2025-01-15` | ✅ Supported |
| `datetime` | Full timestamp | `created: mutable datetime` | ✅ Supported |
| `time` | Time only | `appointment: mutable time` | ✅ Supported |
| `currency` | Money values | `total: mutable currency` | ✅ Supported |
| `id` | Salesforce Record ID | `record_id: mutable id` | ✅ Supported |
| `object` | Complex object with Lightning type | See advanced syntax below | ✅ Supported |
| `list[type]` | Array of values | `list[string]`, `list[number]` | ✅ Supported |
| `integer` | Integer values only | `count: mutable integer = 5` | ❌ NOT Supported |
| `long` | Long integers | `big_num: mutable long = 9999999999` | ❌ NOT Supported |

**⚠️ CRITICAL: `integer` and `long` types are NOT supported in AiAuthoringBundle!**
- Validation fails with: "Variable with type integer is not supported for mutable variables"
- Use `number` instead for all numeric values (works for both integers and decimals)

**⚠️ CRITICAL: Collection syntax uses SQUARE BRACKETS, not angle brackets!**
- ✅ CORRECT: `list[string]`, `list[number]`, `list[boolean]`
- ❌ WRONG: `list<string>` (causes "Unexpected '<'" syntax error)
- Only primitive types allowed in lists: `string`, `number`, `boolean`
- `list[object]` is NOT supported

**Notes**:
- Boolean values must be capitalized: `True`, `False`

### Type Restrictions: Mutable vs Linked Variables

**Mutable variables** (state you modify during conversation):

| Type | Mutable | Notes |
|------|---------|-------|
| `string` | ✅ | Primary text type |
| `number` | ✅ | Use for ALL numeric (integer + decimal) |
| `boolean` | ✅ | Must use `True`/`False` |
| `date` | ✅ | YYYY-MM-DD format |
| `timestamp` | ✅ | Use instead of `datetime` |
| `currency` | ✅ | Money values |
| `id` | ✅ | Salesforce Record IDs |
| `list[type]` | ✅ | `list[string]`, `list[number]`, `list[boolean]` only |
| `object` | ✅ | Complex types with `complex_data_type_name` |
| `datetime` | ❌ | **Use `timestamp` instead** |
| `time` | ❌ | Not supported for mutable variables |
| `integer` | ❌ | **Use `number` instead** |
| `long` | ❌ | **Use `number` instead** |

**Linked variables** (connect to Salesforce data):

| Type | Linked | Notes |
|------|--------|-------|
| `string` | ✅ | Most common for IDs as strings |
| `number` | ✅ | Numeric fields |
| `boolean` | ✅ | Checkbox fields |
| `date` | ✅ | Date fields |
| `timestamp` | ✅ | DateTime fields |
| `currency` | ✅ | Currency fields |
| `id` | ✅ | Salesforce ID fields |
| `list[*]` | ❌ | **Collections NOT supported for linked** |
| `object` | ❌ | **Complex types NOT supported for linked** |

### Advanced `object` Type with Lightning Data Types (Tested Dec 2025)

The `object` type enables fine-grained control over action inputs/outputs using Lightning data types:

```agentscript
inputs:
   order_number: object
      description: "The Order Number"
      label: "order_number"
      is_required: False
      is_user_input: False
      complex_data_type_name: "lightning__textType"
outputs:
   order_id: object
      description: "The Record ID"
      label: "order_id"
      complex_data_type_name: "lightning__textType"
      filter_from_agent: False
      is_used_by_planner: True
      is_displayable: False
```

### Lightning Data Types (`complex_data_type_name`)

Lightning Types are Salesforce's complex type system for action inputs/outputs. Use them when you need explicit type mapping between AgentScript and Flows/Apex.

**When to use:**
- Passing data to/from Flows with specific Salesforce types
- Ensuring proper serialization of complex values
- Mapping record types and relationships

**Available Lightning Types:**

| Lightning Type | Use For | Base Type |
|---------------|---------|-----------|
| `lightning__textType` | Text/String values | `string` |
| `lightning__numberType` | Numeric values (integer/decimal) | `number` |
| `lightning__booleanType` | Boolean True/False | `boolean` |
| `lightning__dateTimeStringType` | DateTime as ISO string | `timestamp` |
| `lightning__recordInfoType` | Salesforce Record references | `id` |
| `lightning__currencyType` | Currency values | `currency` |
| `lightning__dateType` | Date values (no time) | `date` |
| `lightning__percentType` | Percentage values | `number` |
| `lightning__urlType` | URL/hyperlink values | `string` |
| `lightning__emailType` | Email addresses | `string` |
| `lightning__phoneType` | Phone numbers | `string` |

**Example with complex types:**
```agentscript
actions:
   update_record: flow://Update_Customer_Record
      inputs:
         customer_id: object
            description: "Customer record ID"
            complex_data_type_name: "lightning__recordInfoType"
         update_date: object
            description: "Date of update"
            complex_data_type_name: "lightning__dateTimeStringType"
      outputs:
         success: object
            complex_data_type_name: "lightning__booleanType"
```

**Input Attributes:** `is_required`, `is_user_input`, `label`, `complex_data_type_name`

**Output Attributes and Defaults:**

| Attribute | Type | Default | Description |
|-----------|------|---------|-------------|
| `filter_from_agent` | Boolean | `False` | Hide sensitive output from LLM (still stored) |
| `is_used_by_planner` | Boolean | `True`* | Allow LLM to use output for reasoning |
| `is_displayable` | Boolean | `False` | Show output to user in chat |
| `complex_data_type_name` | String | — | Lightning type for Salesforce integration |

***Default behavior for `is_used_by_planner`:**
- Defaults to `True` UNLESS `filter_from_agent` is `True`
- When `filter_from_agent: True`, `is_used_by_planner` automatically becomes `False`
- This prevents sensitive data from reaching the LLM

**Example - Sensitive data handling:**
```agentscript
outputs:
   customer_name: string
      description: "Customer's name"
      is_used_by_planner: True     # LLM can use this
      is_displayable: True         # User sees this

   ssn_last_four: string
      description: "Last 4 of SSN (for verification)"
      filter_from_agent: True      # Hidden from LLM
      is_used_by_planner: False    # Auto-set by filter_from_agent
      is_displayable: False        # Don't show user
```

**⚠️ CRITICAL: `filter_from_agent` is NOT supported in AiAuthoringBundle!**
- Causes "Unexpected 'filter_from_agent'" syntax error
- Use conditional topic routing in instructions as alternative
- Works in GenAiPlannerBundle

### Data Type Mappings with Flow (Tested Dec 2025)

| Agent Script Type | Flow Data Type | Status | Notes |
|-------------------|----------------|--------|-------|
| `string` | String | ✅ Works | Standard text values |
| `number` | Number (scale=0) | ✅ Works | Integer values |
| `number` | Number (scale>0) | ✅ Works | Decimal values |
| `boolean` | Boolean | ✅ Works | Use `True`/`False` |
| `list[string]` | Text Collection | ✅ Works | Use `isCollection=true` in Flow |
| `string` | Date | ✅ Works* | *See String I/O pattern below |
| `string` | DateTime | ✅ Works* | *See String I/O pattern below |

**⚠️ All Flow inputs must be provided!** If Flow has more input variables than Agent Script defines, publish fails with "Internal Error".

### Date/DateTime Handling (No Native Types)

Agent Script does NOT have native `date` or `datetime` types. Direct type coercion between `string` (Agent Script) and `Date`/`DateTime` (Flow) will fail.

**Use the String I/O Pattern:**

1. Flow accepts String inputs (not Date/DateTime)
2. Flow parses strings internally with `DATEVALUE()` or `DATETIMEVALUE()`
3. Flow converts back to String for output with `TEXT()`

```xml
<!-- Flow variables use String, not Date -->
<variables>
    <name>inp_DateString</name>
    <dataType>String</dataType>  <!-- NOT Date -->
    <isInput>true</isInput>
</variables>
<formulas>
    <name>parsedDate</name>
    <dataType>Date</dataType>
    <expression>DATEVALUE({!inp_DateString})</expression>
</formulas>
```

```agentscript
# Agent Script uses string type for dates
inputs:
   inp_DateString: string
      description: "Date in YYYY-MM-DD format"
```

---

## Resource References

Use the `@` prefix to reference resources.

| Resource | Syntax | Usage | AiAuthoringBundle |
|----------|--------|-------|-------------------|
| Variables | `@variables.name` | Access stored values | ✅ Supported |
| Actions | `@actions.name` | Invoke defined actions | ✅ Supported |
| Topics | `@topic.name` | Reference topics | ✅ Supported |
| Outputs | `@outputs.field` | Action output values | ✅ Supported |
| Utilities | `@utils.transition` | Built-in utilities | ✅ Supported |
| Utilities | `@utils.escalate` | Escalate to human | ✅ Supported |
| Utilities | `@utils.setVariables` | Set multiple variables | ⚠️ GenAiPlannerBundle only |
| Utilities | `@utils.set` | Set single variable | ⚠️ GenAiPlannerBundle only |

### @utils.setVariables Usage

**In GenAiPlannerBundle (Supported):**

```agentscript
# ✅ GenAiPlannerBundle - @utils.setVariables works
reasoning:
   actions:
      set_some_variables: @utils.setVariables
         description: "Set user context variables"
         available when @variables.needs_update == True
         with user_name=...         # LLM slot-fills, inherits type from variable
         with visit_count=@variables.visit_count + 1   # Fixed expression

      clear_session: @utils.setVariables
         description: "Reset session state"
         with is_logged_in=False
         with cart_items=[]
```

**Key Behaviors:**
- `with var=...` - LLM slot-fills the value (inherits type from variable definition)
- `with var=@expression` - Fixed value from expression (not slot-filled)
- Types must match variable declarations

**In AiAuthoringBundle (NOT Supported):**

⚠️ `@utils.setVariables` and `@utils.set` cause "Unknown utils declaration type" errors in AiAuthoringBundle. Use the `set` keyword in instructions instead:

```agentscript
# ❌ WRONG - @utils.setVariables NOT supported in AiAuthoringBundle
reasoning:
   actions:
      update_state: @utils.setVariables
         with user_name=...
         with is_verified=True

# ✅ CORRECT - Use 'set' keyword in instructions (AiAuthoringBundle)
reasoning:
   instructions: ->
      | Ask the user for their name.
      set @variables.user_name = ...
      | Verify the user.
      set @variables.is_verified = True
```

```agentscript
# Variable reference
if @variables.user_name is None:

# Action reference
invoke: @actions.get_order
    with order_id=@variables.current_order_id

# Topic reference
go: @utils.transition to @topic.checkout

# Output capture
set @variables.status = @outputs.order_status

# Escalation
escalate: @utils.escalate
    description: "Transfer to human agent"
```

---

## Instructions

### Syntax (CRITICAL)

Use `instructions: ->` (with space before arrow), NOT `instructions:->`.

```agentscript
# ✅ CORRECT
reasoning:
   instructions: ->
      | Determine user intent.

# ❌ WRONG - missing space before arrow
reasoning:
   instructions:->
      | Determine user intent.
```

### Prompt Mode (|)

Use `|` for natural language instructions:

```agentscript
instructions: ->
   | This is line one.
   | This is line two.
   | Each line starts with a pipe.
```

### Procedural Mode (->)

Use `->` for logic-based instructions:

```agentscript
instructions: ->
   if @variables.amount > 1000:
      | This is a large order.
   else:
      | Standard order processing.
```

### Procedural Instructions with Inline Actions (Advanced Pattern)

**You can execute actions directly inside the `instructions:->` block for conditional data loading.** This pattern fetches data only when needed, reducing unnecessary API calls.

```agentscript
topic order_status:
   label: "Order Status"
   description: "Looks up and explains order status"

   actions:
      get_order_status:
         description: "Retrieves current status for an order"
         inputs:
            order_id: string
               description: "The unique order identifier"
         outputs:
            status: string
               description: "Current order status"
            tracking_number: string
               description: "Shipping tracking number"
         target: "flow://GetOrderStatus"

   reasoning:
      instructions:->
         # Step 1: Validate input - ask for order ID if missing
         if not @variables.order_id:
            | Ask the customer for their order number so you can look up the status.

         # Step 2: Fetch data only when we have order_id but not status yet
         if @variables.order_id and not @variables.order_status:
            run @actions.get_order_status
               with order_id=@variables.order_id
               set @variables.order_status = @outputs.status
               set @variables.tracking_number = @outputs.tracking_number

         # Step 3: Provide context-specific guidance based on status
         | The customer's order {!@variables.order_id} has status: {!@variables.order_status}

         if @variables.order_status == "pending":
            | The order is being processed. Let them know:
            | - Order is confirmed and being prepared
            | - They'll receive tracking info within 24 hours

         if @variables.order_status == "shipped":
            | The order has shipped! Provide:
            | - Tracking number: {!@variables.tracking_number}
            | - Link to track shipment

         if @variables.order_status == "delivered":
            | The order was delivered. Confirm they received it.
            | Ask if everything was satisfactory.

         | Be proactive and helpful. Anticipate what the customer might need next.
```

**Key Pattern Features:**
- **Conditional Data Loading**: `if @variables.x and not @variables.y:` guards prevent redundant API calls
- **Action Inside Instructions**: `run @actions.x` executes directly in the instruction flow
- **State-Based Guidance**: Different instructions appear based on variable values
- **Progressive Flow**: Ask for input → Fetch data → Provide tailored response

**When to Use This Pattern:**
- Data should only be fetched after certain conditions are met
- You need to adapt instructions based on fetched data
- The action result determines what guidance to provide

**⚠️ Note on Deployment Methods:**
- This pattern uses `run` keyword which has limited support in AiAuthoringBundle
- Test thoroughly - if validation fails, consider GenAiPlannerBundle deployment

### ⚠️ CRITICAL: Slot Filling (`...`) Cannot Be Inside Conditionals (Tested Dec 2025)

**Slot filling with the `set @variables.x = ...` syntax CANNOT be placed inside conditionals!**

This causes `SyntaxError: Unexpected 'if'` during validation.

```agentscript
# ❌ WRONG - Slot filling inside conditional causes SyntaxError
instructions: ->
   if @variables.name is None:
      | Please provide your name.
      set @variables.name = ...   # FAILS! Cannot use ... inside if block

# ✅ CORRECT - Use unconditional slot filling, LLM handles context
instructions: ->
   | If the user hasn't provided their name, ask for it.
   set @variables.name = ...
   | Once you have the name, proceed with the order.

# ✅ ALSO CORRECT - Use action with slot filling (not in conditional)
reasoning:
   actions:
      collect_info: @actions.collect_customer_info
         with name=...
         with email=...
         set @variables.name = @outputs.name
```

**Why?** The `...` ellipsis is evaluated at parse time, not runtime. The parser cannot determine conditional paths, so slot filling must be at the top level of instructions.

**⚠️ CRITICAL: Pipes Cannot Be Nested Inside Pipes!**

```agentscript
# ❌ WRONG - Nested pipes cause "Start token somehow not of the form | + 2 spaces" error
instructions: ->
   | Some text.
   | if @variables.name is None:
   | 	| Please provide your name.    # NESTED PIPE - FAILS!

# ✅ CORRECT - Conditionals at same level as pipes, not nested inside
instructions: ->
   | Some introductory text.
   if @variables.name is None:
      | Please provide your name.
   | More text continues here.
```

### Template Expressions

Use `{!...}` for variable interpolation:

```agentscript
instructions: ->
   | Hello {!@variables.user_name}!
   | Your order total is ${!@variables.total}.
```

---

## Conditionals

### If/Else

```agentscript
instructions: ->
   if @variables.amount > 1000:
      | Large order - requires approval.
   else:
      | Standard order.

   if @variables.status == "shipped":
      | Your order is on its way!

   if @variables.email is None:
      | Please provide your email address.

   if @variables.verified == True:
      | Identity confirmed.
```

### Comparison Operators

| Operator | Meaning | Example |
|----------|---------|---------|
| `==` | Equals | `@variables.status == "active"` |
| `!=` | Not equals | `@variables.count != 0` |
| `>` | Greater than | `@variables.amount > 100` |
| `<` | Less than | `@variables.count < 10` |
| `>=` | Greater or equal | `@variables.age >= 18` |
| `<=` | Less or equal | `@variables.priority <= 5` |

### Null Checks

```agentscript
if @variables.name is None:
   | Name not provided.

if @variables.email is not None:
   | Email is available.
```

### Logical Operators

| Operator | Meaning | Example |
|----------|---------|---------|
| `and` | Both conditions true | `@variables.a and @variables.b` |
| `or` | At least one true | `@variables.x or @variables.y` |
| `not` | Negation | `not @variables.flag` |

```agentscript
# Combine conditions with AND
if @variables.verified == True and @variables.amount > 0:
   | Processing verified request.

# Check multiple conditions with OR
if @variables.is_vip == True or @variables.loyalty_years > 5:
   | Premium customer detected.

# Negate a condition
if not @variables.is_blocked:
   | Access granted.
```

### N-ary Boolean Operations (3+ Operands)

**AgentScript fully supports N-ary boolean operations** - you can chain 3 or more conditions with `and` or `or` operators. This is the recommended pattern instead of nested if statements.

```agentscript
# ✅ Three+ conditions with AND
if @variables.is_authenticated and @variables.has_permission and @variables.is_active:
   transition to @topic.authorized

# ✅ Three+ conditions with OR
if @variables.is_admin or @variables.is_moderator or @variables.is_owner:
   transition to @topic.elevated_access

# ✅ Complex multi-condition check (4+ operands)
if @variables.verified and @variables.amount > 0 and @variables.email is not None and @variables.consent == True:
   | All validation criteria met. Proceeding with order.

# ✅ N-ary in available when (action conditional availability)
actions:
   process_return: @actions.process_return
      description: "Process a product return"
      available when @variables.eligible == True and @variables.order_id is not None and @variables.tier != "basic"

   apply_premium_discount: @actions.calculate_discount
      description: "Apply premium customer discount"
      available when @variables.is_premium or @variables.loyalty_years > 5 or @variables.total_spent > 10000
```

**⚠️ Mixing `and`/`or`**: While you can chain multiple `and` or multiple `or` operators, avoid mixing them without clear grouping (parentheses are NOT supported). Use separate conditions for complex mixed logic.

```agentscript
# ⚠️ AVOID - Mixed and/or can be ambiguous
if @variables.a and @variables.b or @variables.c:   # Unclear precedence

# ✅ PREFER - Separate conditions for clarity
if @variables.a and @variables.b:
   | Path A: Both A and B are true.
if @variables.c:
   | Path B: C is true.
```

### ⚠️ CRITICAL: Nested If Statements NOT Supported (Tested Dec 2025)

**Nested if statements (if inside if) cause parse errors in AiAuthoringBundle!**

```agentscript
# ❌ WRONG - Nested if causes "Missing required element" and "Unexpected 'else'"
if @variables.is_premium == True:
   | User is premium.
   if @variables.order_total > 1000:     # NESTED IF - FAILS!
      | Large order.
   else:
      | Regular order.
else:
   | Standard user.

# ✅ CORRECT - Use flat conditionals with 'and' operators
if @variables.is_premium == True and @variables.order_total > 1000:
   | Premium user with large order.
if @variables.is_premium == True and @variables.order_total <= 1000:
   | Premium user with regular order.
if @variables.is_premium == False:
   | Standard user.
```

### Math Operators (Tested Dec 2025)

Math operators work in both `set` statements and conditions:

```agentscript
# ✅ Addition in set statement
set @variables.counter = @variables.counter + 1

# ✅ Subtraction in set statement
set @variables.remaining = @variables.total - 100

# ✅ Math in conditions
if @variables.counter + 5 > 10:
   | Counter plus 5 is greater than 10.

if @variables.total - 50 < 0:
   | Total minus 50 would be negative.
```

| Operator | Works in `set` | Works in `if` | Example |
|----------|---------------|---------------|---------|
| `+` | ✅ Yes | ✅ Yes | `set @variables.x = @variables.x + 1` |
| `-` | ✅ Yes | ✅ Yes | `if @variables.total - 50 < 0:` |

### Additional Expression Features

**Length function (`len`):**
```agentscript
# Check if list is empty
if len(@variables.cart_items) == 0:
   | Your cart is empty.

# Check list size for pagination
if len(@variables.results) > 10:
   | Showing first 10 of {!len(@variables.results)} results.
```

**Ternary expression (`x if condition else y`):**
```agentscript
# Conditional value assignment
set @variables.greeting = "VIP" if @variables.is_premium else "Customer"

# In output context
| Hello {!"VIP" if @variables.tier == "premium" else "valued customer"}!
```

**Unary negation (`-`):**
```agentscript
# Negate a value
set @variables.offset = -@variables.amount
```

**⚠️ Grouping Parentheses:**
Parentheses `()` for expression grouping are NOT well-supported. Avoid complex expressions that require precedence grouping:

```agentscript
# ⚠️ AVOID - complex grouped expressions may not parse
if (@variables.a and @variables.b) or @variables.c:
   | This may not work.

# ✅ PREFER - split into separate conditions
if @variables.a and @variables.b:
   | Path A.
if @variables.c:
   | Path C.
```

---

## Comments

Use the `#` symbol to add comments. Everything after `#` on a line is ignored.

```agentscript
# This is a comment - the parser ignores this line
config:
   agent_name: "My_Agent"    # Inline comment explaining the field
   description: "A helpful agent"

# Comments help document your agent script
# Use them to explain complex logic
```

---

## Actions

### Defining Actions

```agentscript
topic my_topic:
   label: "My Topic"
   description: "Topic description"

   actions:
      get_order:
         description: "Retrieves order details"
         inputs:
            order_id: string
               description: "The order ID"
         outputs:
            status: string
               description: "Order status"
            total: number
               description: "Order total"
         target: "flow://Get_Order_Details"

   reasoning:
      instructions: ->
         | Help the user with their order.
```

### Action-Level Attributes (Advanced)

Beyond `inputs`, `outputs`, `target`, and `description`, actions support additional attributes for controlling behavior:

| Attribute | Type | Description | Default |
|-----------|------|-------------|---------|
| `require_user_confirmation` | Boolean | Prompt user to confirm before executing | `False` |
| `include_in_progress_indicator` | Boolean | Show progress indicator during execution | `False` |
| `label` | String | Display label for the action | Action name |

**Example with Advanced Attributes:**
```agentscript
actions:
   cancel_order:
      description: "Cancels an order and issues refund"
      require_user_confirmation: True    # ★ Ask user before

…(truncated)
