flow_graph column (jsonb) of the
agent’s agent_config row. The TeleQuick control plane’s
pipelineFromFlowGraph() prefers a saved graph over the synthesized
ASR→LLM→TTS chain, so an agent with a saved graph runs exactly what is
drawn on the canvas rather than a pipeline inferred from its profile.
The saved shape
agent_config.flow_graph holds a single object:
positions exists for the editor. Everything else is forwarded to the
runtime. If a step has no saved position — a graph written by something
other than the canvas, or a step added after the last layout — the editor
lays the unpositioned steps out in a row so nothing is hidden off-canvas.
node_typeselects the spec. Every node config carries anode_typestring that must match thenameof a spec in the node-spec catalog. The editor resolves the spec by that name to know what to render and what to validate.- Step ids are wire keys. The runtime routes on the keys of
nodes, so they must be unique and stable. When you add a step, the editor seeds the id from the spec’s name and only appends a numeric suffix if that id is already taken. Renaming a step changes the key the runtime routes on.
Where the palette comes from
The palette is not a fixed list in the console. It is the server’s node-spec catalog, fetched at screen load. Each spec declares itsname, displayName, description, a category, a list of
properties, and optional graphConstraints.
Three things follow from the spec, not from the console:
- The palette groups. Steps are grouped by the
categorythe spec declares. - New-step defaults. When you add a step, the editor writes
node_typeplus every property that declares adefault. Properties without a default start unset. - The per-step form. The inspector walks the spec’s
propertiesand applies the spec’s display rules. There is no per-node-type form in the console.
A group with no specs in it is not rendered. The category list is kept
deliberately in step with the catalog’s own vocabulary: a category the
server never sends would otherwise render as an empty heading. IVR menu
routing is not exposed in the builder — keypad capture lives under call
control.
Some properties reference other objects rather than holding a literal
value. The editor resolves those against two catalogs:
- Tools come from
admin.listAgentTools({ orgId, agentId }), which returns the agent’sagent_toolrows (kind,name,description,silent,spec) ordered by creation. The dropdown uses the tool name as the value and shows itskind. - Step targets are the other steps currently on the canvas, computed live. Rename a step and every “next step” dropdown updates at once.
One successor per step: how next_node behaves
A step’s successor is a single field,next_node, holding one step id.
There is no branching fan-out in the saved shape.
The canvas enforces that in both directions:
- Loading. Edges are derived from the saved graph. For each node with
a non-empty string
next_node, the editor draws one edge from that node to the named target. - Connecting. Dragging a new edge out of a step first removes any
existing edge from that step, then adds the new one and writes
next_nodeon the source. A second outgoing edge would silently overwrite the first on save, so it replaces instead. - Editing in the inspector. Changing
next_nodein the step form re-derives that step’s edge. Clearing it removes the edge.
Graph constraints
A spec may declare agraphConstraints object. These are the graph-shape
rules, and they live on the server so the editor and the runtime cannot
drift apart.
Constraints that a spec does not declare are not enforced by the editor.
What blocks a publish
Save & publish is disabled while a save is in flight, while there is nothing to save, and while the graph has any of the problems below. Each problem is listed above the canvas with the offending step id:- No start step.
entry_nodeis unset, or it names a step that is not on the canvas. - An unknown step type. A node’s
node_typedoes not match any spec in the catalog this build loaded. The node renders with its type or id rather than a blank box, marked invalid, and the inspector says the build does not know that type. - A step property fails its spec contract. The editor runs the spec’s full validation — required, min/max, URL shape, max length — over the properties the display rules actually show for that node.
- A non-terminal step with no successor. Reported as “the call would stop there”.
- An instance limit exceeded. More steps of one type than that spec’s
maxInstancesallows.
Save, then hydrate
Publishing is two writes, and the screen performs both in order:adminAgents.saveFlowGraph({ orgId, agentId, flowGraph })persists theSavedFlowGraphobject toagent_config.flow_graph.admin.hydrateAgent({ orgId, agentId })hydrates and publishes the agent’s runtime config.
hydrateAgent returns:
If
ready is false, the canvas reports that the graph saved but the
agent cannot run yet, naming the providers from missing_providers. The
graph is on disk and hydrated; the steps that need those credentials
cannot execute.
Every hydrate is recorded in the audit trail as action hydrate on
resource type agent_config, carrying hydrated_at_ms and ready. That
is what lets an operator later correlate “I hit Save, and the agent config
was hydrated shortly after”.
Agents with no flow graph
An agent whoseflow_graph is null or has no nodes opens on an empty
canvas with an explanatory overlay rather than a blank grid. That is a
valid, working state: the agent runs as a single-model agent, straight
from its profile settings, on the synthesized pipeline.
Adding a step from the palette starts a multi-step flow. The first step
you add becomes the start step if none is set yet. Nothing changes for
live calls until you save and publish — and once a graph is published,
pipelineFromFlowGraph() prefers it, so the profile-derived pipeline is
no longer what runs.
Removing every step and publishing an empty graph is not something the
editor will publish: a graph with steps requires a valid start step, and
the save button stays disabled while problems remain.
Related
- Modalities overview
- Telemetry —
agent_idon CDRs, set when a call routed through an agent graph