run-workflow
Skill3 starsChanged 13 days agoMITView on GitHub
How to write and run a dynamic workflow script with the `run_workflow` tool: a Python script that orchestrates many agents with `agent`, `parallel`, `pipeline`, and `phase`. Use when a task splits into many independent or staged pieces of agent work (fan out over files, modules, bugs, or review angles, then aggregate), or when the user asks for a workflow. Don''t use for a single delegated task (use a subagent) or for legacy workflow files (see migrate-workflows).
6 other projects use a version of this.
Adopted by
Most recently taken up by
- khpeet/newrelic-skillstheir own version, first seen Nov 8, 2026
- norman-finance/norman-mcp-servertheir own version, first seen Nov 2, 2026
- HLIntel/thea-softwareword for word, first seen Nov 1, 2026
- vasilyu1983/AI-Agents-publictheir own version, first seen Oct 13, 2026
- oocx/tfplan2mdtheir own version, first seen Oct 8, 2026
- simstudioai/skillstheir own version, first seen Sep 9, 2026
Other versions6 projects use a version of this.
Word for word · 1
- HLIntel/thea-software★ 3100% same
Edited · 0
None found.
Wrote their own version · 5
- oocx/tfplan2md★ 174same name
- vasilyu1983/AI-Agents-public★ 94same name
- norman-finance/norman-mcp-server★ 65same name
- simstudioai/skills★ 2same name
- khpeet/newrelic-skills★ 1same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
The file
We split this skill into the rules it gives an agent
4 rules. 0 are common, 1 is distinctive, and 3 are very specific to this project.
0
Common
Most of the 5 edited versions we read keep these.
1
Distinctive
This author's own choices. Worth a look.
3
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 29
Read the tool result: it contains the log() output (Logs:) and what the script printed (Output:).
- Distinctive: line 32
If the tool returns an error, fix the script at the reported line and run it again.
- Very specific: line 97
When one agent should do several related turns, such as a reviewer that checks the fixes for the bugs it found, call it instead of awaiting …
- Very specific: line 168
If one agent in parallel fails, the whole call fails and the other agents in it are cancelled.
“Common” is measured: kept by at least 6 in 10 of the 5 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Dynamic workflows with run_workflow
A workflow is a short Python script that the run_workflow tool executes. The
script decides which agents to start, in what order, and what to do with their
results. Use it instead of many manual subagent calls when the orchestration
itself is code: loops over a list, fan-out then aggregate, or staged pipelines.
Steps
- Pass the script inline as
Script, or write it to a file (for example in the conversation scratch directory) and pass its absolute path asScriptPath. Set exactly one of them; a file is easier to fix and rerun. - Call
run_workflow(Script="...", Description="...")orrun_workflow(ScriptPath="/abs/path/workflow.py", Description="...").Descriptionis a one-line goal shown in the UI. The workflow runs in the background, and you are notified when it ends. For a quick script, such as acall_toolfan-out, setWaitMsBeforeAsync(up to 10000) to get the result in the same turn if it finishes in time. To pass inputs, setArgsto a JSON object; the script reads it as theargsdict. WhenScriptPathis a saved workflow's entry script,Argsis validated against the args schema in itsworkflow.json. - Read the tool result: it contains the
log()output (Logs:) and what the script printed (Output:). Each agent is also linked from the workflow step, so the user can open its conversation. - If the tool returns an error, fix the script at the reported line and run it again.
Script rules
The script runs in its own python3 process, which needs Python 3.8 or later
on PATH:
awaitworks at the top level, and you can defineasync defhelpers.- Installed library workflows are importable by name (
import <library>orfrom <library> import <name>); library modules see the same primitives as the script. - What the script prints (
print(),stderr, child-process output, and thereprof a trailing expression) is returned in the tool result'sOutput:section (and appended to the error when the workflow fails). - An uncaught exception stops the workflow and reports the script line.
CONVERSATION_IDandSTEP_INDEXidentify therun_workflowstep.WORKSPACESis a tuple of the workspace directories, primary first (empty if there are none).- The working directory is
Cwdif given, elseWORKSPACES[0], so relative paths resolve against it. Local modules are not importable.
Primitives
| Primitive | Behavior |
log(*args) | Adds a line to the workflow output and the UI. |
phase(name) | Starts a named phase; later logs and agents group under it. |
agent(prompt=None, schema=None, role=None, type_name="self", workspace="inherit", model=None) | Defines an agent; nothing runs yet. await agent(prompt, schema) runs it once and returns its result. a = agent(...) then await a(prompt, schema) runs one turn in a conversation kept across calls. |
parallel(items, fn) | Promise that calls fn(item) for each item and awaits all results concurrently; results are in item order. If one fails, the rest are cancelled. |
pipeline(items, fn) | Promise that calls and awaits fn(item) one item at a time; results are in item order. |
trajectory(last=None, types=None, tools=None, min_index=None, max_index=None) | await trajectory() returns steps before this run_workflow call started, oldest first. min_index and max_index bound step.index (inclusive); types and tools filter by step type or tool name; last keeps the last N matching steps. |
Arguments:
type_name: the agent type to run."self"uses the current agent's configuration; any available custom agent name also works.workspace:"inherit"(the current workspace),"branch"(a new isolated clone, for agents that edit code in parallel), or"share"(a new workspace that shares the underlying repository). Other values are errors.schema(required): a JSON schema (dict or string) for the agent's result. The agent returns the result by calling itsfinishtool, which checks it against the schema, andawait agent(...)returns the parsed value. An agent that ends without callingfinishfails the workflow. Use{"type": "string"}for a free-form answer.role: an optional short title for the agent's card, such as"Security reviewer".model: the agent's model tier:"inherit"(default, your model),"flash_lite","flash", or"pro". Use a lighter tier for simple, high-fan-out work.fn: usually alambdathat returnsagent(...), or anasync defhelper that awaits several agents. It must return something awaitable: anagent()or a call on one, a nestedparallel()/pipeline(), or anasync defcall. Build per-item prompts and roles with f-strings.
Awaiting the same agent twice runs it once and returns the same result. An
agent() that is never awaited or called never runs, so always await it, or
pass it to parallel / pipeline and await that.
The workflow step shows one card per agent, in the order agents start, titled
with its role, or else the first line of its prompt, so set a role or start
each prompt with a short line that names the job.
Agents that keep their context
When one agent should do several related turns, such as a reviewer that checks
the fixes for the bugs it found, call it instead of awaiting it. Each
a(prompt, schema) is lazy like an awaited agent() and returns that turn's
finish result; the agent's own prompt, if any, is its standing instructions
and leads the first turn. schema on a call defaults to the agent's. Turns on
one agent run one at a time, in the order they were started. A failed turn
raises, but the agent stays usable. An agent is either awaited or called, not
both. del a ends the agent, unless something else, such as a lambda, still
references it; every agent ends when the workflow ends.
reviewer = agent("You review Go code for correctness.", role="Reviewer")
bugs = await reviewer("List the bugs in foo.go.", {"type": "array", "items": {"type": "string"}})
fixes = await parallel(bugs, lambda b: agent(f"Fix: {b}", {"type": "string"}))
ok = await reviewer(f"Fixes made: {fixes}. Are all your bugs fixed?", {"type": "boolean"})
del reviewerCalling tools from the script
Where the run_workflow tool description lists tools for call_tool, the
script can call them directly:
call_tool(name, **args)returns a promise likeagent()does. Awaiting it runs the tool with the arguments you would pass to it directly and returns the tool's output as astr.- It raises
ToolError(with.tooland.message) if the tool fails or the call is denied..deniedisTruewhen a pre-tool hook or the permission check rejected the call before the tool ran, so a script can skip denied calls and still fail on real errors. - The user cannot be asked anything from a workflow: a call that needs user approval that has not already been granted is denied.
- Tool outputs are not shown to you unless the script logs or prints them.
Reading earlier conversation steps
Pass inputs you already have in Args (or inline in the script). Use
await trajectory(...) when the script needs to inspect steps from earlier in
this conversation—such as mining a failed run_command output without
rerunning it—and pass only the relevant slice into child agent() prompts.
Each returned Step is a dict that also supports attribute access (missing
fields return None as attributes):
index(int): 0-based step index.type(str): step type, e.g."USER_INPUT","PLANNER_RESPONSE","SYSTEM_MESSAGE", or"GENERIC"(tool calls).status(str): e.g."DONE","ERROR", or"CLEARED".- Message steps set
text(str); tool steps settool(str),args(parseddict), andoutput(str, truncated at 200k characters).
steps = await trajectory(tools=["run_command"], last=20)
errors = [s.output for s in steps if s.status == "ERROR" and s.output]
log(f"Found {len(errors)} failed command(s)")trajectory() only returns steps before the run_workflow call started (steps
added while the workflow runs in the background are not included). Older tool
steps evicted from memory have status == "CLEARED" and output == None; read
the conversation's transcript.jsonl / transcript_full.jsonl when you need
cleared tool outputs or steps added after the workflow started.
Behavior to know
- Workflow agents are main agents, not subagents. They are hidden from the conversation list and reachable from the workflow step. An awaited agent is single-use; a called agent lasts until deleted or the workflow ends.
await agent(...)returns when the agent's run ends, with the result it passed tofinish.- If one agent in
parallelfails, the whole call fails and the other agents in it are cancelled. Stopping the parent conversation stops all running workflow agents. - Agents do not see the script or each other. Put all the context an agent needs in its prompt, and ask for a short, well-defined output.
Example
Fan out over a list that an agent produces:
phase("Discover")
files = await agent(
"List the Go files under pkg/foo that call Bar(). Paths only.",
schema={"type": "array", "items": {"type": "string"}},
)
phase("Review")
reviews = await parallel(files, lambda f: agent(
f"Review {f} for misuse of Bar(). Reply in 3 bullets or 'OK'.",
schema={"type": "string"},
role=f"Review {f}",
))
for r in reviews:
log(r)Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

