| name | zenflow |
| description | Multi-agent workflow engine for Go. Declarative YAML workflows; an LLM coordinator routes events through hub-and-spoke mailboxes. Three CLI verbs (flow / goal / agent) cover deterministic execution, dynamic planning, and one-shot agent calls. Use this when you need to run a multi-step LLM pipeline with branching, loops, conditions, retries, or persistence. |
| allowed-tools | Bash |
zenflow
zenflow is a multi-agent workflow engine for Go. Workflows are declarative YAML; the engine is a DAG executor with an LLM coordinator that narrates progress and routes events between running steps through race-safe mailboxes. The Go SDK layer is goai - any provider goai supports works as a zenflow agent backend (Google, AWS Bedrock, Azure, OpenAI-compatible).
Install
One-line installer (Linux + macOS):
curl -fsSL https://zenflow.sh/install.sh | sh
Or via go install:
go install github.com/zendev-sh/zenflow/cmd/zenflow@latest
Or pull the Docker image:
docker pull ghcr.io/zendev-sh/zenflow:latest
The single Go binary has no runtime dependencies.
Three CLI verbs
| Command | When |
|---|
zenflow flow workflow.yaml | The plan is fixed up-front; you want a deterministic DAG execution. |
zenflow goal "build a thing" | The plan must adapt to user input; the coordinator decomposes the goal into a workflow on the fly and runs it. |
zenflow agent "<prompt>" | One-shot agent call with optional tool loop. Reuses zenflow's lifecycle hooks and provider routing. |
zenflow flow - deterministic DAG
zenflow flow review.yaml --model gemini/gemini-3-pro-preview
Loads the YAML, validates, runs every step in dependency order. Failures cascade per options.onStepFailure.
zenflow goal - dynamic planning
zenflow goal "Refactor the auth module to use OAuth2" --model bedrock/anthropic.claude-sonnet-4-6
The coordinator LLM decomposes the goal into a workflow (agents + steps), validates it against the spec schema, and executes. Pass --plan to print the plan without running it.
zenflow agent - single-agent chat
zenflow agent "Summarise this repo's README" --model azure-deployment/gpt-5
A single agent loop with the configured tool catalog. Most useful for one-shot tasks that don't need multi-step coordination.
Provider env vars
Set the credentials for whichever provider you target:
| Provider | Env var | Example model |
|---|
| Google Gemini | GEMINI_API_KEY | gemini/gemini-3-pro-preview |
| AWS Bedrock | AWS_ACCESS_KEY_ID + AWS_SECRET_ACCESS_KEY (or AWS profile) | bedrock/anthropic.claude-sonnet-4-6, bedrock/minimax.minimax-m2.5 |
| Azure (AI Services / OpenAI / Anthropic) | AZURE_OPENAI_API_KEY + AZURE_RESOURCE_NAME | azure-deployment/gpt-5, azure-deployment/claude-sonnet-4-6 |
The --model flag accepts the provider/model-id form. For Azure, deployment-based URLs are auto-resolved when the model id matches a known deployment family.
MCP tools
zenflow reads a Claude-compatible .zenflow/settings.json and exposes every declared MCP server's tools to agents. All three verbs use it; override with --mcp-config PATH, disable with --no-mcp.
{ "mcpServers": { "firecrawl": { "command": "npx", "args": ["-y", "firecrawl-mcp"], "env": { "FIRECRAWL_API_KEY": "${FIRECRAWL_API_KEY}" } } } }
Tools are namespaced <server>__<tool>; an agent's tools: list grants a whole server by its bare name (firecrawl) or one tool by its full name (firecrawl__scrape). stdio, HTTP, and SSE transports are supported, with ${VAR} env expansion.
YAML workflow shape
Minimum:
name: my-workflow
agents:
writer:
description: A concise technical writer.
prompt: "Always reply in numbered bullet points."
steps:
- id: draft
agent: writer
instructions: "Draft a 5-point summary of {{.context.input}}."
Add features as you need them:
dependsOn: [otherStep] - DAG edges.
condition: steps.draft.result.length > 100 - CEL expression; step is skipped when false.
loop: - three flavours: forEach (iterate an array from a previous step's output), repeat-until (run a sub-DAG until an untilAgent returns done: true or a until CEL expression is true), outputMode: cumulative | last.
include: subworkflow.yaml - compose sub-workflows; their step IDs are namespaced under the parent step.
tool: read / toolInput: {path: $steps["prev"].content} - invoke a registered goai tool directly (no LLM call). String values in toolInput starting with $ are evaluated as CEL expressions. Available CLI tools: bash, read, write, glob, grep. Mutually exclusive with agent, instructions, loop, and include.
resultSchema: { ... } - auto-injects a submit_result tool so the agent emits structured output.
options: - maxConcurrency, onStepFailure: cascade | skip-dependents | abort, timeout, stepTimeout, isolation: worktree-per-step, scheduler: dependency-first | round-robin | least-busy.
Full reference: https://zenflow.sh/yaml.
NDJSON --json event schema
Every CLI verb accepts --json for line-delimited JSON output. Each event line carries:
| Field | Type | Notes |
|---|
type | string | Event type (see table below). |
timestamp | RFC3339 string | Event clock. |
runId | string | Workflow run ID; correlates events from the same run. |
stepId | string | Step ID; absent for run-scoped events. |
agent | string | Agent name; absent for non-agent events. |
agentId | string | Per-call agent ID (set on streaming output events when the runner is namespaced). |
message | string | Human-readable detail; semantics differ per event type. |
duration | string | Go duration string ("1.234s"); set on completion events. |
tokens | object | {prompt, completion, total} token counts; set on completion events. |
error | string | Error string; set on failure events. |
data | object | Event-type-specific payload (input args, output bytes, drop reasons, etc.). |
delta | string | Streaming token delta. Only on output. |
done | boolean | Streaming-end marker. Only on output. |
reasoning | boolean | Set to true when the streaming delta is reasoning/thinking content (vs final agent text). Omitted otherwise. |
Event types
type | Emitted when |
|---|
workflow_start | Workflow run begins. |
workflow_end | Workflow run ends (success or failure). |
step_start | A DAG step begins. |
step_end | A DAG step ends successfully. |
step_skipped | A step skipped (failed dep with skip-dependents strategy, or false condition). |
error | A step failed; subsequent dependents may cascade per options.onStepFailure. |
agent_turn | The agent loop took a turn (LLM call). data.phase is request or response. |
tool_call | A tool was invoked. data.phase is start or end. |
message | An informational message (e.g. CEL skip reason, forEach item cap). |
coordinator_narration | Coordinator's narrate(...) tool call. |
coordinator_message | Coordinator's forward_to_agent(...) tool call. |
coordinator_synthesis | Coordinator's finalize(summary=...) tool call. |
coordinator_inbox_message | Reverse reply from a resumed step landed in the coordinator's inbox. |
message_sent | An agent or coord-side outbound message was queued. |
message_dropped | A router message was dropped before delivery. data.reason carries the typed reason. |
agent_inbox_drain | An agent drained one router message into its LLM conversation. |
agent_idle | The agent finished a goai iteration with no unread mailbox messages. |
agent_wake | The agent woke from idle to drain new messages. |
max_wake_cycles_warning | Wake-cycle cap is at 80% of configured limit. |
resume_started | Auto-resume goroutine spawned for a terminated step that received a new router message. |
resume_completed | Resume run finished with a final response. |
resume_failed | Resume could not complete (ctx cancel, transcript cap, agent error). |
resume_queued | A resume attempt arrived while another for the same step was already in flight. |
transcript_sealed | Transcript Append hit the size cap; subsequent appends for the step are suppressed. |
plan_ready | RunGoal decomposition produced a workflow. data.workflow carries the parsed *Workflow. |
output | Streaming agent output token. Set delta, done, optionally reasoning. |
The schema is additive: new event types may appear, existing types never reshape. Unknown type values are safe to skip.
Exit codes
| Code | Meaning |
|---|
0 | Success. |
1 | Step or workflow failure. |
2 | Validation or coordinator-side error. |
3 | Configuration or usage error (missing argument, unknown flag, no LLM provider). |
124 | Hard-watchdog timeout. Fires when --timeout was set and the process did not unwind within timeout + 30s; goroutine stacks are dumped to stderr. |
When to use which mode
You have a YAML workflow file already. → zenflow flow workflow.yaml
You have a free-form goal in a string. → zenflow goal "<goal>"
You have a one-shot agent prompt. → zenflow agent "<prompt>"
You want deterministic, repeatable runs. → zenflow flow
You want the coordinator to plan on the fly. → zenflow goal
You want to chain steps, conditions, loops. → flow or goal (both support full YAML)
You only need a single LLM call. → zenflow agent
You need to embed in Go code. → import github.com/zendev-sh/zenflow
You need to script around the binary. → use --json output (see schema above)
Embedding in Go
The same engine is exposed as a library:
import (
"context"
"github.com/zendev-sh/zenflow"
"github.com/zendev-sh/goai/google"
)
func run(ctx context.Context) error {
llm, _ := google.New(ctx, "gemini-3-pro-preview")
orch := zenflow.New(
zenflow.WithModel(llm),
zenflow.WithStorage(zenflow.NewMemoryStorage()),
)
defer orch.Close()
wf, err := zenflow.LoadWorkflow("review.yaml")
if err != nil { return err }
result, err := orch.RunFlow(ctx, wf)
_ = result
return err
}
49 With* orchestrator options + 22 WithRunner* agent options cover provider config, concurrency, timeouts, mailbox tuning, transcript caps, isolation, observability, and resume policies. Full reference: https://zenflow.sh/api.
Reference