Skip to content

Workflow schema ​

A V2 workflow is a directed graph stored as JSON or YAML under .agentx/workflows/. The editor's canvas displays the same definition. Use agentx workflow validate <file> before importing a hand-written file.

TermMeaning
WorkflowThe named automation definition
NodeOne unit of work, such as a trigger, an agent call, or an output action
EdgeA connection from one node ID to another; a branch can choose a named output port
RunOne execution of a workflow
Pause / resumeStops and later continues a run where supported

Required structure ​

FieldMeaning
idStable lowercase ID using letters, digits, hyphens or underscores
version2
titleHuman-readable title
nodesNonempty array of { id, type, config } objects
edgesConnections such as { "from": "start", "to": "report" }
stateDispatch control: active, disabled, or quarantined
statusReview metadata: draft, review, active, or deprecated

state controls execution. A workflow marked status: "draft" can still run if its state is active. Use state: "disabled" for a proposal that must not fire yet. A quarantined workflow is held because of a detected conflict; investigate that conflict before reactivating it.

A small example ​

Download demo-report.json. It contains three nodes: trigger.manual, agent, and end. The agent node uses config.agentId: "cx" and a fixed report prompt. Replace cx with your registered agent before using it outside the demo.

sh
agentx workflow validate demo-report.json
agentx workflow add demo-report.json

The example is deliberately disabled. After reviewing it, set state to active, import the updated file, and test it:

sh
agentx workflow run demo-report --watch
agentx workflow runs demo-report

The daemon must have workflows.enabled: true. These commands use http://127.0.0.1:18800 by default; pass --daemon <url> when working against another endpoint where that option is available.

Inputs and error handling ​

Node configuration can reference earlier outputs with templates such as {{trigger.payload.text}}. A manual run can supply JSON using --input '{"text":"Prepare a summary"}'. Each node may declare retry: { maxAttempts, backoffMs }; retrying an external write can repeat its side effect, so use it only when the action can safely be repeated.

An agent node needs a registered agentId. It can also set timeoutMinutes. When set, AgentX stops the step once that many minutes have passed and frees the agent's slot, and the step fails with "timed out after …s". Leave it empty for no limit. Give agent work room: a review or a code change can take 20 minutes or more. See time limits and cancel. An action.send node needs a live channel and destination. branch uses named ports to choose an edge; checkpoint pauses for review. Node configuration is validated by the corresponding handler, so passing the top-level file validator alone does not prove that credentials or destinations work.

Node types ​

TypeWhat it does
trigger.manual, trigger.channel, trigger.cron, trigger.hook, trigger.formStarts a run: by hand, from a channel message, on a schedule, on an on:* event, or from a form
agentRuns an agent with a prompt and passes its answer on
classifyPicks one label with a confidence; the port named after the label fires, or unsure
transformPicks or reshapes values from earlier steps
branchChooses an outgoing port; edges match it with fromPort
ruleChecks a declared rule and can stop the run early
gateway.parallelWaits for parallel branches to join
action.send, action.react, action.editMessageSends a message, reacts to one, or edits one, through a channel
action.createIssue, action.setLabel, action.readLabel, action.logTimeCreates an issue (GitLab or GitHub), or changes labels, reads labels or logs time on a GitLab issue or merge request
action.callHTTPMakes an outgoing HTTP request
action.run, action.builtinRuns a registered action, or a built-in one, by name
signal.emit, signal.waitPublishes a signal, or pauses until one arrives
timer.boundaryPauses until a timer runs out
subProcessStarts another workflow and waits for it to finish
checkpointPauses for review until a resume event arrives
endCloses the run with the given status

Each type's config is checked by its handler in src/workflows/nodes/.

Event trigger filters ​

A trigger.hook node subscribes to an on:* event, such as on:gitlab-mr or on:github-pr. Its config.filter narrows which events start a run. Events that don't match are dropped before any agent is woken.

FilterEventsFires only when
topicon:n8nThe topic in /webhook/n8n/<topic> is listed
actionGitLab issue and MR eventsThe action, such as open, is listed
mentionson:gitlab-noteThe comment @-mentions a listed username
noteableTypeon:gitlab-noteThe comment is on a listed type: merge_request or issue
assigneesAdded, reviewersAdded, labelsAddedGitLab issue and MR eventsThe update added a listed assignee, reviewer, or label
skipSelfAuthoredEvents with an authorThe author is not one of this workflow's own agents (off by default)
ignoreAuthorsEvents with an authorThe author is not in the list. Leading @ and letter case are ignored
maxFiresPerTargetIssue, MR, PR, and note eventsThis workflow has fired fewer than count times for the same issue, MR, or PR within windowMinutes (default 60)

Loop guard. With skipSelfAuthored: true, a workflow skips events written by the bot identity of any agent it runs, so a routine's own comment or label change cannot restart it. It is off by default, because label-driven lifecycle workflows react to their own agent's transitions on purpose. To find that identity, AgentX checks the GitLab adapter's username-to-agent map, the signature on AgentX comments, and each agent's gitlabUsernames or githubUsernames in agentMappings. If the identity can't be resolved, the daemon logs this once and only ignoreAuthors applies. On GitLab issue and MR events the identity comes only from the username map. When all agents share one GitLab token, they post as one user, which maps to a single agent, so a workflow run by a different agent won't see those events as self-authored. Comments avoid this because AgentX reads the signature on each comment.

This check can't catch two routines that trigger each other, such as a generator and a critic. Neither one sees its own identity. Use maxFiresPerTarget for that case:

json
{ "event": "on:gitlab-note",
  "filter": { "noteableType": ["merge_request"], "ignoreAuthors": ["ci-bot"],
              "maxFiresPerTarget": { "count": 3, "windowMinutes": 60 } } }

A note and an update on the same MR count toward the same limit. Every loop-guard skip still claims the event, unless the trigger sets passthrough. That way the adapter's fallback (the project's default agent, or the legacy @-mention path) doesn't wake the agent instead. Every skip is logged as [workflows] <id> skipping <event> (<reason>). The counters are held in memory, so a daemon restart resets them.

The editor's assistant can propose a workflow from a request. Apply to canvas replaces the current graph. Review the agent, input, destination, and error path before saving. The complete implementation is in src/workflows/types.ts and src/workflows/nodes/.

Check it worked ​

  1. Terminal: run agentx workflow validate <file>. It reports no errors.
  2. Terminal: run agentx workflow list. Your workflow is listed with its state.
  3. Terminal: after a test run, agentx workflow runs <id> shows the run and its result.

If something is wrong ​

  • The workflow never fires: check state is active (not only status) and that the daemon has workflows.enabled: true.
  • An agent step fails: its agentId must match an agent in agentx.json.
  • An event trigger doesn't fire: look for [workflows] <id> skipping in agentx daemon logs; a filter or loop guard dropped the event.
  • A run stops partway: agentx workflow trace <runId> shows which step failed and why.

Released under the MIT License.