@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.
Files changed (46) hide show
  1. package/.claude-plugin/marketplace.json +20 -0
  2. package/.claude-plugin/plugin.json +13 -0
  3. package/LICENSE.txt +176 -0
  4. package/README.md +352 -0
  5. package/VERSION +1 -0
  6. package/bin/conductor-skills.js +135 -0
  7. package/commands/conductor-optimize.md +18 -0
  8. package/commands/conductor-scaffold-worker.md +19 -0
  9. package/commands/conductor-setup.md +15 -0
  10. package/commands/conductor.md +15 -0
  11. package/install.ps1 +677 -0
  12. package/install.sh +855 -0
  13. package/package.json +50 -0
  14. package/skills/conductor/SKILL.md +151 -0
  15. package/skills/conductor/examples/ai-agent-loop.md +119 -0
  16. package/skills/conductor/examples/ai-agent-mcp.md +129 -0
  17. package/skills/conductor/examples/create-and-run-workflow.md +50 -0
  18. package/skills/conductor/examples/do-while-loop.md +72 -0
  19. package/skills/conductor/examples/fork-join.md +52 -0
  20. package/skills/conductor/examples/llm-chat.md +61 -0
  21. package/skills/conductor/examples/llm-rag.md +115 -0
  22. package/skills/conductor/examples/monitor-and-retry.md +54 -0
  23. package/skills/conductor/examples/review-workflow.md +67 -0
  24. package/skills/conductor/examples/signal-wait-task.md +36 -0
  25. package/skills/conductor/examples/sub-workflow.md +52 -0
  26. package/skills/conductor/examples/workflows/ai-agent-loop.json +88 -0
  27. package/skills/conductor/examples/workflows/ai-agent-mcp.json +69 -0
  28. package/skills/conductor/examples/workflows/child-normalize.json +21 -0
  29. package/skills/conductor/examples/workflows/do-while-loop.json +35 -0
  30. package/skills/conductor/examples/workflows/fork-join.json +61 -0
  31. package/skills/conductor/examples/workflows/llm-chat.json +28 -0
  32. package/skills/conductor/examples/workflows/llm-rag.json +49 -0
  33. package/skills/conductor/examples/workflows/parent-pipeline.json +35 -0
  34. package/skills/conductor/examples/workflows/weather-notification.json +42 -0
  35. package/skills/conductor/references/api-reference.md +111 -0
  36. package/skills/conductor/references/cli-index.md +92 -0
  37. package/skills/conductor/references/fallback-cli.md +36 -0
  38. package/skills/conductor/references/optimization.md +148 -0
  39. package/skills/conductor/references/orkes.md +57 -0
  40. package/skills/conductor/references/schedules.md +81 -0
  41. package/skills/conductor/references/setup.md +126 -0
  42. package/skills/conductor/references/troubleshooting.md +35 -0
  43. package/skills/conductor/references/visualization.md +49 -0
  44. package/skills/conductor/references/workers.md +227 -0
  45. package/skills/conductor/references/workflow-definition.md +672 -0
  46. package/skills/conductor/scripts/conductor_api.py +396 -0
