Skip to main content
Give the node a Script and it runs a Playwright script first, before any model call. The same clicks happen every execution, in seconds, for no model cost.

Let Astro script it

Ask Astro which nodes are suitable to script. It writes the script from what the workflow already did, attaches it to the node, and runs it.
For all of the details about how scripts work and run, see the Script runtime section.

Why script a node?

An agent node normally calls an AI model at each step to decide what to do. The model reads the page and picks each action from your instructions. That is useful when the page is very different each time, regularly changes or requires AI decision making. It is often waste to always use an agent on a page that doesn’t have these characteristics. In that case, you should convert the node into a script.

When should I script a node?

Most nodes should eventually be scripted if you’re working on a website, rather than a Windows or Linux desktop. To start with, it’s convenient to have a node be agentic. This way, the agent can interact with the website for a few executions and you can find failure cases and improve your instructions. After a few successful runs, it’s a good time to start scripting the node. Astro is able to do this for you, or you can do it from your coding agent. Astro or your agent might look at the previous executions or spin up a live environment in order to view the website in real time to understand how to script it effectively. Once the scripts are written, ensure you run test runs with realistic data in order to identify failure modes of the new script.
Time saving tip: a script can route itself to the next node. Use AI to do the routing only when deciding where to go next requires some intelligence and reasoning.

How to add a script

Open the node’s Instructions tab. The Script field sits at the top. Select a .js file from the node’s shared directory. The node becomes a scripted node, and an “If the script fails” picker appears beside the field. Remove the script and the node goes back to running turn by turn agentically. Astro does the same thing from the chat. Ask it to script a node and it writes the file, sets the field, and picks a failure action. Nodes covers where a script file lives and how the runtime resolves its path.

What happens when the script runs

The script runs against the live browser session. After the script finishes, the runtime picks the next node in this order. The first three paths skip the model. Rows 1 and 3 are automatic. Row 2 is the one you drive from inside the script, and it is the only path that lets a script choose between several branches. See Route to the next node yourself.

What the script returns


Route to the next node yourself

A node with one outbound transition takes it automatically (row 3 above). A node that branches has more than one, so the runtime cannot guess. Return a handoff and the script names the branch itself. Call require('asteroid').handoff(...) and return the result.
Here the node has two AI transitions, to a node named “Dashboard” and a node named “Login Failed”. The script reads the page and picks one. No model runs.

The target slug

to is the slug of the target node’s display name. The slug is the name in lowercase, with every run of non-letter, non-digit characters turned into a single _, and any leading or trailing _ removed. A handoff to a name that is not a live target fails the script. The error lists the valid targets, so a wrong slug is easy to fix.
Handoff targets are the node’s AI transitions only. You never hand off to a selector transition. A selector is matched automatically, at higher precedence than a handoff (row 1 above).

Carrying data on the handoff

Pass either output or variables, never both.
  • Use output when the transition has a schema. The object is validated against that schema and arrives at the next node as {{.output}}, exactly as an AI transition’s payload would.
  • Use variables (an array of { name, value }) when the transition has no schema. Each entry becomes a named output variable.
Watch out for these mistakes:
  • Building a handoff but not returning it. Always return asteroid.handoff(...).
  • An output that does not match the transition’s schema, or a value that is not JSON. Pass plain data, never a Playwright locator or element handle.

What happens when the script fails

The picker beside the Script field has two settings.

Fall back to AI

The failure context joins the model’s turn. The workflow recovers and finishes the task from the instructions.

Cancel

The execution cancels at once with the reason script_failed. No model call. This also fires when the script file is missing.
Choose Fall back to AI while a script is new and you’re monitoring it closely, or where the stakes are low. Choose Cancel in situations where an AI making a mistake is not an acceptable outcome.

Pass data into a script

Data that changes per execution reaches the script as args. Declare an input schema on the node, then read each input by name.
A scripted node that declares an input schema must use the async ({ page, args }) => { ... } signature. The plain async (page) => { ... } form fails validation. Tell Astro which values change per execution and it declares the schema and wires the args for you.
Write a JSDoc @param block for every argument. The workflow sees that JSDoc when a script fails and the node falls back. It is the only description of the arguments the workflow gets.
By default, each args value comes from an exact-name match against the workflow inputs and the outputs of upstream nodes. When nothing matches a name, the model composes that value. The next section pins a value to an exact source instead, so no model call is needed.

Pin a script’s inputs

Add x-source to a property in the node’s input schema to say exactly where its value comes from. A pinned value is filled with no model turn, so a script that reads only pinned inputs runs model-free from end to end.
Three forms are allowed. A few rules around pins:
  • The property’s own name is the source name. To pin patient_id, there must be a workflow input, or an upstream output, called patient_id. You cannot rename across the pin.
  • node may be a list of nodes. When several have emitted the same name, the most recent one wins.
  • In a workflow’s files, node is the referenced node’s directory name. In the API and MCP graph form, it is that node’s id. This is the same reference you already use to point a transition at a node.
  • If a pin has no value at run time, that property falls back to the model. The pin never blocks the run.
  • A schema-shaped handoff output is stored as one output variable. You can also pin to any of its top-level fields by their own name.

Inputs and outputs

How inputs reach a node and how outputs travel between nodes

Credentials in a script

##CREDENTIAL## tokens are replaced at the tool boundary, straight from the credential store. They never pass through the model.
The token name is the credential key in upper case, wrapped in ##. Store the values on the Agent profile. See Credentials for the full model.

Patterns for coding agents

These recipes combine handoffs, pinned inputs, and the workflow filesystem into whole paths that need no model.

The filesystem as loop memory

A node can loop back to itself to work through a list one item at a time. That keeps each pass’s context small, which matters on a long list. Record finished items in workspace/ so a loop-back never redoes one. If the session drops and the run re-logs in, the same run picks up where it left off. workspace/ is scratch for the current execution. It survives every step and loop-back inside that run, then is cleared when the run ends. So this is within-run memory only: a fresh execution starts the list over. (Do not use shared/ for this: a running script’s writes to shared/ are not saved back. See the Script runtime.)

Verify against a file before you submit

Read a reference file the caller staged in uploads/. Check the page against it. Submit only on a match, and route to review on a mismatch. The guardrail is deterministic, so it costs nothing and never drifts.

A document that arrives as a PDF or a print-only page

The sandbox has no PDF library, so you cannot parse a PDF with a Node module. Parse it inside the browser page instead. Keep a pdf.js build in shared/, inject it with page.addScriptTag, then read the text in the page context.

Script runtime

The runtime contract: timeouts, the sandbox, the filesystem, require, and credentials

Next

Script runtime

Timeouts, sandbox limits, and the runtime contract

Improve your workflows

The rest of the ways to make a workflow faster

Nodes

Where a script lives, and the rest of a node’s settings

Agent profiles

Store the credentials a script fills in