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

# Flows editor

> The Babelfish Flows page in the Studio: the flow browser, the node edit panel, the LLM router and its dispatch modes, the visual jq editor, and per-node state bindings.

The **Babelfish Flows** page is the flow engine's own editor in the Studio, contributed
by the [Babelfish Flows](/babelfish) plugin (see [the Studio screens](/studio/screens#flows)
for how a plugin contributes a page). The page is a browser for your saved flows that mirrors
the Tools screen's organization: it navigates
[folders](/concepts/tool-organization) with a breadcrumb, toggles between a list
and a card view (the choice persists per browser), and filters the list with a tag
filter and a search box. Each flow row edits its display name, tags, and folder in
place; hide management stays centralized on the Tools screen. The list carries an
**Attachments** column — one badge per kind of thing that references the flow (a
schedule, a hook, a route, an extension, or a bound state) — reserved at a fixed width from
first paint across its loading, empty (a muted em dash), and populated (the kind chips) states,
so a resolved attachment index never re-lays out the list. The whole view — the
open flow, the current folder, dialogs, and the active filters — is derived from
the URL, so a flow is bookmarkable and the back button works; opening a link to a
flow that no longer exists renders a loud not-found card, never a blank editor.

## The node edit panel

Selecting a node on the canvas opens a **tabbed edit panel** — **Settings**,
**Input**, **Output**, **State**. Settings is the per-kind configuration form and
its Update-node action; a fan-in or loop node's Settings also carry the barrier's
[wait knobs](/babelfish#node-waits) (wait mode and the timeout give-up pair) and,
where the flow binds states, the opt-in
[state-refresh](/babelfish#refresh-opt-in-state-re-read) toggle. Input and Output are
read-only: they render the node's
declared input and output schemas — with the node's outcomes surfaced on Output —
so a node's contract is readable in build mode, and each shows an empty state when
nothing is declared. A scheduled node's Settings also carry an optional [**run
condition**](/babelfish#run-condition) — a jq predicate that gates whether the node
runs at all. A false condition *skips* the node: it does not execute, its output is
null, its status reads **skipped** in Run mode, and the flow continues past it (the
gate stops the one node, not the path). The field is absent on a node that runs only as
an on-error handler, since such a node is dispatched directly and never scheduled. When
an agent node carries a [structured-output
schema](/babelfish#structured-agent-output), the Output tab renders that schema live
and its label reads **Output (structured)**. Editing that schema to **remove** a field
surfaces a non-blocking honesty note on the agent's Settings: static analysis scans the
flow for downstream jq that still reads the removed path off this agent
(`.upstream_outputs.<agent>.<path>`, including the flow-level on-error handler) and
names the references that would silently start resolving to null — it never blocks the
save (references built dynamically are, honestly, not detected). The State tab is the
node's [state binding](#state-bindings) — the shared binding editor, one per node — and
its tab label carries a live count of the states the node binds.

## Schema-aware run results

When you run a flow in-page, the run panel and each node's run popup render results
**schema-aware**. Where a node declared its output shape — an agent's structured-output
schema, or a preset's (or the whole flow's) output schema — the actual result
renders as a **labeled, typed display** keyed off that schema: the schema's titles as
field labels, its descriptions as captions, enum values as chips, nested objects as
sections, and a declared array as a list of its typed items. The view stays honest about the data: value keys the schema does **not**
declare are never hidden — they render as an appended raw-JSON remainder — a declared
field absent from the value shows a muted dash, and a value that does not match the
schema's shape at the top level falls back to the plain JSON tree with a small note
rather than a crash. A per-instance **Raw** toggle flips the whole view to the plain
tree, so the underlying data is never more than one click away, and a flow (or node)
that declares no output shape keeps the plain results tree throughout.

## The LLM router card

The [**LLM router**](/babelfish#routing-with-an-llm) card carries its own edit surface:
the **kinds** it classifies into, optional **turn-level output fields** that describe
the whole classified turn (each a top-level output beside the kinds), the author's
system prompt, a **routing** mode — every match (the default, firing every matching
route) or first match (the first matching route wins) — and a **routes table** listing
every wired route with its flavor, condition, guard, and target. A route's target is
the edge drawn from its chip on the card, so the table shows it rather than picking it.
Under first match the table also orders the rows — the row number is the evaluation
priority — and a row reorders by [dragging its grip or stepping its move
buttons](/babelfish#the-routes-table); the table's advisory notes flag a row an earlier
one already covers, without ever blocking the save.

The card also carries one **Dispatch** control, set once for the whole card — **Once**
(every matched chip continues the flow along its line) or **Loop** (each chip's line starts
a body that runs once per classified item, sequentially or in parallel with an optional
max-concurrency cap, or repeatedly while an authored jq condition holds under a max-runs
safety cap). Under Loop the card grows a **result** chip through which the collected output
of every chip leaves. The chips look and connect the same in both modes; the modes are
documented on the [router page](/babelfish#dispatch-once-or-loop).

## The updates half of the binding editor

A node's [state binding](#state-bindings) applies **updates** to a bound state after the
node runs, edited in the **Updates** list of the shared binding editor. Each update writes
one of two ways:

* a **template jq** the state's attached templates declare (`purpose: "update"`), picked
  from a select that lists each update program as `name — description`; the selected
  program's declared write paths show beside it as a `writes:` badge. The run applies its
  returned op batch — [keyed list ops](/babelfish#keyed-state-list-ops) and appends — under
  the attachment path. Because a named update jq never sees a tool's field names, an **adapter**
  form maps the node's `{output, input}` to the update jq's declared input — one row per
  declared key, each picking a field, a literal, or a jq, with a write-jq escape hatch for
  the whole adapter.
* a **custom jq** over `{record, output, input}` returning the op batch itself, for a write
  the templates do not name.

An update may carry an optional **op id** for idempotency. The write-regime check runs at
save: a landing path outside the update's declared writes, or an adapter whose output misses
the declared input, is refused loudly. A save that writes a path another flow also writes
**succeeds** and surfaces a quiet, non-alarming note; a genuine write failure still renders
loudly. With the states store not configured, the binding surfaces self-hide rather than
erroring.

## Presets

The palette's **Presets** section manages a
[preset](/babelfish#presets) as a node: an edit control opens its flow
editor, a **+ New preset** action starts a fresh one, and a delete control
raises a guarded confirm that states the blast radius — the node leaves the palette
and any flow already embedding it keeps a dangling reference. When a node edit
removes an outcome value that wired callers route on, the make-a-node dialog shows an
**informational** warning naming the removed values and how many flows use the node
(their routes would go dead) — it never blocks the save. This inform-don't-block
convention is the editor's house style for changes whose impact lands on callers.

The one thing the make-a-node dialog **does** block on is a genuinely *invalid*
input-schema edit. The make-a-node dialog's input-schema editor nulls its buffer while
an edit is unparseable, and saving then would silently **erase** the schema — the right
outcome for a deliberate clear, the wrong one for a typo. So Save is disabled precisely
while the input-schema buffer is invalid (a bad shape, not an empty one), with an inline
"fix the input schema before saving" note; a cleared buffer reports valid and saves a
null schema as intended. This is a validity gate, distinct from the non-blocking
caller-impact warnings above.

## Message and expression slots

A **message** slot on the agent form is edited as a **Plain / Template** tabbed
control: Plain is a raw message textarea, and Template picks a stored
[template](/concepts/storage-and-resources) with a deep link to the file and a
collapsed preview of its first lines, editing its kwargs through a schema-assisted
form over the authoritative JSON. A jq **expression** slot — on the conditional,
branch, runnable, on-error, and agent forms — is instead an **Inline / Template**
control: the Inline tab is the [visual jq editor](/babelfish#the-visual-jq-editor)'s
field (below), and Template is the same picker. In both, the inline value and the
template are mutually exclusive — the wire carries one, and switching between them
clears the mode being left after a confirm when it holds data. A legacy node that
carries **both** an inline message and a template opens on the Template tab with a
rescue note and a one-click "clear inline text" action, so the exactly-one-of rule is
fixable in place. A node with a return
result may carry a **return expression** (jq) that shapes the value it contributes
to the flow result, and a conditional's branch may be marked **pass-through** to
inject the conditional's single upstream output verbatim — which excludes a
post-route expression and a return. Settings the form does not model — kwargs an
API-authored or newer agent carries — are surfaced **read-only** so nothing an agent
persists is fully invisible, and are round-tripped untouched.

A jq field that can reach a bound state — a binding field, a node expression — offers
autocomplete from the [template jq](/babelfish#state-templates) of the templates attached
to that state: each `purpose: "input"` program by name, inserting its `tjq_<name>({…})`
call handle (`tjq_<template>__<name>({…})` when a name is shared across the state's
attachments).

## The visual jq editor

The **Inline** tab's field is the [visual jq editor](/babelfish#the-visual-jq-editor):
a jq text control with a **Visual editor** button that opens the canvas in a dialog,
where the expression is drawn as a small, editable node graph and written back as jq.
The dialog wraps a **Test** panel (run the jq against a sample seeded to the field's
input shape, plus a server **Validate** where the engine exposes one), a **live
readout** of the jq the graph currently serialises to, an **error summary** that jumps
to each problem, and a **Legend** of the node kinds and wire notations.
The [node vocabulary and its semantic hues](/babelfish#the-visual-jq-editor) — Input,
Value, Operator, Condition, Try/Catch, Call Function, Define Function, Comment — are
documented with the feature.

The editor draws a graph **only when it can prove that reading it back gives exactly
your expression**: a WASM oracle round-trips the graph and adopts it only when the two
behave identically. Otherwise the fallback is non-destructive — an expression the
editor cannot draw, or one whose graph would read back differently, keeps your text
verbatim and shows it as text with a neutral **"not shown here"** notice; nothing is
altered unless you explicitly **Start empty**. This keeps two questions apart — *is
the jq valid* and *can the editor draw it* — so only jq that genuinely fails to
compile raises the loud **"invalid expression"** alert, and valid jq the editor cannot
represent is never mislabelled as broken. On opening the dialog, a faithful expression
is drawn on the canvas; an unfaithful one shows the fallback, and the editable canvas
is then reached only through **Start empty**.

## State bindings

Flows bind to the platform [state store](/concepts/states) rather than a store of
their own, so declaring states, attaching templates, and browsing records all live on
the Studio's [States screen](/studio/screens#states). What the flow editor owns is the
binding: how each node reads and writes those states. Every node's edit panel carries a
**State** tab, and its binding is the [same editor](/reference/studio-sdk/index) the
platform's door screens use — shipped from the Studio SDK so a door and a node bind a
state the same way.

The editor attaches one or more states to the node. Each **state** card picks the
state and one or more **templates** (a template not yet attached is attached on save),
then a **Subject** expression (a full subject object or a bare key) and an optional **Scope**
predicate that gates the state for the run —
[the same on every door and node](/concepts/states#a-bindings-subject-and-scope).
Below the card, the **Inputs** list injects a value into the node's input before it runs —
each row a `purpose: "input"` [template jq](/babelfish#state-templates) (or a custom jq
over `{record, input}`) and the input field it lands in — and the **Updates** list applies
a write after the node runs (the [updates half](#the-updates-half-of-the-binding-editor)
above). Every jq field autocompletes from the attached templates' template jq.

Where the node's tool is a registered preset that carries its own door binding, the editor
shows an advisory on any state both bind — *Overrides the preset's subject* — with the
preset's own subject shown as the read-only default beside it; the node's binding wins.
Every control rebuilds from the stored binding: an update's adapter form recovers each row
from the stored adapter jq, and a state or template the binding names but the server no
longer serves shows a **Template not attached.** error with the raw jq, never a blank
control. A refused save renders the engine's message verbatim.

## Upload

The flows toolbar carries an **Upload** action for a flow or node document with a `kind`,
reporting the uploaded kind and name in a status line and prompting when a file carries
no `kind`. State and state-template documents are uploaded from the
[States screen](/studio/screens#states) instead.

<Frame caption="The flows toolbar Upload action with its result line.">
  <img className="block dark:hidden" src="https://mintcdn.com/tai42/VvoOTVOLg1GbZpoY/images/studio/flows-toolbar-upload-light.png?fit=max&auto=format&n=VvoOTVOLg1GbZpoY&q=85&s=c318f1655b76533b10e3c5c8141a61c2" alt="The flows toolbar Upload action reporting the uploaded document's kind and name." width="1440" height="900" data-path="images/studio/flows-toolbar-upload-light.png" />

  <img className="hidden dark:block" src="https://mintcdn.com/tai42/VvoOTVOLg1GbZpoY/images/studio/flows-toolbar-upload-dark.png?fit=max&auto=format&n=VvoOTVOLg1GbZpoY&q=85&s=473269b39b7c7089efb011ea4aeebd49" alt="The flows toolbar Upload action reporting the uploaded document's kind and name." width="1440" height="900" data-path="images/studio/flows-toolbar-upload-dark.png" />
</Frame>