@@ -0,0 +1,61 @@
1
+ # Example: Minimum LLM Workflow
2
+
3
+ The smallest useful AI workflow — one `LLM_CHAT_COMPLETE` task. Useful as a building block for prompts that don't need tools, retrieval, or loops.
4
+
5
+ > Conductor auto-enables LLM providers when their API key is set in the server's environment (`OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, etc.). No separate provider registration needed in OSS.
6
+
7
+ ## Workflow
8
+
9
+ See [workflows/llm-chat.json](workflows/llm-chat.json):
10
+
11
+ ```json
12
+ {
13
+ "name": "summarize_text",
14
+ "tasks": [{
15
+ "name": "summarize",
16
+ "taskReferenceName": "summarize",
17
+ "type": "LLM_CHAT_COMPLETE",
18
+ "inputParameters": {
19
+ "llmProvider": "openai",
20
+ "model": "gpt-4o-mini",
21
+ "messages": [
22
+ {"role": "system", "message": "You summarize text in one sentence."},
23
+ {"role": "user", "message": "${workflow.input.text}"}
24
+ ],
25
+ "temperature": 0.3,
26
+ "maxTokens": 200
27
+ }
28
+ }],
29
+ "outputParameters": {
30
+ "summary": "${summarize.output.result}",
31
+ "tokensUsed": "${summarize.output.tokenUsed}"
32
+ }
33
+ }
34
+ ```
35
+
36
+ ## Run
37
+
38
+ ```bash
39
+ conductor workflow create examples/workflows/llm-chat.json
40
+ conductor workflow start -w summarize_text -i '{"text": "Conductor is a workflow orchestration platform. It supports SIMPLE tasks, HTTP, SWITCH, FORK_JOIN, AI tasks, and more. Workflows are durable — they survive worker crashes by replaying from last completed task."}' --sync
41
+ ```
42
+
43
+ The `--sync` flag waits for completion and returns the workflow result inline. For long-running prompts (large models, long outputs), drop `--sync` and poll via `conductor workflow get-execution`.
44
+
45
+ ## Output shape
46
+
47
+ `LLM_CHAT_COMPLETE` returns:
48
+
49
+ - `result` — the response text (string)
50
+ - `finishReason` — `STOP`, `LENGTH`, or `TOOL_CALLS`
51
+ - `tokenUsed`, `promptTokens`, `completionTokens` — token accounting
52
+ - `toolCalls` — present if the model invoked a tool (see [ai-agent-mcp.md](ai-agent-mcp.md))
53
+
54
+ Downstream tasks read `${summarize.output.result}` for the text and `${summarize.output.tokenUsed}` for cost tracking.
55
+
56
+ ## Patterns
57
+
58
+ - **Provider per-task.** Mix providers in one workflow — `gpt-4o-mini` for cheap classification, `claude-opus-4-7` for the hard reasoning step. Each task picks its own `llmProvider` + `model`.
59
+ - **Temperature near zero** for deterministic / classification work; **0.7+** for generative / creative.
60
+ - **`maxTokens`** is a hard cap. If `finishReason == "LENGTH"`, the response was truncated — raise the cap.
61
+ - **Don't put secrets in the prompt.** API keys, tokens, PII — keep them in `${workflow.secrets.X}` (Orkes) or worker env. Workflow inputs are visible in the execution view.
@@ -0,0 +1,115 @@
1
+ # Example: RAG — Retrieval-Augmented Generation
2
+
3
+ Classic RAG pattern: search a vector database for context, then ask an LLM to answer using only that context. Two tasks, one workflow.
4
+
5
+ > **⚠️ The single most important RAG rule:** the **embedding model** you use at **query time** in `LLM_SEARCH_INDEX` **MUST exactly match** the embedding model used at **index time** in `LLM_INDEX_TEXT`. Different models produce incompatible vector spaces — a mismatch returns nonsense matches without any error. State this explicitly when generating RAG workflows.
6
+
7
+ ## Pipeline
8
+
9
+ ```
10
+ LLM_SEARCH_INDEX (vector search) → LLM_CHAT_COMPLETE (answer with context)
11
+ ```
12
+
13
+ The search task auto-embeds the user's question, queries the vector DB, and returns the top-k matching chunks. The chat task receives those chunks as system-prompt context and grounds its answer in them.
14
+
15
+ ## Prerequisites
16
+
17
+ 1. **A vector database** registered with Conductor — Pinecone, Postgres pgvector, or MongoDB Atlas. The example uses `postgres-prod` (configured server-side; see your Conductor admin).
18
+ 2. **Documents already indexed.** Use [`LLM_INDEX_TEXT`](../references/workflow-definition.md#llm_index_text) in a separate ingestion workflow to populate the index.
19
+ 3. **An LLM provider** with its API key set (`OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, etc.) — Conductor auto-enables providers when their API key is present.
20
+
21
+ ## Workflow
22
+
23
+ See [workflows/llm-rag.json](workflows/llm-rag.json):
24
+
25
+ ```json
26
+ {
27
+ "name": "rag_qa",
28
+ "tasks": [
29
+ {
30
+ "name": "search_knowledge_base",
31
+ "taskReferenceName": "search",
32
+ "type": "LLM_SEARCH_INDEX",
33
+ "inputParameters": {
34
+ "vectorDB": "postgres-prod",
35
+ "namespace": "kb",
36
+ "index": "articles",
37
+ "embeddingModelProvider": "openai",
38
+ "embeddingModel": "text-embedding-3-small",
39
+ "query": "${workflow.input.question}",
40
+ "llmMaxResults": 3
41
+ }
42
+ },
43
+ {
44
+ "name": "generate_answer",
45
+ "taskReferenceName": "answer",
46
+ "type": "LLM_CHAT_COMPLETE",
47
+ "inputParameters": {
48
+ "llmProvider": "anthropic",
49
+ "model": "claude-sonnet-4-6",
50
+ "messages": [
51
+ {"role": "system", "message": "Answer using only the context below. If the answer isn't in the context, say \"I don't know.\"\n\nContext:\n${search.output.result}"},
52
+ {"role": "user", "message": "${workflow.input.question}"}
53
+ ],
54
+ "temperature": 0.2,
55
+ "maxTokens": 500
56
+ }
57
+ }
58
+ ],
59
+ "outputParameters": {
60
+ "answer": "${answer.output.result}",
61
+ "sources": "${search.output.result}",
62
+ "tokensUsed": "${answer.output.tokenUsed}"
63
+ }
64
+ }
65
+ ```
66
+
67
+ ## Run
68
+
69
+ ```bash
70
+ conductor workflow create examples/workflows/llm-rag.json
71
+ conductor workflow start -w rag_qa -i '{"question": "How does Conductor handle worker failures?"}' --sync
72
+ ```
73
+
74
+ The `--sync` flag waits for both tasks (search + chat) to complete and returns the final answer plus the source chunks for citation.
75
+
76
+ ## Output
77
+
78
+ - `answer` — the grounded response (string)
79
+ - `sources` — the retrieved chunks, with their metadata (use to render citations)
80
+ - `tokensUsed` — for cost tracking
81
+
82
+ ## Variant: pre-computed embeddings
83
+
84
+ If you already have the query embedding (e.g., computed by an upstream worker), use `LLM_SEARCH_EMBEDDINGS` instead — same shape, but takes `embeddings` (a float array) instead of `query` (text). Saves one embedding call per request.
85
+
86
+ ## Patterns
87
+
88
+ - **System prompt does the grounding.** "Answer only from the context below" is the difference between a real RAG system and a thin wrapper that pretends. State it explicitly. Tell the model what to do when the context doesn't cover the question — "say I don't know" beats hallucination.
89
+ - **Low temperature for QA.** `0.2` keeps answers grounded; higher temperatures invent facts.
90
+ - **`llmMaxResults` is the recall knob.** 3 chunks → tight answer, low cost. 10 chunks → broader recall, higher token spend, risk of off-topic context diluting the signal.
91
+ - **Different providers per task is fine.** Cheap small model for embedding (`text-embedding-3-small`), a strong reasoning model for the answer (`claude-sonnet-4-6`). Each task picks its own provider/model.
92
+ - **Return sources.** Always return `${search.output.result}` so the caller can cite or display sources. RAG without sources is just expensive Q&A.
93
+
94
+ ## Ingesting documents (separate workflow)
95
+
96
+ For the search to find anything, an ingestion workflow needs to populate the index. Sketch:
97
+
98
+ ```json
99
+ {
100
+ "name": "ingest_doc",
101
+ "type": "LLM_INDEX_TEXT",
102
+ "inputParameters": {
103
+ "vectorDB": "postgres-prod",
104
+ "namespace": "kb",
105
+ "index": "articles",
106
+ "embeddingModelProvider": "openai",
107
+ "embeddingModel": "text-embedding-3-small",
108
+ "text": "${workflow.input.document}",
109
+ "docId": "${workflow.input.docId}",
110
+ "metadata": {"source": "${workflow.input.source}"}
111
+ }
112
+ }
113
+ ```
114
+
115
+ Run this once per document. The embedding model **must** match the one used at query time in `rag_qa` — different models produce incompatible vector spaces.
@@ -0,0 +1,54 @@
1
+ # Example: Monitor and Retry Failed Workflows
2
+
3
+ > "Show me failed workflows from today and retry the timeout failures."
4
+
5
+ ## Steps
6
+
7
+ ```bash
8
+ # 1. Find failed workflows in the time range
9
+ conductor workflow search -s FAILED --start-time-after "2024-01-15" -c 20
10
+
11
+ # 2. Inspect each one to identify the failed task and reason
12
+ conductor workflow get-execution {workflowId} -c
13
+ ```
14
+
15
+ Look at the failed task's `status` and `reasonForIncompletion`:
16
+
17
+ | Status | Action |
18
+ |--------|--------|
19
+ | `TIMED_OUT` | Retryable — `conductor workflow retry {id}` |
20
+ | `FAILED` (transient) | Retryable — `conductor workflow retry {id}` |
21
+ | `FAILED_WITH_TERMINAL_ERROR` | **Not** retryable. Surface root cause to the user before retrying. |
22
+
23
+ ## Retry batch
24
+
25
+ ```bash
26
+ conductor workflow retry {id1}
27
+ conductor workflow retry {id2}
28
+ conductor workflow retry {id3}
29
+ # verify
30
+ conductor workflow status {id1} # → RUNNING
31
+ ```
32
+
33
+ ## Reporting back
34
+
35
+ Group results in your reply to the user:
36
+
37
+ ```
38
+ Found 4 failed workflows.
39
+
40
+ Retried (3):
41
+ - order_processing ...111 → RUNNING
42
+ - data_pipeline ...222 → RUNNING
43
+ - order_processing ...444 → RUNNING
44
+
45
+ Skipped — terminal failure (1):
46
+ - email_campaign ...333 "Invalid email template" — needs template fix
47
+ ```
48
+
49
+ ## Patterns demonstrated
50
+
51
+ - Time-range search with `--start-time-after` / `--start-time-before`.
52
+ - Distinguishing retryable from terminal failures.
53
+ - Batch retry with post-retry verification.
54
+ - Recommending root-cause fixes when timeouts persist (raise `responseTimeoutSeconds` on the task definition).
@@ -0,0 +1,67 @@
1
+ # Example: Review and Optimize a Workflow
2
+
3
+ > "Review my `order_processing` workflow and tell me what to fix."
4
+
5
+ ## Steps
6
+
7
+ ```bash
8
+ # 1. Load the definition
9
+ conductor workflow get order_processing > /tmp/wf.json
10
+
11
+ # 2. For each SIMPLE task, load the task definition
12
+ conductor taskDef get charge_card
13
+ conductor taskDef get send_email
14
+ conductor taskDef get update_inventory
15
+ ```
16
+
17
+ Walk the checklist in [../references/optimization.md](../references/optimization.md). Group findings by severity.
18
+
19
+ ## Sample report
20
+
21
+ ```
22
+ Workflow: order_processing v3 (12 tasks)
23
+
24
+ CRITICAL (2)
25
+ ✗ B1 SIMPLE task `charge_card` (taskDef): responseTimeoutSeconds=0, timeoutSeconds=0
26
+ A hung payment worker hangs the workflow. Set responseTimeoutSeconds=30,
27
+ pollTimeoutSeconds=60, timeoutSeconds=300 on the task definition.
28
+ ✗ D1 Workflow input `stripeApiKey` is a secret being passed in plaintext.
29
+ Move to ${workflow.secrets.STRIPE_KEY} or to the worker's environment.
30
+
31
+ WARN (3)
32
+ ⚠ A1 description is empty
33
+ ⚠ B2 No workflow timeoutSeconds — a stuck workflow can run forever.
34
+ Suggest 1800s with timeoutPolicy=TIME_OUT_WF.
35
+ ⚠ B3 SIMPLE task `send_email` has retryCount=0. Email is transient by nature —
36
+ set retryCount=3, retryLogic=EXPONENTIAL_BACKOFF, retryDelaySeconds=30.
37
+
38
+ INFO
39
+ • A4 12 tasks — well within the 100-task limit
40
+ • B4 No failureWorkflow. This workflow mutates inventory and charges cards;
41
+ consider a failureWorkflow that releases the inventory hold and notifies ops.
42
+
43
+ Recommended Changes (priority order)
44
+ [ ] task_def_charge_card.json set responseTimeoutSeconds=30, pollTimeoutSeconds=60, timeoutSeconds=300
45
+ [ ] order_processing.json move stripeApiKey to ${workflow.secrets.STRIPE_KEY}
46
+ [ ] order_processing.json add description, timeoutSeconds=1800, timeoutPolicy=TIME_OUT_WF
47
+ [ ] task_def_send_email.json set retryCount=3, retryLogic=EXPONENTIAL_BACKOFF, retryDelaySeconds=30
48
+ [ ] order_processing.json (discussion) add failureWorkflow for inventory rollback + ops alert
49
+ ```
50
+
51
+ ## Offer to fix
52
+
53
+ > Want me to apply any of these? I can:
54
+ > - update the `charge_card` and `send_email` task definitions and re-register them
55
+ > - rewrite the workflow to add `timeoutSeconds`, `timeoutPolicy`, `description`
56
+ > - move `stripeApiKey` out of inputs (need to know where you want it: secrets or worker env)
57
+ >
58
+ > The `failureWorkflow` and the inventory-rollback design are bigger calls — I'd want to know your retry/refund policy before scaffolding that.
59
+
60
+ Don't apply silently. Each fix gets confirmed.
61
+
62
+ ## What this example demonstrates
63
+
64
+ - Loading definition + each SIMPLE task's taskDef before reporting (timeouts and retry config live on the task def, not the workflow task).
65
+ - Grading findings (CRITICAL / WARN / INFO) so the user knows what's urgent.
66
+ - Offering targeted fixes, not a wholesale rewrite.
67
+ - Drawing the line at design decisions that need user input (failureWorkflow design, retry semantics).
@@ -0,0 +1,36 @@
1
+ # Example: Signal a WAIT Task
2
+
3
+ > "Workflow `order-wf-789` is waiting for payment confirmation. Approve it."
4
+
5
+ ## Steps
6
+
7
+ ```bash
8
+ # 1. Find the blocking task
9
+ conductor workflow get-execution order-wf-789 -c
10
+ ```
11
+
12
+ Look for a task with `type: WAIT` and `status: IN_PROGRESS`. That's the one to signal. In this example: `wait_for_payment`.
13
+
14
+ ```bash
15
+ # 2. Signal it (use signal-sync to get the updated workflow back in one round-trip)
16
+ conductor task signal-sync \
17
+ --workflow-id order-wf-789 \
18
+ --task-ref wait_for_payment \
19
+ --status COMPLETED \
20
+ --output '{"paymentId": "pay-456", "amount": 149.99, "method": "credit_card"}'
21
+ ```
22
+
23
+ ## signal vs signal-sync
24
+
25
+ - `signal` — async, fire-and-forget. Returns immediately; workflow advances in the background.
26
+ - `signal-sync` — returns the updated workflow object in the same response. Use when you need to confirm the next task is now running, or to chain follow-up logic.
27
+
28
+ ## Statuses you can signal
29
+
30
+ `COMPLETED`, `FAILED`, `FAILED_WITH_TERMINAL_ERROR`. Use `FAILED_WITH_TERMINAL_ERROR` to reject the WAIT permanently (no retry).
31
+
32
+ ## Patterns demonstrated
33
+
34
+ - Reading execution state to find the blocking WAIT task.
35
+ - Passing structured `output` data with the signal — that data becomes `${wait_for_payment.output.x}` for downstream tasks.
36
+ - Sync signaling for human-in-the-loop confirmations.
@@ -0,0 +1,52 @@
1
+ # Example: Composing Workflows with SUB_WORKFLOW
2
+
3
+ Run another registered workflow as a single task. The parent waits for the child to complete and reads its output. Useful for splitting large workflows into reusable units.
4
+
5
+ > If you want fire-and-forget instead — parent does not wait — use `START_WORKFLOW`.
6
+
7
+ ## Pattern
8
+
9
+ Two registered workflows: `child_normalize` (reusable) and `parent_pipeline` (composes the child).
10
+
11
+ ### Child — `child_normalize`
12
+
13
+ See [workflows/child-normalize.json](workflows/child-normalize.json). Takes a raw payload, returns a normalized object.
14
+
15
+ ### Parent — `parent_pipeline`
16
+
17
+ See [workflows/parent-pipeline.json](workflows/parent-pipeline.json):
18
+
19
+ ```json
20
+ {
21
+ "name": "normalize",
22
+ "taskReferenceName": "normalize",
23
+ "type": "SUB_WORKFLOW",
24
+ "subWorkflowParam": { "name": "child_normalize", "version": 1 },
25
+ "inputParameters": {
26
+ "payload": "${workflow.input.raw}"
27
+ }
28
+ }
29
+ ```
30
+
31
+ The child's `outputParameters` become the SUB_WORKFLOW task's output:
32
+
33
+ ```
34
+ ${normalize.output.normalized}
35
+ ```
36
+
37
+ ## Run
38
+
39
+ ```bash
40
+ # Register both — child first
41
+ conductor workflow create examples/workflows/child-normalize.json
42
+ conductor workflow create examples/workflows/parent-pipeline.json
43
+
44
+ conductor workflow start -w parent_pipeline -i '{"raw": {"name": "ALICE", "email": "ALICE@EX.COM"}}'
45
+ ```
46
+
47
+ ## Notes
48
+
49
+ - The child must be registered before the parent runs (not before the parent is created — Conductor doesn't validate references at definition time).
50
+ - Pin `version` in `subWorkflowParam` for stability. Omitting it picks the latest, which can break parents silently when the child is updated.
51
+ - Child failure surfaces as a SUB_WORKFLOW task failure on the parent.
52
+ - For dynamic-named children, set `subWorkflowParam.name` from input or use `START_WORKFLOW`.
@@ -0,0 +1,88 @@
1
+ {
2
+ "name": "autonomous_agent",
3
+ "description": "ReAct-pattern agent loop: think → act → observe until done",
4
+ "version": 1,
5
+ "schemaVersion": 2,
6
+ "inputParameters": ["task", "mcpServer", "tools"],
7
+ "tasks": [
8
+ {
9
+ "name": "agent_loop",
10
+ "taskReferenceName": "loop",
11
+ "type": "DO_WHILE",
12
+ "loopCondition": "if ($.loop['iteration'] < 10 && $.loop[$.loop['iteration']].think.output.result.done != true) { true; } else { false; }",
13
+ "loopOver": [
14
+ {
15
+ "name": "think",
16
+ "taskReferenceName": "think",
17
+ "type": "LLM_CHAT_COMPLETE",
18
+ "inputParameters": {
19
+ "llmProvider": "openai",
20
+ "model": "gpt-4o-mini",
21
+ "messages": [
22
+ {
23
+ "role": "system",
24
+ "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 }."
25
+ },
26
+ {
27
+ "role": "user",
28
+ "message": "${workflow.input.task}"
29
+ }
30
+ ],
31
+ "temperature": 0.1,
32
+ "maxTokens": 500
33
+ }
34
+ },
35
+ {
36
+ "name": "act",
37
+ "taskReferenceName": "act",
38
+ "type": "SWITCH",
39
+ "evaluatorType": "javascript",
40
+ "expression": "$.done ? 'finish' : 'call_tool'",
41
+ "inputParameters": {
42
+ "done": "${think.output.result.done}"
43
+ },
44
+ "decisionCases": {
45
+ "call_tool": [
46
+ {
47
+ "name": "execute",
48
+ "taskReferenceName": "execute",
49
+ "type": "CALL_MCP_TOOL",
50
+ "inputParameters": {
51
+ "mcpServer": "${workflow.input.mcpServer}",
52
+ "method": "${think.output.result.method}",
53
+ "arguments": "${think.output.result.arguments}"
54
+ }
55
+ }
56
+ ],
57
+ "finish": [
58
+ {
59
+ "name": "done",
60
+ "taskReferenceName": "done",
61
+ "type": "NOOP"
62
+ }
63
+ ]
64
+ }
65
+ }
66
+ ],
67
+ "inputParameters": {
68
+ "loop": "${loop.output}"
69
+ }
70
+ },
71
+ {
72
+ "name": "final_answer",
73
+ "taskReferenceName": "final",
74
+ "type": "INLINE",
75
+ "inputParameters": {
76
+ "evaluatorType": "graaljs",
77
+ "expression": "function e() { var i = $.loop_output['iteration']; return { answer: $.loop_output[i].think.output.result.answer }; } e();",
78
+ "loop_output": "${loop.output}"
79
+ }
80
+ }
81
+ ],
82
+ "outputParameters": {
83
+ "iterations": "${loop.output.iteration}",
84
+ "answer": "${final.output.result.answer}"
85
+ },
86
+ "timeoutSeconds": 600,
87
+ "timeoutPolicy": "TIME_OUT_WF"
88
+ }
@@ -0,0 +1,69 @@
1
+ {
2
+ "name": "my_first_agent",
3
+ "description": "AI agent that discovers MCP tools, plans, executes, and summarizes",
4
+ "version": 1,
5
+ "schemaVersion": 2,
6
+ "inputParameters": ["task"],
7
+ "tasks": [
8
+ {
9
+ "name": "discover_tools",
10
+ "taskReferenceName": "discover",
11
+ "type": "LIST_MCP_TOOLS",
12
+ "inputParameters": {
13
+ "mcpServer": "http://localhost:3001/mcp"
14
+ }
15
+ },
16
+ {
17
+ "name": "plan_action",
18
+ "taskReferenceName": "plan",
19
+ "type": "LLM_CHAT_COMPLETE",
20
+ "inputParameters": {
21
+ "llmProvider": "openai",
22
+ "model": "gpt-4o-mini",
23
+ "messages": [
24
+ {
25
+ "role": "system",
26
+ "message": "You are an AI agent. Available tools: ${discover.output.tools}. Pick exactly one tool and respond as JSON with fields `method` and `arguments`."
27
+ },
28
+ {
29
+ "role": "user",
30
+ "message": "${workflow.input.task}"
31
+ }
32
+ ],
33
+ "temperature": 0.1,
34
+ "maxTokens": 500
35
+ }
36
+ },
37
+ {
38
+ "name": "execute_tool",
39
+ "taskReferenceName": "execute",
40
+ "type": "CALL_MCP_TOOL",
41
+ "inputParameters": {
42
+ "mcpServer": "http://localhost:3001/mcp",
43
+ "method": "${plan.output.result.method}",
44
+ "arguments": "${plan.output.result.arguments}"
45
+ }
46
+ },
47
+ {
48
+ "name": "summarize_result",
49
+ "taskReferenceName": "summarize",
50
+ "type": "LLM_CHAT_COMPLETE",
51
+ "inputParameters": {
52
+ "llmProvider": "openai",
53
+ "model": "gpt-4o-mini",
54
+ "messages": [
55
+ {
56
+ "role": "user",
57
+ "message": "The user asked: \"${workflow.input.task}\". Tool returned: ${execute.output.content}. Reply in one short paragraph."
58
+ }
59
+ ],
60
+ "maxTokens": 500
61
+ }
62
+ }
63
+ ],
64
+ "outputParameters": {
65
+ "plan": "${plan.output.result}",
66
+ "toolResult": "${execute.output.content}",
67
+ "summary": "${summarize.output.result}"
68
+ }
69
+ }
@@ -0,0 +1,21 @@
1
+ {
2
+ "name": "child_normalize",
3
+ "description": "Normalize a user payload (lowercase email, trim name)",
4
+ "version": 1,
5
+ "schemaVersion": 2,
6
+ "inputParameters": ["payload"],
7
+ "tasks": [
8
+ {
9
+ "name": "transform",
10
+ "taskReferenceName": "transform",
11
+ "type": "JSON_JQ_TRANSFORM",
12
+ "inputParameters": {
13
+ "data": "${workflow.input.payload}",
14
+ "queryExpression": "{name: (.name | ascii_downcase | gsub(\"^\\\\s+|\\\\s+$\"; \"\")), email: (.email | ascii_downcase)}"
15
+ }
16
+ }
17
+ ],
18
+ "outputParameters": {
19
+ "normalized": "${transform.output.result}"
20
+ }
21
+ }
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "paginated_fetch",
3
+ "description": "Fetch N pages from an endpoint via DO_WHILE",
4
+ "version": 1,
5
+ "schemaVersion": 2,
6
+ "inputParameters": ["endpoint", "count"],
7
+ "tasks": [
8
+ {
9
+ "name": "loop",
10
+ "taskReferenceName": "loop_ref",
11
+ "type": "DO_WHILE",
12
+ "loopCondition": "if ($.loop_ref['iteration'] < $.value) { true; } else { false; }",
13
+ "loopOver": [
14
+ {
15
+ "name": "do_work",
16
+ "taskReferenceName": "do_work",
17
+ "type": "HTTP",
18
+ "inputParameters": {
19
+ "http_request": {
20
+ "uri": "${workflow.input.endpoint}?page=${loop_ref.output.iteration}",
21
+ "method": "GET"
22
+ }
23
+ }
24
+ }
25
+ ],
26
+ "inputParameters": {
27
+ "value": "${workflow.input.count}",
28
+ "loop_ref": "${loop_ref.output}"
29
+ }
30
+ }
31
+ ],
32
+ "outputParameters": {
33
+ "iterations": "${loop_ref.output.iteration}"
34
+ }
35
+ }
@@ -0,0 +1,61 @@
1
+ {
2
+ "name": "parallel_fetch",
3
+ "description": "Fetch inventory and pricing in parallel, then merge",
4
+ "version": 1,
5
+ "schemaVersion": 2,
6
+ "inputParameters": ["inventoryUrl", "pricingUrl"],
7
+ "tasks": [
8
+ {
9
+ "name": "fork",
10
+ "taskReferenceName": "fork",
11
+ "type": "FORK_JOIN",
12
+ "forkTasks": [
13
+ [
14
+ {
15
+ "name": "fetch_inventory",
16
+ "taskReferenceName": "inventory",
17
+ "type": "HTTP",
18
+ "inputParameters": {
19
+ "http_request": {
20
+ "uri": "${workflow.input.inventoryUrl}",
21
+ "method": "GET"
22
+ }
23
+ }
24
+ }
25
+ ],
26
+ [
27
+ {
28
+ "name": "fetch_pricing",
29
+ "taskReferenceName": "pricing",
30
+ "type": "HTTP",
31
+ "inputParameters": {
32
+ "http_request": {
33
+ "uri": "${workflow.input.pricingUrl}",
34
+ "method": "GET"
35
+ }
36
+ }
37
+ }
38
+ ]
39
+ ]
40
+ },
41
+ {
42
+ "name": "join",
43
+ "taskReferenceName": "join",
44
+ "type": "JOIN",
45
+ "joinOn": ["inventory", "pricing"]
46
+ },
47
+ {
48
+ "name": "merge",
49
+ "taskReferenceName": "merge",
50
+ "type": "JSON_JQ_TRANSFORM",
51
+ "inputParameters": {
52
+ "inventory": "${inventory.output.response.body}",
53
+ "pricing": "${pricing.output.response.body}",
54
+ "queryExpression": "{items: .inventory.items, prices: .pricing.prices}"
55
+ }
56
+ }
57
+ ],
58
+ "outputParameters": {
59
+ "merged": "${merge.output.result}"
60
+ }
61
+ }