# Instruction Resolution > How Agent Script instructions are processed at runtime: from static text to dynamic LLM prompts. --- ## 1. Runtime Lifecycle AgentScript defines deterministic lifecycle hooks around an iterative LLM reasoning loop: ```text before_reasoning (once per turn) -> resolve reasoning.instructions (every reasoning iteration) -> LLM calls a tool or produces a user response -> tool call: execute it, then resolve reasoning.instructions again -> response: end the loop -> after_reasoning (once, if execution reaches the hook) ``` Do not confuse a new reasoning iteration after a tool call with `after_reasoning`. The latter does not run after each tool. --- ## 2. Instruction Surfaces | Surface | Runtime meaning | Authoring use | |---|---|---| | Global `system.instructions` | Default system prompt for every execution block | Durable identity, safety, scope, and response invariants | | Subagent `system.instructions` | Replaces the global system prompt for that subagent; it does not merge | Rare specialist override that restates every invariant still required | | `reasoning.instructions` | Runtime-resolved instructions rebuilt on every reasoning iteration | Current objective, relevant state, action guidance, and stop conditions | | `before_reasoning` | Deterministic procedure that runs once before the reasoning loop | Preconditions, data preparation, and early transitions | | `after_reasoning` | Deterministic procedure that runs if execution reaches the hook after the reasoning loop | Final state updates and post-response transitions | Omit a subagent system override when the global instructions already apply. If an override is necessary, copy the durable invariants that subagent must retain; the compiler selects the subagent value instead of the global value. Treat effective system and reasoning instructions as cumulative. Do not depend on a model provider separating or prioritizing the AgentScript surfaces differently unless the target runtime's public, versioned contract guarantees that behavior. Their combined meaning must contain no contradiction, duplicate policy, or reliance on one layer overriding the other. ### Separate AgentScript Control from Model Instructions Subagents and variables are AgentScript runtime concepts, not model knowledge. The compiler and runtime select the current execution block, evaluate conditions, run deterministic actions, update state, interpolate values, and expose the currently available tools. Portable model instructions must not depend on a provider exposing structured execution-block identity or direct variable-store access unless the target runtime's public contract guarantees it. Developer documentation may explain subagents and variables because authors need that mental model. Text sent to the model must instead state the concrete task and response duty. Do not tell the model to inspect the active subagent or read `@variables`. ### Scope Response Duties by Branch Do not put an unconditional response duty in the global layer when any branch must route, verify, clarify, refuse, close, or escalate without answering. This conflict is especially common in router-first agents: ```agentscript # WRONG: the effective prompt says both answer and do not answer. system: instructions: "Answer the user's questions helpfully." start_agent agent_router: reasoning: instructions: -> | Do not answer. Route the request. # RIGHT: the global rule allows the current operating task to set posture. system: instructions: | Perform only the current operating task described below. Answer the underlying request only when that task calls for an answer. Otherwise route, verify, clarify, refuse, or escalate as directed. ``` Review the effective pair for every execution branch: global or replacement system instructions plus the branch's resolved reasoning instructions. A scope narrowing is compatible only when the global layer already permits that narrowing. For every reachable LLM call, that effective pair must prescribe one unambiguous response posture. A subagent system replacement must restate every durable invariant it retains. ### Deterministic Resolution Before Each LLM Call Before every reasoning iteration, the runtime evaluates deterministic constructs in `reasoning.instructions: ->`. This happens before the LLM sees the resolved reasoning instructions. ### What Happens During Resolution 1. **`if`/`else` evaluation**: Conditions are evaluated against current variable values. Only the matching branch is included in the prompt. 2. **Variable injection**: `{!@variables.X}` tokens are replaced with current values. 3. **`run` execution**: Deterministic `run @actions.X` calls execute and their outputs are captured. 4. **`set` execution**: Variable assignments execute immediately. 5. **`transition to`**: If reached, the subagent switch happens immediately (LLM is never called). ### Resolution Example Given this instruction block: ```agentscript reasoning: instructions: -> # Completed-state check from a previous tool call if @variables.order_status != "": | Report order status {!@variables.order_status}, then stop. transition to @subagent.confirmation | Collect the order number, then use {!@actions.lookup_order} once. ``` Before lookup, the LLM sees only: ```text Collect the order number, then use lookup_order once. ``` After the tool stores `order_status`, the instructions are rebuilt. The completed-state branch resolves and transitions without asking the LLM to call the tool again. --- ## 3. LLM Processing AgentScript guarantees the authored semantics, not an exact provider message count. Reasoning receives: - One authored node-instruction value: the subagent system override when present, otherwise the global `system.instructions`. Runtime base prompts and metadata may add other system content outside AgentScript. - Conversation history. - The resolved `reasoning.instructions`, when non-empty. - The currently available tools and their schemas. Do not document or test an exact four-message structure. Tool schemas are not necessarily encoded as a chat message, and message transport is not the AgentScript authoring contract. ### What the LLM Decides Based on the assembled prompt, the LLM: 1. **Selects an action** (if applicable) from the available actions list 2. **Fills slot parameters** (`...` values) from conversation context 3. **Generates a text response** to send to the user 4. **Decides whether to transition** (if a transition action is available and appropriate) ### What the LLM Does NOT See - Raw `if`/`else` blocks (already resolved before the iteration) - Structured AgentScript execution-block identity or direct variable-store access unless the target runtime explicitly provides it - `run` statements (already executed before the iteration) - `set` statements (already executed) - `available when` conditions (already evaluated -- hidden actions are simply absent) - `after_reasoning` blocks (run after the LLM, not shown to it) --- ## 4. Tool Loop and Re-Resolution After the LLM selects and executes a tool, the runtime begins another reasoning iteration and rebuilds `reasoning.instructions` from current state. ### Loop Sequence ```text 1. Resolve reasoning instructions 2. LLM reasons and selects a tool 3. Action executes -> outputs captured in variables 4. Re-resolve reasoning instructions with updated variables - Post-action checks at TOP of instructions fire - New data is injected into the prompt 5. LLM reasons again with updated context 6. Repeat until a transition occurs or the LLM produces a user response ``` Only after the LLM produces a user response does the reasoning loop end and `after_reasoning` run. ### Why Post-Action Checks Go at the TOP Place post-action checks at the TOP of `instructions: ->` so they fire immediately on re-resolution: ```agentscript reasoning: instructions: -> # POST-ACTION CHECK (at TOP -- fires on re-resolution) if @variables.order_cancelled == True: | Your order has been cancelled successfully. transition to @subagent.confirmation # These instructions are for the FIRST entry (before action runs) | I can help you cancel your order. | What is your order number? ``` If the check were at the BOTTOM, the LLM would see the "ask for order number" instructions again even after the cancellation succeeded, causing confusion. --- ## 5. Concise Reasoning Instructions Keep `reasoning.instructions` task-local. Include only information that can change the current tool choice or response: 1. Checks backed by trusted outcomes or material invariants. 2. Deterministic data loading required for this iteration. 3. The current objective and only the resolved state it consumes. 4. Action guidance, exclusions, and a stop condition. Do not add a variable or branch merely to remind the model what happened in surviving conversation history. Keep persona, tone, disclosure, safety, and broad scope rules in the effective system layer. Put tool-specific trigger details in action descriptions instead of repeating them in the reasoning instructions. ```agentscript reasoning: instructions: -> # Trusted verification output controls the protected capability. if @variables.is_verified == True: | Complete the requested account task using the available action once. if @variables.is_verified == False: | Ask for the minimum information needed to verify identity. Do not use account-changing actions. ``` --- ## 6. Common Instruction Patterns ### Pattern 1: Security Gate Prevent access to sensitive actions until identity is verified: ```agentscript reasoning: instructions: -> if @variables.is_verified == False: | You must verify your identity before I can help with account changes. | Please provide your email address. if @variables.is_verified == True: | Identity verified. I can now help with account changes. | What would you like to do? actions: update_account: @actions.update_account_info description: "Update account information" available when @variables.is_verified == True with field = ... with value = ... ``` The `available when` guard hides the action from the LLM until verification passes. The conditional instructions tell the user what to do. ### Pattern 2: Data-Dependent Instructions Load data first, then tailor instructions based on the result: ```agentscript reasoning: instructions: -> run @actions.get_account_status with account_id = @variables.account_id set @variables.account_status = @outputs.status set @variables.balance = @outputs.balance | Account status: {!@variables.account_status} | Current balance: {!@variables.balance} if @variables.account_status == "delinquent": | IMPORTANT: This account is delinquent. | Collect payment before processing any other requests. | Offer payment plan options if customer cannot pay in full. if @variables.account_status == "active": | This account is in good standing. | Process requests normally. ``` ### Pattern 3: Action Chaining Execute one action, then use its output to drive the next: ```agentscript reasoning: instructions: -> # Post-action check: case was created in previous loop if @variables.case_id != "": | Case {!@variables.case_id} has been created. run @actions.assign_case with case_id = @variables.case_id with priority = @variables.priority transition to @subagent.case_confirmation | I need to collect some information to create a support case. | What is the issue you're experiencing? ``` ### Pattern 4: Machine-Known Gate Use compound conditions when trusted machine state controls a material capability. Do not persist the user's current intent merely to route it; let the router infer current intent from the latest turn and conversation history. ```agentscript reasoning: instructions: -> if @variables.is_verified == True and @variables.account_locked == False: | Complete the requested account task using the available action. if @variables.is_verified == False: | Ask for the minimum information needed to verify identity. if @variables.account_locked == True: | Do not use account-changing actions. Explain how to unlock the account or escalate. ``` --- ## 7. Anti-Patterns to Avoid ### Anti-Pattern 1: Re-Explaining Language Syntax Keep this reference focused on instruction resolution. For supported `if / else if / else`, invalid `elif`, nested-condition limitations, and post-action conditionals, use the canonical [Conditional Control Flow Syntax](agent-script-core-language.md#conditional-control-flow-syntax) section. ### Anti-Pattern 2: Post-Action Check at Bottom ```agentscript # WRONG -- Check at bottom; LLM sees stale instructions on re-resolution reasoning: instructions: -> | What is your order number? if @variables.order_status != "": transition to @subagent.show_status # CORRECT -- Check at TOP reasoning: instructions: -> if @variables.order_status != "": transition to @subagent.show_status | What is your order number? ``` ### Anti-Pattern 3: Persona in Subagent Instructions ```text # WRONG -- Persona text duplicated in every subagent reasoning: instructions: | You are a friendly, professional customer service agent. Help the customer with their order. # CORRECT -- Persona in system instructions, subagent has operational instructions only system: instructions: | You are a friendly, professional customer service agent. subagent order_support: reasoning: instructions: -> | Help the customer check their order status. | Ask for the order number if not provided. ``` ### Anti-Pattern 4: Using `|` When `->` Is Needed ```agentscript # WRONG -- Using literal mode when conditionals are needed reasoning: instructions: | if @variables.is_verified == True: Show account details. # The above sends the literal text "if @variables.is_verified == True:" to the LLM! # CORRECT -- Use procedural mode for conditionals reasoning: instructions: -> if @variables.is_verified == True: | Show account details. ``` ### Anti-Pattern 5: Missing Variable Injection Syntax ```agentscript # WRONG -- Variable name as literal text reasoning: instructions: -> | Your order ID is @variables.order_id # CORRECT -- Use injection syntax reasoning: instructions: -> | Your order ID is {!@variables.order_id} ``` ### Pattern: `after_reasoning` Lifecycle Actions `run` is supported in `after_reasoning` through the common action mechanism. The block runs after the reasoning loop ends, including a turn where the model produced a response without calling a tool. Use it only when the follow-up must run after every completed reasoning pass. Do not use this lifecycle hook for an irreversible action merely because it is deterministic; consequential-action preconditions still need explicit guards. ```agentscript # Log every completed reasoning turn. after_reasoning: run @actions.log_event with event = "turn_completed" ``` ### Anti-Pattern 7: Prose-Based Conditional Logic ```agentscript # WRONG -- Conditional behavior described in prose; the LLM must interpret # these directives and may ignore, reorder, or misapply them reasoning: instructions: -> | If the user is a VIP, offer priority support. | If they haven't been verified, ask for verification first. | If the refund has been approved, confirm it and end the conversation. | Check availability before booking. # CORRECT when the conditions are trusted machine facts protecting material # invariants. The LLM sees only the matching operating instructions. reasoning: instructions: -> if @variables.refund_approved == True: | Your refund has been confirmed. Reference: {!@variables.refund_id} transition to @subagent.confirmation if @variables.customer_verified == False: | I need to verify your identity before proceeding. | Please provide your email address. if @variables.customer_tier == "vip": | As a VIP customer, you have access to priority support. | How can I help you today? ``` Why this matters: machine-known authorization, confirmation, action-result, and external-ordering conditions must not depend on model discretion. AgentScript resolves those branches before the model sees the prompt. Do not manufacture state merely so conversational judgment can become an `if/else`. Use a deterministic branch only when a named runtime consumer and material cause justify it. Reserve prose for current intent, tone, phrasing, and other decisions the model should interpret from the conversation. --- ## 8. Syntax Patterns Reference ### Literal Mode (`|`) Static text passed directly to the LLM. No evaluation occurs: ```agentscript instructions: | Help the customer with their order. Be professional and concise. ``` Or with the `|` prefix on each line (inside procedural mode): ```agentscript instructions: -> | Help the customer with their order. | Be professional and concise. ``` ### Procedural Mode (`->`) Enables conditionals, variable injection, and deterministic actions: ```agentscript instructions: -> if @variables.condition == True: | Text shown when condition is true. else: | Text shown when condition is false. ``` ### Variable Injection ```agentscript | Your order {!@variables.order_id} is {!@variables.status}. ``` ### Deterministic Run ```agentscript run @actions.load_data with param = @variables.value set @variables.result = @outputs.field ``` ### Deterministic Set ```agentscript set @variables.counter = @variables.counter + 1 ``` ### Deterministic Transition ```agentscript transition to @subagent.next_subagent ``` ### Conditional Transition ```agentscript if @variables.all_collected == True: transition to @subagent.confirmation ``` --- ## 9. Programmatic Trace Access To verify how instructions were resolved at runtime, use the trace files generated by `sf agent preview`. ### Trace File Location ```text .sfdx/agents/{BundleName}/sessions/{sessionId}/traces/{planId}.json ``` ### Reading Instruction Resolution from Traces ```bash # Extract the resolved instructions that the LLM received jq -r '.planTrace.steps[] | select(.type == "LLM_STEP") | .input' \ ~/.sf/sfdx/agents/MyAgent/sessions/*/traces/*.json # Extract the LLM's response jq -r '.planTrace.steps[] | select(.type == "LLM_STEP") | .output' \ ~/.sf/sfdx/agents/MyAgent/sessions/*/traces/*.json # Check which variables were set during resolution jq -r '.planTrace.steps[] | select(.type == "ACTION_STEP") | {name: .name, pre: .preVars, post: .postVars}' \ ~/.sf/sfdx/agents/MyAgent/sessions/*/traces/*.json ``` ### Verifying Per-Iteration Resolution To confirm that `if`/`else` blocks resolved correctly, compare the trace's `LLM_STEP` input against your `instructions: ->` block. The LLM input should contain only the branches that matched, with all `{!@variables.X}` tokens replaced with actual values. If the trace shows unexpected instruction text: 1. Check that you used `->` mode (not `|` mode) when conditionals are present 2. Verify variable values at the time of resolution (check `preVars` on preceding `ACTION_STEP`) 3. Confirm that `if` conditions use the correct comparison operators ### Using STDM for Production Trace Analysis For production agents, use the Session Trace Data Model (STDM) in Data Cloud to access trace data programmatically. The STDM captures `LLM_STEP` records with `input` and `output` fields that contain the resolved prompt and LLM response. This is useful for auditing instruction resolution at scale across hundreds of live sessions. --- ## 10. Resolution Across Subagent Transitions When a subagent transition occurs (via `@utils.transition to @subagent.X` or `transition to @subagent.X`), instruction resolution starts fresh in the new subagent: 1. The current subagent's remaining instructions are NOT processed 2. The new subagent's `before_reasoning:` runs (if present) 3. The new subagent's `reasoning: instructions:` resolves for its first iteration 4. The LLM receives the new subagent's assembled prompt **Important**: Variables persist across transitions. A variable set in Subagent A is available in Subagent B. This is how you pass data between subagents: ```agentscript # Subagent A: Collect data subagent collect_info: reasoning: instructions: -> | Please provide your order number. actions: capture_order: @actions.get_order_id with input = ... set @variables.order_id = @outputs.order_id after_reasoning: if @variables.order_id != "": transition to @subagent.process_order # Subagent B: Use the data subagent process_order: reasoning: instructions: -> # order_id is available from Subagent A | Processing order {!@variables.order_id}... run @actions.get_order_details with order_id = @variables.order_id set @variables.order_status = @outputs.status ```