agent(name, opts, fn) is the inverse of a connector. Instead of calling out to a service,
the run parks (WAITING_AGENT) and asks the driving Claude Code session to run a prompt and
return a typed result. The handler is required and receives the answer as data.result.
An agent() step only works when Claude Code channels
are configured and the run is started from a Claude Code session with a channel
attached (flowctl run start … —channel <id>). That’s how the prompt reaches
an agent. Without a live channel the run parks at WAITING_AGENT with no one to
answer it — set up channels first. Support for OpenAI Codex as the driving
editor is in progress.
agent("triage", {
prompt: "Classify ticket {{ticket_id}}: {{subject}}. Return {category, severity}.",
resultType: "json", // "json" | "text" (default "text")
context: ["ticket_id", "subject"], // REQUIRED allowlist of vars sent as context
resultSchema: { // optional; validates a json result
type: "object",
required: ["category", "severity"],
properties: { category: { type: "string" }, severity: { type: "string" } },
},
onError: "triage_failed", // optional; route here if the agent reports an error
next: "route",
}, function onTriage(ctx, data) {
return { category: data.result.category, severity: data.result.severity };
});
How it behaves
promptis{{var}}-templated against the run’s variables.contextis an explicit allowlist — only those variables are sent with the prompt. Omit it and nothing is sent.resultType"json"makesdata.resultan object (validated againstresultSchemaif present);"text"makes it a string.- The agent answers from the shell:
flowctl agent list <exec>
flowctl agent get <exec> triage # prompt, result_type, context, schema
flowctl agent submit <exec> triage --type json --result '{"category":"auth","severity":"high"}'
flowctl agent submit <exec> triage --error 'could not classify' # routes to onError
If you don’t list the variables the prompt needs in context, the agent receives no
context and will guess. Always include every variable the prompt interpolates or relies on.
When to use it
Use agent() for the judgement steps in an otherwise deterministic workflow — classify,
extract, summarize, decide — when the run is being driven interactively by Claude Code. It keeps
no model credentials in the workflow and lets a human or agent supply the answer.
For unattended automation (a scheduled job, a webhook with no human present), call a model directly with the LLM connector instead.
How the prompt reaches an agent
The mechanism is the Claude Code channel bridge. When you start a run with
--channel <id> from a session running the bridge, the engine pushes the prompt (and its
allowlisted context) into that session as a <channel interaction="agent"> event; the agent does
the work and submits the result. This isn’t optional polish — it’s how an agent() step is
answered at all. Set up channels before relying on agent() in a workflow.
Next: Claude Code channels.