Skip to main content

Flow — Visual Agent Builder

The Agent Flow editor is a drag-and-drop canvas for building agents without writing a main loop. You connect pre-built nodes — LLM calls, file operations, git commands, memory reads, conditional branches, and more — into a visual graph. Codebolt executes that graph at runtime as a fully functional agent.

When to use the visual builder

Use the flow builder when…Use code levels when…
You want to prototype quickly without TypeScript boilerplateYou need custom processor logic
Your agent is a linear or branching pipelineYour agent has a complex, dynamic loop
You want to compose Codebolt API nodes visuallyYou're integrating non-Codebolt systems
You need to hand the agent to a non-engineer to modifyYou need a testable, version-controlled class

Flow agents and code agents are fully interchangeable from the server's perspective — both receive tasks, execute, and return results through the same WebSocket protocol.


Creating a flow agent

From the desktop app

  1. Open Agent Creator panel (sidebar or ⌘ K → "Create Agent").
  2. Select Visual Flow Agent.
  3. Codebolt scaffolds an .agentflow file at:
    agentflows/agentflow_{timestamp}_{random}.agentflow
  4. The Agent Flow Editor opens automatically.

From the CLI

codebolt agent create --flow --name my-flow-agent

This creates the .agentflow file and a codeboltagent.yaml manifest with type: flowAgent.


The editor

┌─────────────────────────────────────────────────────────────────┐
│ [ Create ] [ Save ] [ Properties ] my-flow-agent.agentflow│
├───────────────┬─────────────────────────────────┬───────────────┤
│ │ │ │
│ Node Palette │ Canvas │ Properties │
│ │ │ Panel │
│ ▸ Chat │ ┌──────────┐ ┌──────────┐ │ │
│ ▸ File Sys │ │OnMessage │───▶│Inference │ │ Node type │
│ ▸ Git │ └──────────┘ └──────────┘ │ name: value │
│ ▸ LLM │ │ │ │ ... │
│ ▸ Memory │ ▼ ▼ │ │
│ ▸ Tasks │ ┌──────────┐ ┌──────────┐ │ │
│ ▸ Logic │ │ Branch │ │SendMsgNd │ │ │
│ ▸ Plugins │ └──────────┘ └──────────┘ │ │
│ │ │ │
├───────────────┴─────────────────────────────────┴───────────────┤
│ Search nodes... │
└─────────────────────────────────────────────────────────────────┘

Left panel — Node Palette: Expandable categories of available nodes. Click a node to add it to the canvas; or drag it to a specific position.

Center — Canvas: The LiteGraph-powered graph editor. Drag nodes to reposition. Click and drag from an output slot to an input slot to create a connection. Right-click the canvas for a context menu.

Right panel — Properties Panel: Appears when you select a node. Shows that node's configurable fields (prompt, file path, key name, etc.) as editable inputs.

Keyboard shortcuts

ShortcutAction
Delete / BackspaceDelete selected node or link
Ctrl Z / ⌘ ZUndo
Ctrl Y / ⌘ YRedo
Ctrl A / ⌘ ASelect all
Mouse wheelZoom canvas
Middle-dragPan canvas
FFrame selected nodes

Node categories

Nodes are grouped into categories in the palette.

Chat

NodeWhat it does
OnMessageEntry point — fires when the user sends a message
SendMessageSends a reply to the user in the chat panel
GetChatHistoryRetrieves the full conversation history
WaitForReplyPauses execution until the user replies
AskQuestionSends a message and waits for a response
SendNotificationEventEmits a UI notification event

LLM / Inference

NodeWhat it does
InferenceCalls the LLM with a prompt; returns the response
GetModelConfigReads the current model configuration

File System

NodeWhat it does
ReadFileReads a file from disk
WriteToFileWrites content to a file
CreateFileCreates a new file
UpdateFileAppends or replaces file content
DeleteFileDeletes a file
ListFileLists files in a directory
SearchFilesSearches for files matching a pattern
GrepSearchFull-text grep across files
FileSearchFuzzy file name search
EditFileWithDiffApplies a diff-style edit to a file
ReadManyFilesReads multiple files and concatenates
ListDirectoryLists directory contents

Git

NodeWhat it does
GitStatusRuns git status
GitAddStages files
GitCommitCreates a commit
GitPushPushes to remote
GitPullPulls from remote
GitCheckoutChecks out a branch
GitBranchLists or creates branches
GitLogsRetrieves commit history
GitDiffShows diff output
GitInitInitialises a git repository

Memory

