> ## Documentation Index
> Fetch the complete documentation index at: https://docs.asteroid.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Agent Node

> Instruct an LLM to interact with a webpage

The **Agent node** uses natural language instructions to perform actions inside a real browser.
It is the most flexible node type and is ideal for handling dynamic UI, multi-step interactions, and workflows that benefit from LLM reasoning.

Common actions include:

<CardGroup cols={2}>
  <Card title="Navigate" icon="compass" horizontal>
    Navigate to and through pages
  </Card>

  <Card title="Fill forms" icon="list" horizontal>
    Enter and submit structured data
  </Card>

  <Card title="Use files" icon="file" horizontal>
    Read, upload, or download files
  </Card>

  <Card title="Extract data" icon="chart-line" horizontal>
    Scrape data from a webpage
  </Card>
</CardGroup>

***

## Configuration

### Instructions

The core of an Agent node is a **natural language instruction block**.
You describe what the agent should do, and the LLM executes those steps in the browser.

#### Recommended Structure

To achieve the most reliable behavior, structure your instructions with:

* **Goal**: What the node is trying to accomplish
* **Ordered steps**: A sequence of specific actions
* **Edge cases**: Known variations the agent must handle
* **Success criteria**: Observable conditions indicating completion

Example:

```
Your goal is to submit the consultation form for the current patient:
1.	Click “New Consultation”
2.	Fill the form fields with the provided data
3.	Upload any provided attachments
4.	Click “Create”

Edge cases:
- If a modal appears, close it before continuing
- If the page asks for confirmation, accept it

Success criteria:
Once you see the “Consultation Submitted” banner, the task is complete.
```

### Dynamic Variables

Dynamic variables make your instructions reusable across multiple runs with different inputs.
Variables are passed at execution time and referenced with the `{{.var_name}}` syntax.

Example:

```
Navigate to the patient search page:
1.	Enter {{.patient_name}} into the search bar
2.	Select the matching result
3.	Fill {{.diagnosis}}
4.	Click Submit
...
```

#### Naming Rules

Variable names must:

* Start with a letter or underscore
* Contain only letters, numbers, and underscores
* Not include special characters such as `@`, `-`, `.`, or spaces

Valid: `{{.user_name}}`, `{{.email}}`, `{{.api_key}}`

Invalid: `{{.user-name}}`, `{{.my@var}}`, `{{.1st_item}}`

### Advanced Templating

Agent instructions support full **Go Template** syntax, enabling conditionals, loops, and structured data.

<AccordionGroup>
  <Accordion title="Advanced: Conditionals">
    Use `if` statements to conditionally adjust behavior:

    ```
    {{if .is_premium}}
    Navigate to the premium dashboard and enable all features.
    {{else}}
    Navigate to the free dashboard.
    {{end}}
    ```

    The corresponding variable values should be:

    ```json theme={null}
    {"is_premium": true}
    ```
  </Accordion>

  <Accordion title="Advanced: Loops">
    Iterate through lists or arrays.

    **Simple array:**

    ```
    Fill the form with the following items:
    {{range .items}}
    - Enter "{{.}}" in the next field
    {{end}}
    ```

    **Array of objects:**

    ```
    Add products to the cart:
    {{range .products}}
    - {{.name}}: {{.price}}
    {{end}}
    ```

    The corresponding variable values should be:

    ```json theme={null}
    {"items": ["Item 1", "Item 2", "Item 3"]}
    ```

    Via SDK, you'd pass:

    ```json theme={null}
    {
      "inputs": {
        "products": [
          {"name": "Widget A", "price": "$10"},
          {"name": "Widget B", "price": "$20"}
        ]
      }
    }
    ```

    <Info>
      Inside a `range`, `{{.}}` refers to the current item.
      For objects, use fields like `{{.name}}` and `{{.price}}`.
    </Info>
  </Accordion>

  <Accordion title="Advanced: Nested Data">
    Access nested JSON structures:

    ```
    User: {{.user.name}} ({{.user.email}})

    {{if .user.verified}}
    Proceed to checkout.
    {{else}}
    Please verify your email before continuing.
    {{end}}
    ```

    The corresponding variable values should be:

    ```json theme={null}
    {"user": {"name": "Jane Smith", "email": "jane@example.com", "verified": true}}
    ```
  </Accordion>
</AccordionGroup>

## Model Selection

Choose the model that best fits the complexity of the task:

