Skip to content

Visual editor

Fork a Loop and edit its body on a canvas — nodes, edges, a per-node inspector, an inline linter, and a Graph/DSL toggle over one canonical definition.

For people running agent work18 pages in this section

The visual editor is a fork-and-edit surface — there is no blank-canvas builder. You fork an existing read-only or workspace Loop and edit its body: add nodes, wire edges, configure each node in an inspector, and publish. It is one of three surfaces over the same canonical definition; the file and the API edit the exact same document.

The three regions

  • Node palette (left), grouped by class:
    • Action — the three reserved kinds run-agent, run-loop, transform, a curated shortlist of common tools (including a Channel post shortcut that inserts a pre-filled compozy__network_send node), and a searchable "Call tool…" picker over the whole tool registry.
    • Controlfan-out, collect, branch, gate, sub-loop, wait, ask, route.
    • Sourcewatch-source, file-import, input.
  • Canvas (center) — positioned node cards and edges on a dot grid. A selected node gets an accent ring; a node with an error gets a danger ring and a badge.
  • Inspector (right) — the fields for the selected node.

Both rails are collapsible and ship collapsed, so a fresh editor opens full-bleed. Toggle them from the toolbar or with [ and ] while the canvas has focus; the inspector also opens on its own when you select a node, add one from the palette, or reveal a node from the linter dock. Rail state and the dock's fold persist per user across reloads.

On the canvas: press a (or double-click empty space) for quick-add — pick a kind to place it at the visible centre, or search an existing node id to reveal it. Drag a connection onto empty canvas to get a picker that creates the node already wired from the source. Right-click a node for duplicate, copy, paste, rename, and delete; on a read-only source loop those verbs are absent. Marquee-drag or modifier-click to select several nodes for a bulk move or delete — deleting a node removes its edges too. A selected edge carries a delete affordance at its midpoint, and an edge leaving a route node shows the condition that chooses it.

The inspector renders from the schema

The inspector fields come from the canonical DSL types and the tool registry's own schemas — never an editor-local model. A run-agent node shows agent, prompt, output_schema, and its session envelope; a gate shows its supported criterion fields, verdict_policy, and max_revisions; any tool-ID action renders a params form generated from that tool's input schema. Runtime fields authored through the definition file or API round-trip without loss and are validated by the daemon even when the current inspector does not expose an editor control for them.

Four composite controls write shapes the DSL constrains, so each writes its whole value at once rather than leaving a half-shape the daemon would reject:

  • ask — prompt, context, the required expect answer schema, whether agents may answer, and an expiry. The run page renders its answer form from that same expect schema.
  • route — the ordered routes[] list (a condition plus a forward target each) and the required default. Declaration order is the tiebreak the runtime evaluates in, so the editor preserves it exactly, and target pickers offer forward nodes only.
  • strategy on a fan-out — wait_all, fail_fast, race, or best_effort with its threshold, plus the missing: acceptable acknowledgement, alongside the bind_as / index_as iteration names.
  • review on an action node — the decision allowlist, when, prompt, responder policy, on_reject.route, and an expiry. Enabling writes the whole block; disabling clears it.

The linter is the daemon's

A dock along the bottom of the canvas shows structural invariants as pass/fail chips — acyclicity, reachability, termination, fan-out bounds, references, routing, and human requests — plus the current issue list. The editor surfaces the runtime's validation; it does not invent its own. An issue such as a missing positive max_fan_out (code fan_out_unbounded) links to the offending node, and Publish stays disabled until it clears.

Codes you will meet while authoring the newer kinds: route_default_missing, route_target_invalid, route_mapping_invalid, strategy_threshold_invalid, strategy_coverage_undeclared, iteration_name_conflict, ask_expect_required, review_shape_invalid, review_respond_schema_required, and responder_policy_invalid. A code the editor cannot attribute to a chip is still listed and still blocks Publish — the dock never claims an invariant it cannot name.

Validate and publish

  • Validate lints and compiles the draft without saving it (POST /loops/:name/validate), returning deterministic per-node codes — unknown_reference, node_id_invalid, verdict_policy_requires_judge, fan_out_unbounded.
  • Publish persists the draft with expected_version (PATCH /loops/:name). It is compare-and-swap: if the stored version moved since you opened the editor, the publish is rejected 409 with the current version, and the editor surfaces the conflict. Publish compiles and stores the resolved form the runtime consumes — never the author YAML.

This is the same authoring loop the CLI and API follow; the editor is a faithful render of the linter → 422 → publish gate.

Graph and DSL views

A Graph / DSL toggle switches between the canvas and the compozy.loop/v1 YAML on disk. The DSL view is the same definition, so an issue highlighted on a node is highlighted on the corresponding YAML line. In the graph view, start bindings show as a read-only chip strip pinned at the origin with the note that they are edited in the DSL view — there is no graph-side start editing in this version.

Node layout positions persist separately in a UI-annotation store keyed (workspace_id, loop_name, node_id) (GET/PUT /loops/:name/annotations), so layout is never mixed into the canonical definition. These endpoints require a live definition. Deleting a workspace-authored Loop removes its config override and editor annotations while preserving run history.

On this page