NodeWhat it does
MemoryAddWrites a key-value entry
MemoryGetReads a key-value entry
MemoryMarkdownSaveSaves a markdown note
MemoryMarkdownUpdateUpdates a markdown note
MemoryMarkdownListLists markdown notes
MemoryJsonSaveSaves a JSON record
MemoryJsonUpdateUpdates a JSON record
MemoryJsonListLists JSON records

Tasks

NodeWhat it does
CreateTaskCreates a task
GetTaskListLists all tasks
StartTaskMarks a task as started
CompleteTaskMarks a task as completed
UpdateTaskUpdates task fields
CreateTaskGroupCreates a task group
GetTaskDetailGets full task detail
AddStepToTaskAdds a sub-step to a task
GetAllStepsLists all steps for a task

Terminal

NodeWhat it does
ExecuteCommandRuns a shell command; returns output
ExecuteCommandRunUntilErrorRetries a command until it errors
ExecuteCommandWithStreamStreams command output line by line
SendManualInterruptSends SIGINT to the current command

MCP (Model Context Protocol)

NodeWhat it does
MCPGetEnabledLists enabled MCP servers
MCPListToolsLists tools from a server
MCPExecuteToolExecutes a named tool on an MCP server
MCPConfigureConfigures an MCP server connection

Agent Management

NodeWhat it does
StartAgentSpawns a child agent with a task
FindAgentFinds an agent by name or ID
ListAgentsLists all available agents
AgentsDetailGets metadata for an agent

Logic & Flow Control

NodeWhat it does
BranchIf/else conditional — routes data based on a condition
SequenceExecutes connected nodes in strict order
And / Or / NotBoolean operators
SelectorPicks one of N outputs based on an index
DelayPauses execution for a set duration

Data & Constants

NodeWhat it does
ConstantStringString literal value
ConstantNumberNumeric literal value
ConstantBooleanBoolean literal value
ConstantObjectJSON object literal
JSONParseParses a JSON string into an object
ToStringCoerces any value to a string
CompareReturns boolean result of a comparison
ContainsChecks if a string contains a substring

Output Parsing

NodeWhat it does
ParseJSONExtracts structured JSON from LLM output
ParseCSVParses CSV-formatted output
ParseXMLParses XML-formatted output
ParseTextExtracts text blocks by pattern
ParseErrorsExtracts error messages from output

Web

NodeWhat it does
SearchPerforms a web search
GetFirstLinkReturns the first URL from search results
CrawlerStartOpens a headless browser session
CrawlerGoToPageNavigates to a URL
CrawlerClickClicks an element
CrawlerScrollScrolls the page
CrawlerScreenshotTakes a screenshot

State

NodeWhat it does
GetAgentStateReads the current agent's state object
AddToAgentStateAdds or updates a key in agent state
GetProjectStateReads project-level shared state
UpdateProjectStateWrites project-level state

Action Planning

NodeWhat it does
GetAllPlansLists all action plans
CreateActionPlanCreates a new action plan
AddTaskToActionPlanAdds a task to a plan
StartTaskStepStarts a step in an action plan

Connecting nodes

Nodes have input slots (left side, triangular) and output slots (right side, triangular). Drag from an output slot to an input slot to connect them. The connection type must match (e.g., a string output cannot connect directly to an object input without a converter node).

Data flows from left to right through connections. Execution triggers flow from event outputs — OnMessage fires first and pushes data downstream.


Configuring a node

Select a node on the canvas to open the Properties Panel. Each field corresponds to a property of that node type:

  • String fields — type directly into the text input
  • Number fields — type a number; the field enforces the type
  • Boolean fields — checkbox toggle
  • Template fields — support {{variable}} interpolation from upstream node outputs

To wire an upstream output into a field, connect the output slot of the upstream node to the corresponding input slot on the target node.


Agent Properties

Click Properties in the editor toolbar to open the Agent Properties dialog. These settings are saved to codeboltagent.yaml:

FieldDescription
titleDisplay name shown in the agent picker
descriptionShort description shown in the marketplace/picker
versionSemantic version string (e.g., 1.0.0)
typeMust be flowAgent for visual flows
agentIdUnique ID — auto-generated from title if left empty

How flow agents run

When the server starts a flowAgent:

  1. Runtime initialization — Codebolt extracts the bundled LiteGraph runtime to {userData}/agentflow-runtime/ on first use and installs dependencies.
  2. Graph deserialization — the .agentflow JSON is loaded and the LiteGraph graph object is reconstructed in memory.
  3. Execution — the OnMessage node fires when a user message arrives, pushing the message content through connected nodes.
  4. Node execution — each node's onExecute() method runs when upstream data is available. Codebolt API nodes make real SDK calls (file reads, LLM inference, git commands, etc.).
  5. Output — a SendMessage node at the end of the graph sends the reply to the user.