<Card title="Asteroid Fast" icon="zap" horizontal>
  Lowest latency. Best for simple, fast interactions.
</Card>

<Card title="Asteroid Balanced" icon="lightbulb" horizontal>
  Default model. Balanced speed and accuracy.
</Card>

<Card title="Asteroid Max" icon="brain" horizontal>
  Highest intelligence and reasoning depth for complex flows.
</Card>

## Script

By default an Agent node runs **fully agentically** — the model picks tool calls turn by turn using the `Instructions` below. To make a node run a pre-approved Playwright script first, set its **Script** field. The presence of a script will make the node attempt to run the script before any LLM involvement.

In the graph editor, the **Script** field sits at the top of the node's **Instructions** tab. Selecting a `.js` file from the node's shared directory turns the node into a scripted node and reveals a small "If the script fails" picker beside it. Removing the script returns the node to fully agentic behaviour.

In API or YAML exports, the same switch is the `script_filepath` field on the Agent node — a node is scripted iff `script_filepath` is set.

<Card title="Agent filesystem" icon="folder-open" href="/fundamentals/files" horizontal>
  Read how `shared/` keeps files across runs for the same agent
</Card>

### If the script fails

When a script is selected, the node exposes a failure-behaviour picker.

<CardGroup cols={2}>
  <Card title="Fall back to AI" icon="sparkles" horizontal>
    The script's output (or failure context) is attached to the LLM turn and the AI agent recovers and continues the task using the `Instructions`.
  </Card>

  <Card title="Cancel" icon="ban" horizontal>
    The execution is canceled immediately with reason `script_failed`. No LLM fallback. Also fires when the referenced script is missing on disk.
  </Card>
</CardGroup>

<Tip>
  Use [Astro](/astro/getting-started) to recommend areas of the workflows that can be scripted, and to write and test them for you.
</Tip>

### Where scripts live

Scripts live under the agent's persistent **`shared/`** tree (see [Agent filesystem](/fundamentals/files)). The runtime resolves `script_filepath` **relative to the node's own shared directory**:

```
shared/<node-slug>/<script_filepath>
```

* `<node-slug>` is derived from the node's display name (lowercased, punctuation stripped) and is added automatically by the runtime — authors never encode it, so renaming a node never breaks the path.
* `script_filepath` itself is the part you author, and is stored in its canonical `./`-prefixed form. Typical values: `./scripts/find_patient.js`, `./scripts/login.js`, `./scripts/submit_order.js`.
* Name the file after the action it performs in `lower_snake_case`. The directory already groups by node — the filename should communicate *what* the script does. Avoid generic names like `scripts/main.js` or `scripts/index.js`.

For example, a node named "Find Patient" with `script_filepath: scripts/find_patient.js` resolves on disk in the sandbox to `/home/agent/shared/find_patient/scripts/find_patient.js`.

### Passing data into the script

Data that changes from run to run reaches the script as **`args`**. Declare an input schema on the node, then use the destructured signature and read each input by name:

```javascript theme={null}
/**
 * @param {object} args
 * @param {string} args.patient_name - Patient to file the note against.
 * @param {string} args.date_of_birth - Date of birth, as shown in the EHR.
 */
module.exports = async ({ page, args }) => {
  await page.fill('#username', '##USERNAME##');
  await page.fill('#password', '##PASSWORD##');
  await page.click('button[type="submit"]');

  await page.fill('#patient-name', args.patient_name);
  await page.fill('#dob', args.date_of_birth);
  await page.click('#save');

  return { saved: true };
};
```

A scripted node that declares an input schema must use the `async ({ page, args }) => { ... }` signature — the plain `async (page) => { ... }` form fails validation.

<Tip>
  Document every argument with a JSDoc `@param` block, as above. If the script fails and the node falls back to the agent, that JSDoc is the only description of the arguments the agent can see — it does not get the input schema.
</Tip>

Secrets are separate: **`##CREDENTIAL##`** tokens are substituted at the tool boundary straight from the credential store and never pass through the LLM. See [Agent profiles](/fundamentals/profiles).

### Runtime behaviour

The stored script runs against the live browser session (the same browser the LLM would have used). Then:

* If a **selector transition** matches immediately after the script (eagerly checked), the runtime takes it and skips the LLM. Fast.
* If the script **succeeded** and the node has **exactly one** outbound transition, the runtime takes it directly. No LLM. Fast.
* Otherwise, the runtime invokes the LLM with the script's output added as context — the agent decides what to do next (pick among multiple transitions, recover from a partial failure, etc).

