@conductor-oss/conductor-skills 1.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +20 -0
- package/.claude-plugin/plugin.json +13 -0
- package/LICENSE.txt +176 -0
- package/README.md +352 -0
- package/VERSION +1 -0
- package/bin/conductor-skills.js +135 -0
- package/commands/conductor-optimize.md +18 -0
- package/commands/conductor-scaffold-worker.md +19 -0
- package/commands/conductor-setup.md +15 -0
- package/commands/conductor.md +15 -0
- package/install.ps1 +677 -0
- package/install.sh +855 -0
- package/package.json +50 -0
- package/skills/conductor/SKILL.md +151 -0
- package/skills/conductor/examples/ai-agent-loop.md +119 -0
- package/skills/conductor/examples/ai-agent-mcp.md +129 -0
- package/skills/conductor/examples/create-and-run-workflow.md +50 -0
- package/skills/conductor/examples/do-while-loop.md +72 -0
- package/skills/conductor/examples/fork-join.md +52 -0
- package/skills/conductor/examples/llm-chat.md +61 -0
- package/skills/conductor/examples/llm-rag.md +115 -0
- package/skills/conductor/examples/monitor-and-retry.md +54 -0
- package/skills/conductor/examples/review-workflow.md +67 -0
- package/skills/conductor/examples/signal-wait-task.md +36 -0
- package/skills/conductor/examples/sub-workflow.md +52 -0
- package/skills/conductor/examples/workflows/ai-agent-loop.json +88 -0
- package/skills/conductor/examples/workflows/ai-agent-mcp.json +69 -0
- package/skills/conductor/examples/workflows/child-normalize.json +21 -0
- package/skills/conductor/examples/workflows/do-while-loop.json +35 -0
- package/skills/conductor/examples/workflows/fork-join.json +61 -0
- package/skills/conductor/examples/workflows/llm-chat.json +28 -0
- package/skills/conductor/examples/workflows/llm-rag.json +49 -0
- package/skills/conductor/examples/workflows/parent-pipeline.json +35 -0
- package/skills/conductor/examples/workflows/weather-notification.json +42 -0
- package/skills/conductor/references/api-reference.md +111 -0
- package/skills/conductor/references/cli-index.md +92 -0
- package/skills/conductor/references/fallback-cli.md +36 -0
- package/skills/conductor/references/optimization.md +148 -0
- package/skills/conductor/references/orkes.md +57 -0
- package/skills/conductor/references/schedules.md +81 -0
- package/skills/conductor/references/setup.md +126 -0
- package/skills/conductor/references/troubleshooting.md +35 -0
- package/skills/conductor/references/visualization.md +49 -0
- package/skills/conductor/references/workers.md +227 -0
- package/skills/conductor/references/workflow-definition.md +672 -0
- package/skills/conductor/scripts/conductor_api.py +396 -0
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@conductor-oss/conductor-skills",
|
|
3
|
+
"version": "1.4.2",
|
|
4
|
+
"description": "Conductor workflow orchestration skill for AI coding agents — Claude Code, Cursor, Codex, Gemini, Cline, Copilot, and more.",
|
|
5
|
+
"bin": {
|
|
6
|
+
"conductor-skills": "bin/conductor-skills.js"
|
|
7
|
+
},
|
|
8
|
+
"files": [
|
|
9
|
+
"bin/",
|
|
10
|
+
"skills/",
|
|
11
|
+
"commands/",
|
|
12
|
+
".claude-plugin/",
|
|
13
|
+
"install.sh",
|
|
14
|
+
"install.ps1",
|
|
15
|
+
"VERSION",
|
|
16
|
+
"README.md",
|
|
17
|
+
"LICENSE.txt"
|
|
18
|
+
],
|
|
19
|
+
"engines": {
|
|
20
|
+
"node": ">=18"
|
|
21
|
+
},
|
|
22
|
+
"scripts": {
|
|
23
|
+
"validate": "python3 scripts/validate_plugin.py",
|
|
24
|
+
"test": "python3 scripts/validate_plugin.py"
|
|
25
|
+
},
|
|
26
|
+
"repository": {
|
|
27
|
+
"type": "git",
|
|
28
|
+
"url": "git+https://github.com/conductor-oss/conductor-skills.git"
|
|
29
|
+
},
|
|
30
|
+
"homepage": "https://github.com/conductor-oss/conductor-skills",
|
|
31
|
+
"bugs": {
|
|
32
|
+
"url": "https://github.com/conductor-oss/conductor-skills/issues"
|
|
33
|
+
},
|
|
34
|
+
"license": "Apache-2.0",
|
|
35
|
+
"keywords": [
|
|
36
|
+
"claude-code",
|
|
37
|
+
"claude-code-plugin",
|
|
38
|
+
"ai-agent",
|
|
39
|
+
"ai-skill",
|
|
40
|
+
"conductor",
|
|
41
|
+
"conductor-oss",
|
|
42
|
+
"orkes",
|
|
43
|
+
"workflow",
|
|
44
|
+
"orchestration",
|
|
45
|
+
"microservices"
|
|
46
|
+
],
|
|
47
|
+
"publishConfig": {
|
|
48
|
+
"access": "public"
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: conductor
|
|
3
|
+
description: "Create, run, monitor, manage, and review Conductor workflows and tasks. Use when the user wants to define workflows, start executions, check status, pause/resume/terminate/retry workflows, signal tasks, schedule recurring runs, or review/optimize an existing workflow. Uses the `conductor` CLI or falls back to bundled REST API script. Requires a reachable Conductor server — auto-detected for a local `conductor server start`, otherwise set `CONDUCTOR_SERVER_URL`."
|
|
4
|
+
allowed-tools: Bash(conductor *), Bash(npx *conductor*), Bash(python3 *conductor_api.py*), Bash(npm install *), Bash(chmod *), Bash(* --version), Bash(* --help), Bash(echo *), Read, Write, Edit, Grep, Glob
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Conductor Workflows
|
|
8
|
+
|
|
9
|
+
## What this skill does
|
|
10
|
+
|
|
11
|
+
When asked what you can help with, enumerate these nine areas — every one is covered by this skill and the references it links to:
|
|
12
|
+
|
|
13
|
+
1. **Create** workflow definitions (any task type: SIMPLE, HTTP, SWITCH, FORK_JOIN, DO_WHILE, WAIT, SUB_WORKFLOW, LLM_*, MCP, etc.)
|
|
14
|
+
2. **Run** executions — sync or async, with file or inline input, by version, with correlation ID
|
|
15
|
+
3. **Monitor** — search by status / name / time, fetch execution details, diagnose failures
|
|
16
|
+
4. **Manage** — pause, resume, terminate, restart, retry, rerun, skip-task, jump
|
|
17
|
+
5. **Signal** — advance WAIT / HUMAN tasks (sync or async) with structured output
|
|
18
|
+
6. **Schedule** — Quartz-cron schedules (part of OSS, not Orkes-only)
|
|
19
|
+
7. **Scaffold workers** in Python, JavaScript / TypeScript, Java, Go (referral to upstream SDKs for C# / Ruby / Rust)
|
|
20
|
+
8. **Visualize** — render any workflow as a Mermaid flowchart + UI link
|
|
21
|
+
9. **Review & optimize** — walk the 19-rule checklist in [references/optimization.md](references/optimization.md) and report CRITICAL / WARN / INFO
|
|
22
|
+
|
|
23
|
+
Plus Orkes-only: **secrets** and **webhooks**.
|
|
24
|
+
|
|
25
|
+
## Rules
|
|
26
|
+
|
|
27
|
+
1. **Worker gate (most important).** Every time you create *or* update a workflow, list its `SIMPLE` tasks and verify each has a registered task definition (`conductor taskDef list`). For any unregistered SIMPLE task, **tell the user** the workflow will hang on that step and offer to (a) create the task definition or (b) scaffold a worker (see [references/workers.md](references/workers.md)). This applies to every workflow create/update — not just first-time setup.
|
|
28
|
+
2. **Never use `python3 -c` to construct, parse, format, or post-process Conductor data.** Write JSON to files via the Write tool or heredoc; format command output yourself in plain text. You can read JSON — don't spawn Python to interpret it. (Validation utilities run from script files are fine.)
|
|
29
|
+
3. **CLI resolution order — STOP and ASK before any global install.**
|
|
30
|
+
1. If `conductor` is on PATH, use it directly.
|
|
31
|
+
2. Otherwise prefer `npx @conductor-oss/conductor-cli ...` for one-off use (no system change).
|
|
32
|
+
3. **NEVER run `npm install -g @conductor-oss/conductor-cli` without first asking the user.** Phrase it explicitly: *"OK to globally install `@conductor-oss/conductor-cli` via npm? It modifies your global node_modules."* Wait for a yes.
|
|
33
|
+
4. Only after `conductor` and `npm` are both unavailable, fall back to `scripts/conductor_api.py`. If the user has stated upfront that Node/npm cannot be installed, note that constraint and go straight to the fallback — no need to retry npm. See [references/setup.md](references/setup.md).
|
|
34
|
+
4. **Use `--json` flags** when available; format the parsed result yourself.
|
|
35
|
+
5. **Never echo auth tokens, keys, or secrets.** Set them via env vars (`CONDUCTOR_AUTH_KEY`, `CONDUCTOR_AUTH_SECRET`, or `CONDUCTOR_AUTH_TOKEN`). Confirm credentials by name in output, never by value.
|
|
36
|
+
|
|
37
|
+
## Slash commands
|
|
38
|
+
|
|
39
|
+
Three structured procedures are exposed as slash commands. The skill itself handles everything else through natural language.
|
|
40
|
+
|
|
41
|
+
| Command | Purpose |
|
|
42
|
+
|---------|---------|
|
|
43
|
+
| `/conductor` | Menu — lists subcommands, shows examples of natural-language requests |
|
|
44
|
+
| `/conductor-setup` | First-time setup (CLI install, server, auth) |
|
|
45
|
+
| `/conductor-optimize` | Review an existing workflow against the optimization checklist |
|
|
46
|
+
| `/conductor-scaffold-worker` | Generate a worker stub in any supported language |
|
|
47
|
+
|
|
48
|
+
Anything else (run, status, schedule, pause, retry, signal, visualize, create) is fluid through plain English — no command needed.
|
|
49
|
+
|
|
50
|
+
## Setup check
|
|
51
|
+
|
|
52
|
+
If the user has nothing set up, walk them through **[references/setup.md](references/setup.md)** Steps 1–4. To verify a working environment:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
conductor --version # CLI present?
|
|
56
|
+
conductor workflow list # Server reachable?
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Commands
|
|
60
|
+
|
|
61
|
+
Full verb-to-CLI lookup is in **[references/cli-index.md](references/cli-index.md)**. Python fallback equivalents (when neither `conductor` nor `npx` is available) are in **[references/fallback-cli.md](references/fallback-cli.md)**. Schedules (OSS) are in **[references/schedules.md](references/schedules.md)**. Enterprise commands (secrets, webhooks) are in **[references/orkes.md](references/orkes.md)**.
|
|
62
|
+
|
|
63
|
+
## Creating workflows
|
|
64
|
+
|
|
65
|
+
1. Consult **[references/workflow-definition.md](references/workflow-definition.md)** for task types, the `${...}` expression syntax, and the `$.var` rule for JS-evaluated tasks (INLINE, DO_WHILE, SWITCH/javascript).
|
|
66
|
+
2. Write the JSON to a file with the Write tool, then `conductor workflow create file.json`.
|
|
67
|
+
3. **Run the worker gate** (Rule 1).
|
|
68
|
+
|
|
69
|
+
For inputs to workflow start: use `-i '{"...":"..."}'` for small inline JSON, `-f input.json` for larger payloads.
|
|
70
|
+
|
|
71
|
+
## Examples
|
|
72
|
+
|
|
73
|
+
Operational:
|
|
74
|
+
|
|
75
|
+
- [Create and run](examples/create-and-run-workflow.md) — define, register, execute end-to-end.
|
|
76
|
+
- [Monitor and retry](examples/monitor-and-retry.md) — search failures, distinguish retryable from terminal, batch retry.
|
|
77
|
+
- [Signal a WAIT task](examples/signal-wait-task.md) — human-in-the-loop signaling.
|
|
78
|
+
- [Review and optimize a workflow](examples/review-workflow.md) — apply the optimization checklist.
|
|
79
|
+
|
|
80
|
+
Design patterns:
|
|
81
|
+
|
|
82
|
+
- [FORK_JOIN parallel branches](examples/fork-join.md)
|
|
83
|
+
- [DO_WHILE loop with iteration counter](examples/do-while-loop.md)
|
|
84
|
+
- [SUB_WORKFLOW composition](examples/sub-workflow.md)
|
|
85
|
+
|
|
86
|
+
AI / LLM patterns:
|
|
87
|
+
|
|
88
|
+
- [Minimum LLM workflow](examples/llm-chat.md) — single `LLM_CHAT_COMPLETE` task.
|
|
89
|
+
- [AI agent with MCP tools](examples/ai-agent-mcp.md) — `LIST_MCP_TOOLS` → plan → `CALL_MCP_TOOL` → summarize.
|
|
90
|
+
- [Autonomous agent loop (ReAct)](examples/ai-agent-loop.md) — `DO_WHILE` think/act/observe until done.
|
|
91
|
+
- [RAG — retrieval-augmented Q&A](examples/llm-rag.md) — `LLM_SEARCH_INDEX` then grounded `LLM_CHAT_COMPLETE`.
|
|
92
|
+
|
|
93
|
+
Raw definitions in [examples/workflows/](examples/workflows/) — pass any directly to `conductor workflow create`:
|
|
94
|
+
|
|
95
|
+
| File | Pattern |
|
|
96
|
+
|------|---------|
|
|
97
|
+
| `weather-notification.json` | Two HTTP tasks in sequence, output chaining |
|
|
98
|
+
| `fork-join.json` | Parallel branches with JOIN + JQ merge |
|
|
99
|
+
| `do-while-loop.json` | DO_WHILE with iteration counter (self-reference pattern) |
|
|
100
|
+
| `child-normalize.json` | Reusable child workflow (JQ transform) |
|
|
101
|
+
| `parent-pipeline.json` | SUB_WORKFLOW composing the child above |
|
|
102
|
+
| `llm-chat.json` | Single LLM_CHAT_COMPLETE — summarize text |
|
|
103
|
+
| `ai-agent-mcp.json` | 4-task AI agent: list tools → plan → call → summarize |
|
|
104
|
+
| `ai-agent-loop.json` | DO_WHILE agent loop, ReAct pattern |
|
|
105
|
+
| `llm-rag.json` | RAG: vector search + grounded LLM answer |
|
|
106
|
+
|
|
107
|
+
## Reviewing workflows
|
|
108
|
+
|
|
109
|
+
When the user asks to **review**, **optimize**, **simplify**, or **audit** a workflow:
|
|
110
|
+
|
|
111
|
+
1. Load the definition (file path, or `conductor workflow get {name}`).
|
|
112
|
+
2. For each `SIMPLE` task, also load its task definition (`conductor taskDef get {name}`) — timeouts and retry config live there, not on the workflow task.
|
|
113
|
+
3. Walk the checklist in **[references/optimization.md](references/optimization.md)**, grouping findings as **CRITICAL** / **WARN** / **INFO**.
|
|
114
|
+
4. Offer fixes one at a time. Don't apply changes silently.
|
|
115
|
+
|
|
116
|
+
Worked example: [examples/review-workflow.md](examples/review-workflow.md).
|
|
117
|
+
|
|
118
|
+
## Visualizing workflows
|
|
119
|
+
|
|
120
|
+
When the user asks to visualize a workflow, or after creating one, generate a Mermaid flowchart. Construct mappings (FORK_JOIN, SWITCH, DO_WHILE, etc.) and rules are in **[references/visualization.md](references/visualization.md)**. If a server is reachable, also offer the UI link `{BASE_URL}/workflowDef/{name}` (resolve `BASE_URL` from `CONDUCTOR_SERVER_URL` by stripping `/api`).
|
|
121
|
+
|
|
122
|
+
## Workers
|
|
123
|
+
|
|
124
|
+
When the user asks to write a worker:
|
|
125
|
+
|
|
126
|
+
1. Ask which language (Python, JS/TS, Java, Go, C#, Ruby, Rust).
|
|
127
|
+
2. Install the SDK and scaffold from the pattern in **[references/workers.md](references/workers.md)**.
|
|
128
|
+
3. The worker's task type **must** match the `name` of the SIMPLE task in the workflow.
|
|
129
|
+
4. Note in code that workers must be idempotent — Conductor may redeliver on failure or timeout.
|
|
130
|
+
|
|
131
|
+
## Output
|
|
132
|
+
|
|
133
|
+
- Render workflow data as structured summaries (`workflowId`, `status`, `startTime`, `endTime`, failed-task name + reason + retry count).
|
|
134
|
+
- Render searches as a table (`workflowId`, `name`, `status`, `startTime`).
|
|
135
|
+
- For more on output, error decoding, and stuck-workflow diagnosis, see **[references/troubleshooting.md](references/troubleshooting.md)**.
|
|
136
|
+
|
|
137
|
+
## References at a glance
|
|
138
|
+
|
|
139
|
+
| File | Purpose |
|
|
140
|
+
|------|---------|
|
|
141
|
+
| [setup.md](references/setup.md) | Install CLI, configure server, auth, profiles |
|
|
142
|
+
| [cli-index.md](references/cli-index.md) | Verb → CLI command lookup |
|
|
143
|
+
| [fallback-cli.md](references/fallback-cli.md) | Python fallback equivalents (subset of CLI) |
|
|
144
|
+
| [workflow-definition.md](references/workflow-definition.md) | JSON schema, all task types, expression syntax |
|
|
145
|
+
| [workers.md](references/workers.md) | SDK examples in 7 languages |
|
|
146
|
+
| [api-reference.md](references/api-reference.md) | REST endpoints |
|
|
147
|
+
| [visualization.md](references/visualization.md) | Mermaid mappings + UI link |
|
|
148
|
+
| [schedules.md](references/schedules.md) | Cron schedules (OSS) — schema, format, patterns |
|
|
149
|
+
| [orkes.md](references/orkes.md) | Enterprise (Orkes): secrets, webhooks |
|
|
150
|
+
| [optimization.md](references/optimization.md) | Workflow review/optimize checklist |
|
|
151
|
+
| [troubleshooting.md](references/troubleshooting.md) | Common errors + diagnosis flow |
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# Example: Autonomous Agent Loop (ReAct Pattern)
|
|
2
|
+
|
|
3
|
+
An LLM-driven agent that **thinks**, **acts**, and **observes** in a loop until it decides it's done. Built with `DO_WHILE` wrapping an LLM_CHAT_COMPLETE → SWITCH → CALL_MCP_TOOL inner sequence.
|
|
4
|
+
|
|
5
|
+
Use this when the answer requires multiple tool calls in sequence — e.g., "look up the customer, then their last order, then refund the matching line item." A single-shot agent ([ai-agent-mcp.md](ai-agent-mcp.md)) only plans once; an agent loop replans every iteration with the accumulated tool results in context.
|
|
6
|
+
|
|
7
|
+
## Pipeline
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
DO_WHILE {
|
|
11
|
+
think (LLM_CHAT_COMPLETE) → emits { done, method?, arguments?, answer? }
|
|
12
|
+
act (SWITCH on done) → CALL_MCP_TOOL if not done, NOOP if done
|
|
13
|
+
} until think says done == true
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
The whole loop is one workflow task. Each iteration is a durable checkpoint — a crash mid-tool-call resumes from the last completed iteration **without replaying earlier LLM calls**.
|
|
17
|
+
|
|
18
|
+
## Workflow
|
|
19
|
+
|
|
20
|
+
See [workflows/ai-agent-loop.json](workflows/ai-agent-loop.json). Key tasks:
|
|
21
|
+
|
|
22
|
+
```json
|
|
23
|
+
{
|
|
24
|
+
"name": "agent_loop",
|
|
25
|
+
"taskReferenceName": "loop",
|
|
26
|
+
"type": "DO_WHILE",
|
|
27
|
+
"loopCondition": "if ($.loop['iteration'] < 10 && $.loop[$.loop['iteration']].think.output.result.done != true) { true; } else { false; }",
|
|
28
|
+
"loopOver": [
|
|
29
|
+
{
|
|
30
|
+
"name": "think",
|
|
31
|
+
"taskReferenceName": "think",
|
|
32
|
+
"type": "LLM_CHAT_COMPLETE",
|
|
33
|
+
"inputParameters": {
|
|
34
|
+
"llmProvider": "openai",
|
|
35
|
+
"model": "gpt-4o-mini",
|
|
36
|
+
"messages": [
|
|
37
|
+
{"role": "system", "message": "You are an agent. Tools: ${workflow.input.tools}. Previous results: ${loop.output}. Decide next step. Respond as JSON: { done: bool, method?: string, arguments?: object, answer?: string }."},
|
|
38
|
+
{"role": "user", "message": "${workflow.input.task}"}
|
|
39
|
+
],
|
|
40
|
+
"temperature": 0.1,
|
|
41
|
+
"maxTokens": 500
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"name": "act",
|
|
46
|
+
"taskReferenceName": "act",
|
|
47
|
+
"type": "SWITCH",
|
|
48
|
+
"evaluatorType": "javascript",
|
|
49
|
+
"expression": "$.done ? 'finish' : 'call_tool'",
|
|
50
|
+
"inputParameters": {
|
|
51
|
+
"done": "${think.output.result.done}"
|
|
52
|
+
},
|
|
53
|
+
"decisionCases": {
|
|
54
|
+
"call_tool": [{
|
|
55
|
+
"name": "execute",
|
|
56
|
+
"taskReferenceName": "execute",
|
|
57
|
+
"type": "CALL_MCP_TOOL",
|
|
58
|
+
"inputParameters": {
|
|
59
|
+
"mcpServer": "${workflow.input.mcpServer}",
|
|
60
|
+
"method": "${think.output.result.method}",
|
|
61
|
+
"arguments": "${think.output.result.arguments}"
|
|
62
|
+
}
|
|
63
|
+
}],
|
|
64
|
+
"finish": [{"name": "done", "taskReferenceName": "done", "type": "NOOP"}]
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
],
|
|
68
|
+
"inputParameters": {
|
|
69
|
+
"loop": "${loop.output}"
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Then a single task after the loop pulls out the final answer:
|
|
75
|
+
|
|
76
|
+
```json
|
|
77
|
+
{
|
|
78
|
+
"name": "final_answer",
|
|
79
|
+
"taskReferenceName": "final",
|
|
80
|
+
"type": "INLINE",
|
|
81
|
+
"inputParameters": {
|
|
82
|
+
"evaluatorType": "graaljs",
|
|
83
|
+
"expression": "function e() { var i = $.loop_output['iteration']; return $.loop_output[i].think.output.result.answer; } e();",
|
|
84
|
+
"loop_output": "${loop.output}"
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Run
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
conductor workflow create examples/workflows/ai-agent-loop.json
|
|
93
|
+
conductor workflow start -w autonomous_agent -i '{
|
|
94
|
+
"task": "Find user 42 and refund their last order.",
|
|
95
|
+
"mcpServer": "http://localhost:3001/mcp",
|
|
96
|
+
"tools": "search_user, get_orders, refund_order"
|
|
97
|
+
}' --sync
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## The `$.loop_ref` self-reference pattern
|
|
101
|
+
|
|
102
|
+
`"loop": "${loop.output}"` in `inputParameters` wires the loop task's own output back into its `loopCondition` scope. This looks like a typo — referencing a task before it has output — but it's the canonical pattern. Conductor resolves it lazily at each iteration, exposing `iteration` and all prior iterations' task outputs (`$.loop[1].think.output...`, `$.loop[2].think.output...`).
|
|
103
|
+
|
|
104
|
+
See [do-while-loop.md](do-while-loop.md) for more detail on this pattern.
|
|
105
|
+
|
|
106
|
+
## Critical guardrails
|
|
107
|
+
|
|
108
|
+
- **Cap iterations.** The condition above includes `$.loop['iteration'] < 10` as a hard cap. Without it, a buggy "I'm not done yet" reply spins forever and burns LLM budget. The optimization checklist ([../references/optimization.md](../references/optimization.md)) flags unbounded loops as **CRITICAL** (rule B5).
|
|
109
|
+
- **Set a workflow timeout** (`timeoutSeconds` + `timeoutPolicy: TIME_OUT_WF`). A 10-iteration cap with no per-iteration timeout can still hang on a slow tool call.
|
|
110
|
+
- **Token budget per iteration is enforced by `maxTokens`** — the loop itself has no token budget. For cost control, multiply: 10 iterations × 500 max tokens × $/token.
|
|
111
|
+
|
|
112
|
+
## When to use the loop vs the single-shot
|
|
113
|
+
|
|
114
|
+
| Use single-shot ([ai-agent-mcp.md](ai-agent-mcp.md)) | Use the loop |
|
|
115
|
+
|------------------------------------------------------|--------------|
|
|
116
|
+
| Answer always needs exactly one tool call | Answer needs an unknown number of tool calls |
|
|
117
|
+
| You can constrain the model to pick ONE action | Tasks chain — output of one tool informs the next |
|
|
118
|
+
| You want strict, audit-friendly determinism | Some exploration is acceptable |
|
|
119
|
+
| Latency matters (one LLM call + one tool call) | Total budget tolerates 5–10 LLM round trips |
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# Example: AI Agent with MCP Tools
|
|
2
|
+
|
|
3
|
+
A 4-task agent that discovers tools from an MCP server, plans an action with an LLM, calls the chosen tool, then summarizes the result for the user. This is the canonical "first AI agent" pattern from the [Orkes docs](https://docs.conductor-oss.org/devguide/ai/first-ai-agent.html).
|
|
4
|
+
|
|
5
|
+
## Pipeline
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
LIST_MCP_TOOLS → LLM_CHAT_COMPLETE (plan) → CALL_MCP_TOOL → LLM_CHAT_COMPLETE (summarize)
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
The agent is **linear, deterministic, durable**: every task is a Conductor checkpoint. If the tool call or the summarizer crashes, the workflow resumes from the last completed task — no replay of the planning LLM call.
|
|
12
|
+
|
|
13
|
+
## Prerequisites
|
|
14
|
+
|
|
15
|
+
1. **An MCP server** reachable from Conductor. For local dev: `mcp-testkit --transport http` listens on `http://localhost:3001/mcp`.
|
|
16
|
+
2. **An LLM provider** with its API key set on the Conductor server:
|
|
17
|
+
```bash
|
|
18
|
+
export OPENAI_API_KEY=sk-...
|
|
19
|
+
# or
|
|
20
|
+
export ANTHROPIC_API_KEY=sk-ant-...
|
|
21
|
+
```
|
|
22
|
+
Conductor auto-enables providers when their API key is set — no separate registration in OSS.
|
|
23
|
+
|
|
24
|
+
## Workflow
|
|
25
|
+
|
|
26
|
+
See [workflows/ai-agent-mcp.json](workflows/ai-agent-mcp.json). Key tasks:
|
|
27
|
+
|
|
28
|
+
```json
|
|
29
|
+
{
|
|
30
|
+
"name": "discover_tools",
|
|
31
|
+
"taskReferenceName": "discover",
|
|
32
|
+
"type": "LIST_MCP_TOOLS",
|
|
33
|
+
"inputParameters": {
|
|
34
|
+
"mcpServer": "http://localhost:3001/mcp"
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"name": "plan_action",
|
|
39
|
+
"taskReferenceName": "plan",
|
|
40
|
+
"type": "LLM_CHAT_COMPLETE",
|
|
41
|
+
"inputParameters": {
|
|
42
|
+
"llmProvider": "openai",
|
|
43
|
+
"model": "gpt-4o-mini",
|
|
44
|
+
"messages": [
|
|
45
|
+
{"role": "system", "message": "You are an AI agent. Available tools: ${discover.output.tools}. Pick exactly one tool and respond as JSON with fields `method` and `arguments`."},
|
|
46
|
+
{"role": "user", "message": "${workflow.input.task}"}
|
|
47
|
+
],
|
|
48
|
+
"temperature": 0.1,
|
|
49
|
+
"maxTokens": 500
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"name": "execute_tool",
|
|
54
|
+
"taskReferenceName": "execute",
|
|
55
|
+
"type": "CALL_MCP_TOOL",
|
|
56
|
+
"inputParameters": {
|
|
57
|
+
"mcpServer": "http://localhost:3001/mcp",
|
|
58
|
+
"method": "${plan.output.result.method}",
|
|
59
|
+
"arguments": "${plan.output.result.arguments}"
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
"name": "summarize_result",
|
|
64
|
+
"taskReferenceName": "summarize",
|
|
65
|
+
"type": "LLM_CHAT_COMPLETE",
|
|
66
|
+
"inputParameters": {
|
|
67
|
+
"llmProvider": "openai",
|
|
68
|
+
"model": "gpt-4o-mini",
|
|
69
|
+
"messages": [
|
|
70
|
+
{"role": "user", "message": "The user asked: \"${workflow.input.task}\". Tool returned: ${execute.output.content}. Reply in one short paragraph."}
|
|
71
|
+
],
|
|
72
|
+
"maxTokens": 500
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Run
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
conductor workflow create examples/workflows/ai-agent-mcp.json
|
|
81
|
+
conductor workflow start -w my_first_agent -i '{"task": "What is the weather in San Francisco?"}' --sync
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Or hit the REST API directly for synchronous execution:
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
curl -s -X POST 'http://localhost:8080/api/workflow/execute/my_first_agent/1' \
|
|
88
|
+
-H 'Content-Type: application/json' \
|
|
89
|
+
-d '{"task": "What is the weather in San Francisco?"}' | jq
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Output
|
|
93
|
+
|
|
94
|
+
```
|
|
95
|
+
${plan.output.result} → the LLM's chosen action (parsed JSON: method + arguments)
|
|
96
|
+
${execute.output.content} → raw output from the MCP tool
|
|
97
|
+
${summarize.output.result} → final natural-language answer for the user
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Adding human-in-the-loop
|
|
101
|
+
|
|
102
|
+
Insert a `HUMAN` task between `plan` and `execute` to require approval before any tool call:
|
|
103
|
+
|
|
104
|
+
```json
|
|
105
|
+
{
|
|
106
|
+
"name": "approve",
|
|
107
|
+
"taskReferenceName": "approve",
|
|
108
|
+
"type": "HUMAN",
|
|
109
|
+
"inputParameters": {
|
|
110
|
+
"plannedAction": "${plan.output.result}",
|
|
111
|
+
"userTask": "${workflow.input.task}"
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
The workflow pauses indefinitely until signaled. Approve via:
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
conductor task signal-sync --workflow-id {id} --task-ref approve --status COMPLETED --output '{"approved": true}'
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
See [signal-wait-task.md](signal-wait-task.md) for the signaling pattern.
|
|
123
|
+
|
|
124
|
+
## Patterns
|
|
125
|
+
|
|
126
|
+
- **Two LLM calls, two purposes.** First call plans (deterministic, low-temperature, JSON-shaped output). Second call summarizes (natural language for the user). Splitting them lets you swap models per role — small model for planning, larger for summary, or vice versa.
|
|
127
|
+
- **JSON-shaped LLM output.** `${plan.output.result.method}` works because Conductor parses the LLM response as JSON when the model emits one. Make the system prompt require JSON. Use `temperature: 0.1` to keep it deterministic.
|
|
128
|
+
- **Durable checkpoints.** Each task is a save point. A crashed MCP call resumes without re-running the planner.
|
|
129
|
+
- **Tool surface from `LIST_MCP_TOOLS`.** The agent learns its capabilities at runtime — change the MCP server's exposed tools and the workflow adapts without redeploy.
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Example: Create and Run a Workflow
|
|
2
|
+
|
|
3
|
+
> "Create a workflow that calls a weather API and sends a notification, then run it."
|
|
4
|
+
|
|
5
|
+
## Steps
|
|
6
|
+
|
|
7
|
+
1. Verify CLI: `conductor --version`. Verify `CONDUCTOR_SERVER_URL` (or that a local server is running). See [setup.md](../references/setup.md).
|
|
8
|
+
2. Consult [workflow-definition.md](../references/workflow-definition.md) for task types.
|
|
9
|
+
3. Write the definition to a file and register it.
|
|
10
|
+
4. Start the workflow.
|
|
11
|
+
5. Check execution status.
|
|
12
|
+
6. Run the worker gate (Rule 1 in [SKILL.md](../SKILL.md)) — for this example, no SIMPLE tasks, so nothing to scaffold.
|
|
13
|
+
|
|
14
|
+
## Definition
|
|
15
|
+
|
|
16
|
+
See [workflows/weather-notification.json](workflows/weather-notification.json). Two HTTP tasks: fetch weather, then post a notification using the result.
|
|
17
|
+
|
|
18
|
+
```json
|
|
19
|
+
{
|
|
20
|
+
"name": "send_notification", "taskReferenceName": "send_notification", "type": "HTTP",
|
|
21
|
+
"inputParameters": {
|
|
22
|
+
"http_request": {
|
|
23
|
+
"uri": "https://api.notify.example.com/send",
|
|
24
|
+
"method": "POST",
|
|
25
|
+
"body": {
|
|
26
|
+
"to": "${workflow.input.notifyEmail}",
|
|
27
|
+
"message": "Weather in ${workflow.input.city}: ${fetch_weather.output.response.body.temperature}°F"
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Run
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
conductor workflow create examples/workflows/weather-notification.json
|
|
38
|
+
conductor workflow start -w weather_notification -i '{"city": "San Francisco", "notifyEmail": "user@example.com"}'
|
|
39
|
+
# → returns workflowId
|
|
40
|
+
conductor workflow get-execution {workflowId} -c
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Expected status: `COMPLETED`. Output: `${fetch_weather.output.response.body}` and `${send_notification.output.response.statusCode}`.
|
|
44
|
+
|
|
45
|
+
## Patterns demonstrated
|
|
46
|
+
|
|
47
|
+
- HTTP task for external APIs.
|
|
48
|
+
- Workflow input parameterization via `${workflow.input.x}`.
|
|
49
|
+
- Task-to-task data passing via `${taskRef.output.x}`.
|
|
50
|
+
- `outputParameters` for aggregating results.
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# Example: DO_WHILE Loop with Iteration Counter
|
|
2
|
+
|
|
3
|
+
Loop a body of tasks until a JavaScript condition returns false. The body sees an auto-managed `iteration` counter on the loop task's output.
|
|
4
|
+
|
|
5
|
+
## The self-reference pattern
|
|
6
|
+
|
|
7
|
+
The `loopCondition` is JavaScript. Variables in the script are read from the task's `inputParameters` via `$.varName`. To read the iteration counter, you must wire the loop task's *own output* back into its inputs:
|
|
8
|
+
|
|
9
|
+
```json
|
|
10
|
+
"inputParameters": {
|
|
11
|
+
"value": "${workflow.input.count}",
|
|
12
|
+
"loop_ref": "${loop_ref.output}"
|
|
13
|
+
}
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
This looks like a typo — referencing the task before it has output — but it's the canonical pattern. Conductor resolves `${loop_ref.output}` lazily at each iteration, exposing the running counter as `$.loop_ref['iteration']` inside the script.
|
|
17
|
+
|
|
18
|
+
## Workflow
|
|
19
|
+
|
|
20
|
+
See [workflows/do-while-loop.json](workflows/do-while-loop.json):
|
|
21
|
+
|
|
22
|
+
```json
|
|
23
|
+
{
|
|
24
|
+
"name": "loop",
|
|
25
|
+
"taskReferenceName": "loop_ref",
|
|
26
|
+
"type": "DO_WHILE",
|
|
27
|
+
"loopCondition": "if ($.loop_ref['iteration'] < $.value) { true; } else { false; }",
|
|
28
|
+
"loopOver": [
|
|
29
|
+
{
|
|
30
|
+
"name": "do_work",
|
|
31
|
+
"taskReferenceName": "do_work",
|
|
32
|
+
"type": "HTTP",
|
|
33
|
+
"inputParameters": {
|
|
34
|
+
"http_request": {
|
|
35
|
+
"uri": "${workflow.input.endpoint}?page=${loop_ref.output.iteration}",
|
|
36
|
+
"method": "GET"
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
],
|
|
41
|
+
"inputParameters": {
|
|
42
|
+
"value": "${workflow.input.count}",
|
|
43
|
+
"loop_ref": "${loop_ref.output}"
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Run
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
conductor workflow create examples/workflows/do-while-loop.json
|
|
52
|
+
conductor workflow start -w paginated_fetch -i '{"endpoint": "https://api.example.com/items", "count": 5}'
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Reading per-iteration output
|
|
56
|
+
|
|
57
|
+
Each iteration's task output is keyed by iteration number under the loop task:
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
${loop_ref.output.1.do_work.response.body}
|
|
61
|
+
${loop_ref.output.2.do_work.response.body}
|
|
62
|
+
...
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
The total iteration count is at `${loop_ref.output.iteration}`.
|
|
66
|
+
|
|
67
|
+
## Notes
|
|
68
|
+
|
|
69
|
+
- `iteration` is 1-indexed.
|
|
70
|
+
- The condition uses semicolon-terminated JS — Conductor's evaluator is strict.
|
|
71
|
+
- Every variable referenced as `$.x` in the condition must appear as a key in `inputParameters`. Forgetting this gives a confusing "undefined" failure at runtime.
|
|
72
|
+
- For unbounded loops, guard with both an iteration cap and a result-driven condition (e.g. `iteration < 100 && !$.loop_ref[iteration].do_work.output.done`).
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Example: Parallel Branches with FORK_JOIN
|
|
2
|
+
|
|
3
|
+
Run independent tasks in parallel, then converge before continuing. Always pair `FORK_JOIN` with a `JOIN` that lists every parallel branch's terminal `taskReferenceName`.
|
|
4
|
+
|
|
5
|
+
## Pattern
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
┌─→ branch_a_task ─┐
|
|
9
|
+
fork ──┤ ├─→ join ─→ next
|
|
10
|
+
└─→ branch_b_task ─┘
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Workflow
|
|
14
|
+
|
|
15
|
+
See [workflows/fork-join.json](workflows/fork-join.json). Key tasks:
|
|
16
|
+
|
|
17
|
+
```json
|
|
18
|
+
{
|
|
19
|
+
"name": "fork", "taskReferenceName": "fork", "type": "FORK_JOIN",
|
|
20
|
+
"forkTasks": [
|
|
21
|
+
[{"name": "fetch_inventory", "taskReferenceName": "inventory", "type": "HTTP", "inputParameters": {"http_request": {"uri": "${workflow.input.inventoryUrl}", "method": "GET"}}}],
|
|
22
|
+
[{"name": "fetch_pricing", "taskReferenceName": "pricing", "type": "HTTP", "inputParameters": {"http_request": {"uri": "${workflow.input.pricingUrl}", "method": "GET"}}}]
|
|
23
|
+
]
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"name": "join", "taskReferenceName": "join", "type": "JOIN",
|
|
27
|
+
"joinOn": ["inventory", "pricing"]
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Run
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
conductor workflow create examples/workflows/fork-join.json
|
|
35
|
+
conductor workflow start -w parallel_fetch -i '{"inventoryUrl": "https://...", "pricingUrl": "https://..."}'
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Reading the output
|
|
39
|
+
|
|
40
|
+
Each branch's output is available as `${branchRef.output...}` in tasks after the JOIN. The JOIN task's own output aggregates branch outputs by reference name:
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
${join.output.inventory.response.body}
|
|
44
|
+
${join.output.pricing.response.body}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Notes
|
|
48
|
+
|
|
49
|
+
- Branches in `forkTasks` are arrays of arrays — one inner array per parallel branch. Each inner array can itself be a sequence of tasks.
|
|
50
|
+
- `joinOn` must list the **last** task's `taskReferenceName` from each branch.
|
|
51
|
+
- Failure in any branch fails the JOIN unless every task in that branch is `optional: true`.
|
|
52
|
+
- For dynamic branch counts, use `FORK_JOIN_DYNAMIC` (see workflow-definition.md).
|