@jiggai/recipes 0.4.34 → 0.4.36

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.
@@ -0,0 +1,196 @@
1
+ # Template Variables
2
+
3
+ ClawRecipes supports powerful template variable substitution for workflow prompts, file paths, and content. Variables use double-brace syntax: `{{variable.name}}`.
4
+
5
+ ## Global Variables
6
+
7
+ These variables are available in all nodes:
8
+
9
+ - `{{date}}` — Current ISO timestamp (e.g., `2025-04-04T03:53:00.000Z`)
10
+ - `{{run.id}}` — Unique run ID (timestamp-based, e.g., `2025-04-04T03-53-00-123Z`)
11
+ - `{{run.timestamp}}` — Alias for `{{run.id}}`
12
+ - `{{workflow.id}}` — Workflow ID from the JSON file
13
+ - `{{workflow.name}}` — Workflow display name (fallback to ID or filename)
14
+ - `{{node.id}}` — Current node ID (available in media nodes)
15
+
16
+ ## Upstream Node Variables
17
+
18
+ Access outputs from previous workflow nodes using the pattern `{{nodeId.fieldName}}`:
19
+
20
+ ### Basic Access Patterns
21
+
22
+ - `{{nodeId.output}}` — Full JSON output envelope from the node
23
+ - `{{nodeId.text}}` — The `text` field from the node output (most common)
24
+ - `{{nodeId.fieldName}}` — Any specific field from the node output JSON
25
+
26
+ ### Common Pitfall: output vs text
27
+
28
+ **Wrong:** `{{brand_review.output}}` returns the full envelope:
29
+ ```json
30
+ {
31
+ "runId": "2025-04-04T03-53-00-123Z",
32
+ "teamId": "marketing-team",
33
+ "nodeId": "brand_review",
34
+ "kind": "llm",
35
+ "text": "Here is my review...",
36
+ "completedAt": "2025-04-04T03:53:15.000Z"
37
+ }
38
+ ```
39
+
40
+ **Right:** `{{brand_review.text}}` returns just the payload:
41
+ ```
42
+ Here is my review...
43
+ ```
44
+
45
+ ## Nested JSON Extraction
46
+
47
+ When a node's `text` field contains JSON, ClawRecipes automatically extracts nested fields:
48
+
49
+ ### Example Node Output
50
+ ```json
51
+ {
52
+ "text": "{\"title\": \"Product Launch\", \"tags\": [\"marketing\"], \"approved\": true}"
53
+ }
54
+ ```
55
+
56
+ ### Available Template Variables
57
+ - `{{nodeId.text}}` — Raw JSON string
58
+ - `{{nodeId.title}}` — Extracted: "Product Launch"
59
+ - `{{nodeId.approved_json}}` — For non-string values: "true"
60
+
61
+ ### Deeply Nested Extraction
62
+ If the JSON contains nested objects, fields are flattened:
63
+
64
+ ```json
65
+ {
66
+ "text": "{\"meta\": {\"author\": \"John\", \"priority\": 1}}"
67
+ }
68
+ ```
69
+
70
+ Available as:
71
+ - `{{nodeId.meta_json}}` — Full meta object as JSON string
72
+ - `{{nodeId.author}}` — "John" (if meta.author is a string)
73
+
74
+ ## LLM Node Structured Output
75
+
76
+ LLM nodes with `outputFields` configuration produce predictable JSON structures:
77
+
78
+ ### Configuration
79
+ ```json
80
+ {
81
+ "config": {
82
+ "outputFields": [
83
+ {"name": "title", "type": "text"},
84
+ {"name": "tags", "type": "list"},
85
+ {"name": "metadata", "type": "json"}
86
+ ]
87
+ }
88
+ }
89
+ ```
90
+
91
+ ### Template Access
92
+ ```
93
+ Title: {{nodeId.title}}
94
+ Tags: {{nodeId.tags}}
95
+ Metadata: {{nodeId.metadata_json}}
96
+ ```
97
+
98
+ ## Usage Examples
99
+
100
+ ### LLM Prompt Template
101
+ ```
102
+ Review the content from the previous step:
103
+
104
+ {{brand_review.text}}
105
+
106
+ Based on this review, create a social media post with:
107
+ - Title: engaging and on-brand
108
+ - Content: max 280 characters
109
+ - Hashtags: 3-5 relevant tags
110
+
111
+ Current date: {{date}}
112
+ Workflow: {{workflow.name}}
113
+ ```
114
+
115
+ ### File Path Templates
116
+ ```json
117
+ {
118
+ "tool": "fs.write",
119
+ "args": {
120
+ "path": "content/{{run.id}}/{{brand_review.title}}.md",
121
+ "content": "# {{brand_review.title}}\n\n{{brand_review.text}}"
122
+ }
123
+ }
124
+ ```
125
+
126
+ ### Media Node Prompts
127
+ ```json
128
+ {
129
+ "config": {
130
+ "promptTemplate": "Create an image for: {{content_draft.title}}\n\nStyle: {{brand_review.style}}\nMood: professional and engaging"
131
+ }
132
+ }
133
+ ```
134
+
135
+ ## Implementation Details
136
+
137
+ Template substitution happens in the workflow worker during node execution. The code path is:
138
+
139
+ - **Primary**: `src/lib/workflows/workflow-worker.ts` — `buildTemplateVars()` function
140
+ - **Utility**: `src/lib/workflows/workflow-utils.ts` — `templateReplace()` function
141
+
142
+ ### Variable Resolution Process
143
+
144
+ 1. **Global vars** are built from run metadata and timestamps
145
+ 2. **Node outputs** are loaded from each completed node's output file
146
+ 3. **JSON parsing** attempts to extract fields from the `text` field
147
+ 4. **Nested extraction** flattens nested objects with `_json` suffixes for non-strings
148
+ 5. **Template replacement** applies all variables using simple string substitution
149
+
150
+ ### Performance Notes
151
+
152
+ - Variables are rebuilt for each node execution (fresh state)
153
+ - Large node outputs are fully loaded into memory during template resolution
154
+ - JSON parsing failures are silently ignored (graceful degradation)
155
+
156
+ ## Best Practices
157
+
158
+ ### Variable Naming
159
+ - Use descriptive node IDs: `brand_review` not `step1`
160
+ - Design consistent output field names across nodes
161
+ - Use `outputFields` for structured LLM outputs
162
+
163
+ ### Error Handling
164
+ - Always provide fallbacks: `{{nodeId.title || "Untitled"}}`
165
+ - Test templates with missing/malformed node outputs
166
+ - Use `{{nodeId.text}}` when unsure about field structure
167
+
168
+ ### Memory Management
169
+ - Avoid extremely large text outputs in frequently-referenced nodes
170
+ - Consider splitting large data into separate file outputs
171
+
172
+ ## Common Patterns
173
+
174
+ ### Content Pipeline
175
+ ```
176
+ 1. research_node.text → "Market analysis shows..."
177
+ 2. draft_node template: "Based on {{research_node.text}}, write..."
178
+ 3. review_node template: "Review this draft: {{draft_node.text}}"
179
+ ```
180
+
181
+ ### Conditional Content
182
+ ```
183
+ {% if brand_review.approved %}
184
+ Approved content: {{brand_review.text}}
185
+ {% else %}
186
+ Needs revision: {{brand_review.feedback}}
187
+ {% endif %}
188
+ ```
189
+
190
+ Note: ClawRecipes uses simple string substitution, not template engines like Jinja2. Complex conditionals should be handled in LLM prompts.
191
+
192
+ ## Related Documentation
193
+
194
+ - [WORKFLOW_NODES.md](WORKFLOW_NODES.md) — Node types and configuration
195
+ - [WORKFLOW_EXAMPLES.md](WORKFLOW_EXAMPLES.md) — Template usage examples
196
+ - [MEDIA_GENERATION.md](MEDIA_GENERATION.md) — Media node templates and variables
@@ -0,0 +1,334 @@
1
+ # Workflow Approvals
2
+
3
+ Human approval nodes (`human_approval`) pause workflow execution for manual review. This document covers the runtime behavior, state management, and approval flow.
4
+
5
+ ## Approval Flow Overview
6
+
7
+ 1. **Node Execution** — Worker reaches approval node
8
+ 2. **Pause State** — Run status changes to `awaiting_approval`
9
+ 3. **Message Sent** — Approval request sent to configured channel
10
+ 4. **Human Decision** — User replies with approve/decline command
11
+ 5. **State Update** — Approval recorded to disk
12
+ 6. **Resume** — Workflow continues or loops back for revision
13
+
14
+ ## Runtime Behavior
15
+
16
+ ### Node Status Transition
17
+
18
+ When a workflow worker processes a `human_approval` node:
19
+
20
+ ```
21
+ node.status: undefined → waiting → waiting_approval
22
+ run.status: running → awaiting_approval
23
+ ```
24
+
25
+ The worker:
26
+ 1. Creates approval directory: `workflow-runs/{runId}/approvals/`
27
+ 2. Generates random approval code (6-char uppercase)
28
+ 3. Writes `approval.json` with pending status
29
+ 4. Sends message to configured channel with approval code
30
+ 5. Marks node as waiting in run state
31
+ 6. Pauses execution (no further nodes enqueued)
32
+
33
+ ### State Files Written
34
+
35
+ **Run Log Update** (`workflow-runs/{runId}/run.json`):
36
+ ```json
37
+ {
38
+ "status": "awaiting_approval",
39
+ "nodeStates": {
40
+ "approval_node": {"status": "waiting", "ts": "2025-04-04T03:53:15.000Z"}
41
+ },
42
+ "events": [
43
+ {
44
+ "ts": "2025-04-04T03:53:15.000Z",
45
+ "type": "node.awaiting_approval",
46
+ "nodeId": "approval_node",
47
+ "bindingId": "telegram:account:mybot",
48
+ "approvalFile": "workflow-runs/2025-04-04T03-53-00-123Z/approvals/approval.json"
49
+ }
50
+ ],
51
+ "nodeResults": [
52
+ {
53
+ "nodeId": "approval_node",
54
+ "kind": "human_approval",
55
+ "approvalBindingId": "telegram:account:mybot",
56
+ "approvalFile": "workflow-runs/2025-04-04T03-53-00-123Z/approvals/approval.json"
57
+ }
58
+ ]
59
+ }
60
+ ```
61
+
62
+ **Approval Record** (`workflow-runs/{runId}/approvals/approval.json`):
63
+ ```json
64
+ {
65
+ "runId": "2025-04-04T03-53-00-123Z",
66
+ "teamId": "marketing-team",
67
+ "workflowFile": "content-pipeline.workflow.json",
68
+ "nodeId": "approval_node",
69
+ "bindingId": "telegram:account:mybot",
70
+ "requestedAt": "2025-04-04T03:53:15.000Z",
71
+ "status": "pending",
72
+ "code": "XY4K91",
73
+ "ticket": "work/in-progress/0042-social-media-campaign.md",
74
+ "runLog": "workflow-runs/2025-04-04T03-53-00-123Z/run.json"
75
+ }
76
+ ```
77
+
78
+ ## Message Template Rendering
79
+
80
+ The approval message includes template variable substitution:
81
+
82
+ **Template Variables Available:**
83
+ - `{{workflow.name}}` — Workflow display name
84
+ - `{{code}}` — Generated approval code
85
+ - `{{ticket}}` — Relative path to ticket file
86
+ - Proposed content preview (from prior node outputs)
87
+
88
+ **Default Message Template:**
89
+ ```
90
+ Approval requested: {{workflow.name}}
91
+ Ticket: {{ticket}}
92
+ Code: {{code}}
93
+
94
+ ---
95
+ PROPOSED POST (X)
96
+ ---
97
+ {{proposed_content}}
98
+
99
+ Reply with:
100
+ - approve {{code}}
101
+ - decline {{code}} <what to change>
102
+
103
+ (You can also review in Kitchen: http://localhost:7777/teams/{{teamId}}/workflows/{{workflow.id}})
104
+ ```
105
+
106
+ **Proposed Content Logic:**
107
+ 1. Look for `qc_brand` node output first
108
+ 2. Fall back to most recent prior LLM node
109
+ 3. Extract `text` field and sanitize for preview
110
+ 4. Show "(Warning: no proposed text found)" if unavailable
111
+
112
+ ## Human Response Handling
113
+
114
+ ### Command Format
115
+
116
+ Users reply in the configured channel:
117
+
118
+ ```
119
+ approve XY4K91
120
+ decline XY4K91 needs better headlines
121
+ ```
122
+
123
+ **Parsing Rules:**
124
+ - Case-insensitive verb matching: `approve|decline`
125
+ - Code must be exact (uppercase)
126
+ - Optional note after code for decline responses
127
+ - Parser checks multiple lines, prefers last match
128
+
129
+ ### Auto-Processing
130
+
131
+ When a user replies with a valid approval command:
132
+
133
+ 1. **Event Detection** — Plugin listener matches approval format
134
+ 2. **Code Lookup** — Searches all team runs for matching pending approval
135
+ 3. **Decision Recording** — Updates approval.json status and note
136
+ 4. **Auto-Resume** — Calls resume workflow to continue execution
137
+
138
+ **Supported Channels:**
139
+ - Telegram (primary)
140
+ - Other channels with conditional processing based on metadata
141
+
142
+ ## Resume and Continuation
143
+
144
+ ### Approval Resume
145
+
146
+ **Approved Flows:**
147
+ ```
148
+ approval.status: pending → approved
149
+ node.status: waiting → success
150
+ run.status: awaiting_approval → running
151
+ → Next node enqueued
152
+ ```
153
+
154
+ **Rejection Flows:**
155
+ ```
156
+ approval.status: pending → rejected
157
+ node.status: waiting → error
158
+ run.status: awaiting_approval → needs_revision
159
+ → Loop back to revision node
160
+ ```
161
+
162
+ ### Revision Logic
163
+
164
+ When an approval is declined:
165
+
166
+ 1. **Find revision target** — Prefers `draft_assets` node, else closest prior LLM node
167
+ 2. **Clear node states** — Remove status for revision node onward
168
+ 3. **Clear locks** — Remove any stale node lock files
169
+ 4. **Enqueue revision** — Send revision task to appropriate agent
170
+ 5. **Include feedback** — Pass human note in task packet
171
+
172
+ **Revision Node Selection:**
173
+ ```typescript
174
+ // Preferred: explicit draft_assets node
175
+ let reviseIdx = workflow.nodes.findIndex(n => n.id === 'draft_assets');
176
+
177
+ // Fallback: most recent LLM node before approval
178
+ if (reviseIdx < 0) {
179
+ for (let i = approvalIdx - 1; i >= 0; i--) {
180
+ if (workflow.nodes[i]?.kind === 'llm') {
181
+ reviseIdx = i;
182
+ break;
183
+ }
184
+ }
185
+ }
186
+ ```
187
+
188
+ ## Configuration
189
+
190
+ ### Node Configuration
191
+
192
+ ```json
193
+ {
194
+ "id": "final_approval",
195
+ "kind": "human_approval",
196
+ "action": {
197
+ "approvalBindingId": "telegram:account:mybot"
198
+ },
199
+ "config": {
200
+ "provider": "telegram",
201
+ "target": "6477250615",
202
+ "accountId": "mybot",
203
+ "agentId": "marketing-team-lead"
204
+ }
205
+ }
206
+ ```
207
+
208
+ **Configuration Hierarchy:**
209
+ 1. `approvalBindingId` → Look up in OpenClaw config bindings
210
+ 2. `config.target` → Direct channel target
211
+ 3. Back-compat fallbacks for known account IDs
212
+
213
+ ### Agent Assignment
214
+
215
+ Approval nodes are executed by:
216
+ 1. **Explicit `config.agentId`** — Workflow author override
217
+ 2. **Team lead** — `${teamId}-lead` (default orchestrator)
218
+ 3. **Current agent** — Fallback for backwards compatibility
219
+
220
+ ## Command Line Interface
221
+
222
+ ### Manual Approval
223
+ ```bash
224
+ openclaw recipes workflows approve \
225
+ --team-id marketing-team \
226
+ --run-id 2025-04-04T03-53-00-123Z \
227
+ --approved true
228
+
229
+ openclaw recipes workflows approve \
230
+ --team-id marketing-team \
231
+ --run-id 2025-04-04T03-53-00-123Z \
232
+ --approved false \
233
+ --note "Headlines need work"
234
+ ```
235
+
236
+ ### Resume Workflow
237
+ ```bash
238
+ openclaw recipes workflows resume \
239
+ --team-id marketing-team \
240
+ --run-id 2025-04-04T03-53-00-123Z
241
+ ```
242
+
243
+ ### Poll Approvals
244
+ ```bash
245
+ # Auto-resume any decided approvals
246
+ openclaw recipes workflows poll-approvals \
247
+ --team-id marketing-team \
248
+ --limit 20
249
+ ```
250
+
251
+ ## Implementation Details
252
+
253
+ ### Code Locations
254
+
255
+ **Primary Approval Logic:**
256
+ - `src/lib/workflows/workflow-approvals.ts`
257
+ - `approveWorkflowRun()` — Record approval decision
258
+ - `resumeWorkflowRun()` — Continue/revise after decision
259
+ - `pollWorkflowApprovals()` — Auto-resume decided approvals
260
+
261
+ **Worker Execution:**
262
+ - `src/lib/workflows/workflow-worker.ts`
263
+ - Approval node execution in `runWorkflowWorkerTick()`
264
+ - Message sending and state management
265
+
266
+ **Event Handling:**
267
+ - `index.ts` — Auto-approval reply handler in plugin registration
268
+
269
+ ### File Structure
270
+ ```
271
+ workspace-{teamId}/
272
+ └── shared-context/
273
+ └── workflow-runs/
274
+ └── {runId}/
275
+ ├── run.json # Main run state
276
+ ├── approvals/
277
+ │ └── approval.json # Approval record
278
+ ├── locks/ # Node execution locks
279
+ └── node-outputs/ # Node result files
280
+ ```
281
+
282
+ ## Error Handling
283
+
284
+ ### Missing Targets
285
+ ```
286
+ Node final_approval missing approval target (provide config.target or binding mapping)
287
+ ```
288
+
289
+ **Solution:** Configure `config.target` or add binding in OpenClaw config.
290
+
291
+ ### Code Not Found
292
+ ```
293
+ [recipes] approval reply not matched: code=XY4K91
294
+ ```
295
+
296
+ **Causes:**
297
+ - Code already processed/expired
298
+ - Wrong team workspace searched
299
+ - Approval file corrupted
300
+
301
+ ### Resume Failures
302
+ ```
303
+ Approval still pending. Update approval.json first.
304
+ ```
305
+
306
+ **Solution:** Record decision with `workflows approve` before resuming.
307
+
308
+ ## Best Practices
309
+
310
+ ### Node Placement
311
+ - Place approval nodes after content generation but before publishing
312
+ - Use descriptive node IDs: `final_approval`, `brand_review`, `legal_check`
313
+
314
+ ### Message Configuration
315
+ - Always configure approval binding in OpenClaw config for production
316
+ - Test approval flow with known test accounts/channels
317
+ - Include meaningful context in approval messages
318
+
319
+ ### Error Recovery
320
+ - Monitor approval files for stuck `pending` status
321
+ - Use `poll-approvals` for automated processing
322
+ - Set up alerting for approval timeouts
323
+
324
+ ### Workflow Design
325
+ - Design clear revision loops with identifiable `draft_assets` nodes
326
+ - Keep approval criteria specific and actionable
327
+ - Document approval responsibilities in team workflows
328
+
329
+ ## Related Documentation
330
+
331
+ - [WORKFLOW_NODES.md](WORKFLOW_NODES.md) — All node types and configuration
332
+ - [TEMPLATE_VARIABLES.md](TEMPLATE_VARIABLES.md) — Message template variables
333
+ - [OUTBOUND_POSTING.md](OUTBOUND_POSTING.md) — Publishing after approval
334
+ - [COMMANDS.md](COMMANDS.md) — CLI workflow commands
@@ -0,0 +1,147 @@
1
+ # Workflow Node Reference (ClawRecipes)
2
+
3
+ This document is the **runtime** reference for workflow node configuration in ClawRecipes.
4
+
5
+ ClawKitchen (the UI) edits workflow JSON files, but the actual execution semantics live here.
6
+
7
+ If you’re new to the file format, start with:
8
+ - [WORKFLOW_RUNS_FILE_FIRST.md](WORKFLOW_RUNS_FILE_FIRST.md)
9
+
10
+ ---
11
+
12
+ ## Mental model
13
+
14
+ A workflow run is a directory on disk containing a `run.json` (and logs/deliverables). The worker executes nodes in order, persists each node’s output, and downstream nodes reference upstream outputs via `{{ }}` template variables.
15
+
16
+ ### Template variables
17
+
18
+ At runtime, ClawRecipes replaces `{{vars}}` in strings inside node configs.
19
+
20
+ There are two broad categories:
21
+
22
+ **Global vars** (always available):
23
+ - `{{date}}`
24
+ - `{{run.id}}`
25
+ - `{{workflow.id}}`
26
+ - `{{workflow.name}}`
27
+ - `{{node.id}}`
28
+
29
+ **Upstream node vars** (depend on prior nodes):
30
+ - `{{someNode.output}}` — the full stored output envelope
31
+ - `{{someNode.text}}` — the most common “payload string” field
32
+ - `{{someNode.someField}}` — a field extracted from JSON inside the node’s `text` (when applicable)
33
+
34
+ Notes:
35
+ - If an upstream node stores JSON inside its `text` field, the template system can extract nested fields (e.g. `{{draft_assets.video_brief}}`).
36
+ - If you see “garbled JSON” showing up in a media prompt, you’re usually templating the *envelope* (e.g. `{{draft_assets.output}}`) instead of the intended field (e.g. `{{draft_assets.text}}` or `{{draft_assets.video_brief}}`).
37
+
38
+ ---
39
+
40
+ ## Node types
41
+
42
+ This section describes the important node types and the config fields the worker actually reads.
43
+
44
+ ### LLM nodes
45
+
46
+ LLM nodes execute via the `llm-task` tool.
47
+
48
+ Common config fields (stored under `node.config`):
49
+ - `promptTemplate` (string): the prompt content, after template-var substitution
50
+ - `timeoutMs` (number, optional)
51
+ - `model` (string, optional): provider/model selection (node → workflow → global precedence)
52
+
53
+ #### outputFields (structured output)
54
+
55
+ If `node.config.outputFields` is present, ClawRecipes will:
56
+ 1) Append an explicit OUTPUT FORMAT section to the prompt
57
+ 2) Derive a JSON Schema and pass it to `llm-task`
58
+
59
+ That means the LLM output is **validated**.
60
+
61
+ Shape:
62
+
63
+ ```json
64
+ {
65
+ "outputFields": [
66
+ {"name": "summary", "type": "text"},
67
+ {"name": "angles", "type": "list"},
68
+ {"name": "metadata", "type": "json"}
69
+ ]
70
+ }
71
+ ```
72
+
73
+ Types:
74
+ - `text` → JSON string
75
+ - `list` → array of strings
76
+ - `json` → JSON object
77
+
78
+ ---
79
+
80
+ ### Media nodes (image/video/audio)
81
+
82
+ Media nodes ultimately produce a file deliverable. ClawRecipes invokes a **MediaDriver** selected by `provider`.
83
+
84
+ Provider selection:
85
+ - In workflow JSON, providers are referenced as `skill-<slug>`
86
+ - At runtime the worker strips `skill-` and looks up a driver by slug
87
+
88
+ #### Common media config fields
89
+
90
+ These are passed through to drivers as `opts.config`:
91
+
92
+ - `size` (string):
93
+ - For images, typically pixel size like `1024x1024`, `1792x1024`, `1024x1792`
94
+ - Some providers interpret this differently (drivers may map pixel sizes to tiers)
95
+
96
+ - `duration` (string or number): duration in seconds, e.g. `"5s"`, `"10s"`, or `10`
97
+
98
+ - `aspect_ratio` (string): e.g. `"16:9"`, `"9:16"` (provider-specific)
99
+
100
+ - `quality` / `style`: image-provider-specific fields (ex: OpenAI)
101
+
102
+ - `addRefinement` (boolean): **opt-in** prompt refinement pass for video/audio.
103
+ - Default is OFF. When enabled, the worker runs an extra `llm-task` call to refine the prompt before invoking the driver.
104
+
105
+ #### Driver constraints
106
+
107
+ Drivers can optionally declare duration constraints (`minSeconds/maxSeconds/defaultSeconds`).
108
+
109
+ Kitchen surfaces these constraints as UX hints, but the runtime is the source of truth.
110
+
111
+ ---
112
+
113
+ ### Tool nodes
114
+
115
+ Tool nodes call a tool by name with JSON args. Example:
116
+ - `fs.append`
117
+ - `outbound.post`
118
+ - `message.send`
119
+
120
+ Tool nodes support template vars inside string args.
121
+
122
+ ---
123
+
124
+ ### Human approval nodes
125
+
126
+ Human approval nodes pause execution until an approval is granted in the UI.
127
+
128
+ Common pattern:
129
+ - Upstream LLM generates a draft
130
+ - Approval node shows a `messageTemplate` (with template vars)
131
+ - Downstream nodes continue only after approval
132
+
133
+ ---
134
+
135
+ ## Adding new capabilities (where to change code)
136
+
137
+ - LLM output shaping/validation: `src/lib/workflows/workflow-worker.ts`
138
+ - Media providers: `src/lib/workflows/media-drivers/`
139
+ - CLI for Kitchen dropdown: `openclaw recipes workflows media-drivers` (handler: `src/handlers/media-drivers.ts`)
140
+
141
+ ---
142
+
143
+ ## Troubleshooting quick hits
144
+
145
+ - **Provider missing in dropdown**: run `openclaw recipes workflows media-drivers` and check `available` / `missingEnvVars`.
146
+ - **Media node ignores config**: confirm the driver reads `opts.config` (not all scripts support every knob).
147
+ - **Bad video quality**: ensure your prompt is a single clear scene brief; avoid multi-scene paragraphs when the provider needs a seed frame.
@@ -3,6 +3,8 @@
3
3
  This document explains how ClawRecipes workflows work in practice.