Scripts return values from their `module.exports`:

* **Object** (e.g. `return { patient_id: id }`) — each top-level key becomes an output variable available to downstream nodes' `# Execution Data`.
* **String** — wrapped under a single `script_output` variable.
* **Throw** — treated as a failure; behaviour then depends on `script_failure_action`.

## Capabilities

Every Agent node declares three capabilities that decide what it is allowed to do:

<Card title="Browser use" icon="globe" href="/fundamentals/ai-capabilities#browser-use" horizontal>
  Navigate and interact with web pages
</Card>

<Card title="Computer use" icon="desktop" href="/fundamentals/ai-capabilities#computer-use" horizontal>
  Vision-based mouse and keyboard control of a full desktop
</Card>

<Card title="Ask user question" icon="comment" href="/fundamentals/ai-capabilities#ask-user-question" horizontal>
  Pause mid-run and ask the user for input
</Card>

<Warning>
  **Every Agent node needs a way to act**

  An Agent node's capabilities are gated by the workflow's environment.

  * On a **browser** environment, `browser_use` is enabled by default and `computer_use` is off.
  * On a **Linux or Windows** environment, `computer_use` is always on and `browser_use` is always off. Enabling `browser_use` there has no effect.

  For more details, see [Capabilities](/fundamentals/ai-capabilities).
</Warning>

***

## Transitions and Failure Handling

<Warning>
  **Critical: Agent Nodes Must Have Failure Paths**

  **All Agent nodes must have a connection to an Output node via a failure path.** This is essential for proper error handling and workflow completion.

  * Every Agent node should have at least one transition (typically an AI Transition) that connects to an Output node configured for failure scenarios
  * Without a failure path, your workflow may not properly handle errors, unexpected conditions, or edge cases
  * This ensures that unexpected conditions, missing elements, or interpretation errors are surfaced correctly in your workflow

  For more details, see [Transitions](/fundamentals/transitions#failure-transitions) and [Output Nodes](/fundamentals/nodes/output).
</Warning>

***

## YAML Reference

In a workflow's `settings.yaml`, an Agent node is `type: agent`, and its transitions are `type: ai` or `type: selector`.

Every Agent node must declare `type`, `capabilities`, and `model`.

| Field                   | Value                                                                                                                                        |
| ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
| `capabilities`          | Required. The `browser_use`, `computer_use`, and `ask_user_question` booleans. See [Capabilities](/fundamentals/ai-capabilities).            |
| `model`                 | Required. One of `asteroid-fast`, `asteroid-balanced`, or `asteroid-max`.                                                                    |
| `script_filepath`       | Optional. A relative `.js` path inside the node's own shared directory that turns the node into a fast-path scripted node (see rules below). |
| `script_failure_action` | Optional. `fallback_to_ai` (the default) or `cancel_execution`. Only meaningful alongside `script_filepath`.                                 |

Default Agent node (`settings.yaml`):

```yaml theme={null}
label: "Submit Form"
type: agent
capabilities:
  browser_use: true
  computer_use: false
  ask_user_question: true
model: asteroid-balanced
transitions:
  - to: success_output
    type: ai
  - to: failure_output
    type: ai
  - to: confirmation_page
    type: selector
    name: "Confirmation Visible"
    selectors:
      - ".confirmation-banner"
```

Scripted Agent node with LLM fallback (script failure → the agent recovers):

```yaml theme={null}
label: "Find Patient"
type: agent
capabilities:
  browser_use: true
  computer_use: false
  ask_user_question: true
model: asteroid-balanced
script_filepath: ./scripts/find_patient.js
transitions:
  - to: patient_found
    type: ai
  - to: not_found
    type: ai
```

Scripted Agent node with no fallback (script failure cancels the execution):

```yaml theme={null}
label: "Login"
type: agent
capabilities:
  browser_use: true
  computer_use: false
  ask_user_question: true
model: asteroid-balanced
script_filepath: ./scripts/login.js
script_failure_action: cancel_execution
transitions:
  - to: dashboard
    type: ai
```

The node's instructions are stored in a separate `instructions.md` file in the same directory. The Playwright script referenced by `script_filepath` lives in the agent's shared directory at `shared/<node-slug>/<script_filepath>` and is shipped to the sandbox at execution time.

***

## Additional Settings

### Snapshot Compression

Reduce context size by compressing browser snapshots captured during execution.