The runtime is isolated: one LiteGraph instance per agent run. Crashes in the graph do not affect the server.


Extending with custom nodes (plugins)

The palette is extensible via Agent Node plugins — npm packages placed in .codebolt/AgentNodes/. Each plugin contributes one or more new node types.

Plugin discovery: The server scans .codebolt/AgentNodes/ at startup for package.json files with a codebolt.plugin key. The UI loads each plugin's JavaScript bundle and registers the node types with LiteGraph.

Minimal plugin structure:

.codebolt/AgentNodes/my-nodes/
├── package.json
├── dist/
│ ├── index.cjs ← server-side node logic
│ └── ui.js ← browser bundle (registers nodes in LiteGraph)

package.json:

{
"name": "my-agentflow-nodes",
"version": "1.0.0",
"description": "Custom nodes",
"codebolt": {
"plugin": {
"displayName": "My Nodes",
"description": "Custom processing nodes",
"category": "Custom",
"nodes": [
{
"type": "my-nodes/SentimentNode",
"title": "Sentiment Analysis",
"description": "Scores text sentiment 0-1",
"icon": "S",
"color": "#6366f1"
}
]
}
},
"main": "dist/index.cjs",
"browser": "dist/ui.js"
}

UI bundle (dist/ui.js) — must export registerNodes:

import { LiteGraph } from 'litegraph.js';

class SentimentNode {
static title = 'Sentiment Analysis';
static metadata = {
title: 'Sentiment Analysis',
description: 'Scores text 0-1',
icon: 'S',
color: '#6366f1',
category: 'Custom'
};

constructor() {
this.addInput('text', 'string');
this.addOutput('score', 'number');
this.addOutput('label', 'string');
}

onExecute() {
const text = this.getInputData(0);
// Call backend or compute locally
this.setOutputData(0, 0.85);
this.setOutputData(1, 'positive');
}
}

export function registerNodes(LiteGraph) {
LiteGraph.registerNodeType('my-nodes/SentimentNode', SentimentNode);
}

After adding the plugin folder, restart the dev server and the new node appears in the palette under the plugin's category.


The .agentflow file format

Flows are stored as plain JSON in LiteGraph's serialization format. They are version-controlled alongside your project. Example structure:

{
"version": 1,
"type": "litegraph",
"last_node_id": 4,
"last_link_id": 3,
"nodes": [
{
"id": 1,
"type": "Chat/OnMessageNode",
"title": "On Message",
"pos": [100, 200],
"outputs": [{ "name": "message", "type": "string", "links": [1] }]
},
{
"id": 2,
"type": "LLM/InferenceNode",
"title": "LLM Call",
"pos": [320, 200],
"properties": { "systemPrompt": "You are a helpful assistant." },
"inputs": [{ "name": "prompt", "type": "string", "link": 1 }],
"outputs": [{ "name": "response", "type": "string", "links": [2] }]
},
{
"id": 3,
"type": "Chat/SendMessageNode",
"title": "Send Reply",
"pos": [560, 200],
"inputs": [{ "name": "message", "type": "string", "link": 2 }]
}
],
"links": [
[1, 1, 0, 2, 0, "string"],
[2, 2, 0, 3, 0, "string"]
],
"groups": [],
"config": {},
"metadata": { "createdAt": "2026-04-22T10:00:00.000Z" }
}

Each link entry is [linkId, sourceNodeId, sourceSlot, targetNodeId, targetSlot, type].


API reference

The Agent Flow system exposes a REST API at /agentflow:

MethodPathPurpose
POST/agentflow/createCreate a new empty .agentflow file
GET/agentflow/get?filePath=Load flow data from disk
PUT/agentflow/updateSave the current graph state to disk
GET/agentflow/pluginsList all installed agent node plugins
GET/agentflow/plugins/:name/uiServe a plugin's UI JavaScript bundle

Create:

POST /agentflow/create
→ { success: true, filePath: "agentflows/agentflow_1714000000_abc.agentflow" }

Load:

GET /agentflow/get?filePath=agentflows/agentflow_1714000000_abc.agentflow
→ { success: true, data: { ...LiteGraphData }, filePath: "..." }

Save:

PUT /agentflow/update
Body: { filePath: "...", graphData: { ...LiteGraphData }, name: "my-flow" }
→ { success: true, message: "Flow updated successfully", filePath: "..." }

See also