4
4
 
5
5
  If you want a copy-paste cookbook after reading this reference, also see:
6
+
7
+ - [WORKFLOW_NODES.md](WORKFLOW_NODES.md) — runtime node config reference (LLM/media/tool/approval)
6
8
  - [WORKFLOW_EXAMPLES.md](WORKFLOW_EXAMPLES.md)
7
9
 
8
10
  If you are trying to answer any of these questions, start here:
package/index.ts CHANGED
@@ -47,6 +47,7 @@ import { handleScaffold, scaffoldAgentFromRecipe } from "./src/handlers/scaffold
47
47
  import { handleAddRoleToTeam } from "./src/handlers/team-add-role";
48
48
  import { reconcileRecipeCronJobs } from "./src/handlers/cron";
49
49
  import { handleWorkflowsApprove, handleWorkflowsPollApprovals, handleWorkflowsResume, handleWorkflowsRun, handleWorkflowsRunnerOnce, handleWorkflowsRunnerTick, handleWorkflowsWorkerTick } from "./src/handlers/workflows";
50
+ import { handleMediaDriversList } from "./src/handlers/media-drivers";
50
51
  import { listRecipeFiles, loadRecipeById, workspacePath } from "./src/lib/recipes";
51
52
  import {
52
53
  executeWorkspaceCleanup,
@@ -728,6 +729,14 @@ workflows
728
729
  console.log(JSON.stringify(res, null, 2));
729
730
  });
730
731
 
732
+ workflows
733
+ .command("media-drivers")
734
+ .description("List available media generation drivers with env-var availability")
735
+ .action(async () => {
736
+ const drivers = await handleMediaDriversList();
737
+ console.log(JSON.stringify(drivers));
738
+ });
739
+
731
740
  workflows
732
741
  .command("poll-approvals")
733
742
  .description("Auto-resume any workflow runs whose approval decision has been recorded (approved/rejected)")