@n8n/ai-workflow-builder 1.8.1 → 1.10.0

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 (224) hide show
  1. package/dist/agents/planner.agent.d.ts +1 -0
  2. package/dist/agents/planner.agent.js +1 -0
  3. package/dist/agents/planner.agent.js.map +1 -1
  4. package/dist/agents/responder.agent.d.ts +16 -6
  5. package/dist/agents/responder.agent.js +19 -8
  6. package/dist/agents/responder.agent.js.map +1 -1
  7. package/dist/agents/supervisor.agent.d.ts +8 -5
  8. package/dist/agents/supervisor.agent.js +37 -21
  9. package/dist/agents/supervisor.agent.js.map +1 -1
  10. package/dist/ai-workflow-builder-agent.service.d.ts +12 -4
  11. package/dist/ai-workflow-builder-agent.service.js +102 -51
  12. package/dist/ai-workflow-builder-agent.service.js.map +1 -1
  13. package/dist/assistant/assistant-handler.d.ts +23 -0
  14. package/dist/assistant/assistant-handler.js +265 -0
  15. package/dist/assistant/assistant-handler.js.map +1 -0
  16. package/dist/assistant/index.d.ts +2 -0
  17. package/dist/assistant/index.js +6 -0
  18. package/dist/assistant/index.js.map +1 -0
  19. package/dist/assistant/types.d.ts +83 -0
  20. package/dist/assistant/types.js +3 -0
  21. package/dist/assistant/types.js.map +1 -0
  22. package/dist/build.tsbuildinfo +1 -1
  23. package/dist/code-builder/code-builder-agent.d.ts +31 -0
  24. package/dist/code-builder/code-builder-agent.js +463 -0
  25. package/dist/code-builder/code-builder-agent.js.map +1 -0
  26. package/dist/code-builder/code-workflow-builder.d.ts +34 -0
  27. package/dist/code-builder/code-workflow-builder.js +80 -0
  28. package/dist/code-builder/code-workflow-builder.js.map +1 -0
  29. package/dist/code-builder/constants.d.ts +62 -0
  30. package/dist/code-builder/constants.js +83 -0
  31. package/dist/code-builder/constants.js.map +1 -0
  32. package/dist/code-builder/engines/code-builder-node-search-engine.d.ts +19 -0
  33. package/dist/code-builder/engines/code-builder-node-search-engine.js +236 -0
  34. package/dist/code-builder/engines/code-builder-node-search-engine.js.map +1 -0
  35. package/dist/code-builder/handlers/agent-iteration-handler.d.ts +36 -0
  36. package/dist/code-builder/handlers/agent-iteration-handler.js +78 -0
  37. package/dist/code-builder/handlers/agent-iteration-handler.js.map +1 -0
  38. package/dist/code-builder/handlers/auto-finalize-handler.d.ts +29 -0
  39. package/dist/code-builder/handlers/auto-finalize-handler.js +50 -0
  40. package/dist/code-builder/handlers/auto-finalize-handler.js.map +1 -0
  41. package/dist/code-builder/handlers/chat-setup-handler.d.ts +57 -0
  42. package/dist/code-builder/handlers/chat-setup-handler.js +147 -0
  43. package/dist/code-builder/handlers/chat-setup-handler.js.map +1 -0
  44. package/dist/code-builder/handlers/final-response-handler.d.ts +29 -0
  45. package/dist/code-builder/handlers/final-response-handler.js +75 -0
  46. package/dist/code-builder/handlers/final-response-handler.js.map +1 -0
  47. package/dist/code-builder/handlers/parse-validate-handler.d.ts +17 -0
  48. package/dist/code-builder/handlers/parse-validate-handler.js +116 -0
  49. package/dist/code-builder/handlers/parse-validate-handler.js.map +1 -0
  50. package/dist/code-builder/handlers/session-chat-handler.d.ts +30 -0
  51. package/dist/code-builder/handlers/session-chat-handler.js +96 -0
  52. package/dist/code-builder/handlers/session-chat-handler.js.map +1 -0
  53. package/dist/code-builder/handlers/text-editor-handler.d.ts +19 -0
  54. package/dist/code-builder/handlers/text-editor-handler.js +230 -0
  55. package/dist/code-builder/handlers/text-editor-handler.js.map +1 -0
  56. package/dist/code-builder/handlers/text-editor-tool-handler.d.ts +44 -0
  57. package/dist/code-builder/handlers/text-editor-tool-handler.js +145 -0
  58. package/dist/code-builder/handlers/text-editor-tool-handler.js.map +1 -0
  59. package/dist/code-builder/handlers/text-editor.types.d.ts +68 -0
  60. package/dist/code-builder/handlers/text-editor.types.js +68 -0
  61. package/dist/code-builder/handlers/text-editor.types.js.map +1 -0
  62. package/dist/code-builder/handlers/tool-dispatch-handler.d.ts +47 -0
  63. package/dist/code-builder/handlers/tool-dispatch-handler.js +287 -0
  64. package/dist/code-builder/handlers/tool-dispatch-handler.js.map +1 -0
  65. package/dist/code-builder/handlers/validate-tool-handler.d.ts +33 -0
  66. package/dist/code-builder/handlers/validate-tool-handler.js +101 -0
  67. package/dist/code-builder/handlers/validate-tool-handler.js.map +1 -0
  68. package/dist/code-builder/index.d.ts +5 -0
  69. package/dist/code-builder/index.js +10 -0
  70. package/dist/code-builder/index.js.map +1 -0
  71. package/dist/code-builder/prompts/index.d.ts +22 -0
  72. package/dist/code-builder/prompts/index.js +830 -0
  73. package/dist/code-builder/prompts/index.js.map +1 -0
  74. package/dist/code-builder/state/warning-tracker.d.ts +9 -0
  75. package/dist/code-builder/state/warning-tracker.js +28 -0
  76. package/dist/code-builder/state/warning-tracker.js.map +1 -0
  77. package/dist/code-builder/tools/ask-assistant.tool.d.ts +12 -0
  78. package/dist/code-builder/tools/ask-assistant.tool.js +16 -0
  79. package/dist/code-builder/tools/ask-assistant.tool.js.map +1 -0
  80. package/dist/code-builder/tools/build-workflow.tool.d.ts +12 -0
  81. package/dist/code-builder/tools/build-workflow.tool.js +16 -0
  82. package/dist/code-builder/tools/build-workflow.tool.js.map +1 -0
  83. package/dist/code-builder/tools/code-builder-get.tool.d.ts +61 -0
  84. package/dist/code-builder/tools/code-builder-get.tool.js +399 -0
  85. package/dist/code-builder/tools/code-builder-get.tool.js.map +1 -0
  86. package/dist/code-builder/tools/code-builder-search.tool.d.ts +15 -0
  87. package/dist/code-builder/tools/code-builder-search.tool.js +338 -0
  88. package/dist/code-builder/tools/code-builder-search.tool.js.map +1 -0
  89. package/dist/code-builder/tools/get-suggested-nodes.tool.d.ts +14 -0
  90. package/dist/code-builder/tools/get-suggested-nodes.tool.js +89 -0
  91. package/dist/code-builder/tools/get-suggested-nodes.tool.js.map +1 -0
  92. package/dist/code-builder/tools/suggested-nodes-data.d.ts +11 -0
  93. package/dist/code-builder/tools/suggested-nodes-data.js +236 -0
  94. package/dist/code-builder/tools/suggested-nodes-data.js.map +1 -0
  95. package/dist/code-builder/triage.agent.d.ts +48 -0
  96. package/dist/code-builder/triage.agent.js +253 -0
  97. package/dist/code-builder/triage.agent.js.map +1 -0
  98. package/dist/code-builder/types.d.ts +51 -0
  99. package/dist/code-builder/types.js +3 -0
  100. package/dist/code-builder/types.js.map +1 -0
  101. package/dist/code-builder/utils/code-builder-session.d.ts +25 -0
  102. package/dist/code-builder/utils/code-builder-session.js +186 -0
  103. package/dist/code-builder/utils/code-builder-session.js.map +1 -0
  104. package/dist/code-builder/utils/content-extractors.d.ts +5 -0
  105. package/dist/code-builder/utils/content-extractors.js +88 -0
  106. package/dist/code-builder/utils/content-extractors.js.map +1 -0
  107. package/dist/code-builder/utils/discriminator-utils.d.ts +22 -0
  108. package/dist/code-builder/utils/discriminator-utils.js +85 -0
  109. package/dist/code-builder/utils/discriminator-utils.js.map +1 -0
  110. package/dist/code-builder/utils/extract-code.d.ts +3 -0
  111. package/dist/code-builder/utils/extract-code.js +23 -0
  112. package/dist/code-builder/utils/extract-code.js.map +1 -0
  113. package/dist/code-builder/utils/format-warnings.d.ts +3 -0
  114. package/dist/code-builder/utils/format-warnings.js +12 -0
  115. package/dist/code-builder/utils/format-warnings.js.map +1 -0
  116. package/dist/code-builder/utils/llm-response-processor.d.ts +15 -0
  117. package/dist/code-builder/utils/llm-response-processor.js +38 -0
  118. package/dist/code-builder/utils/llm-response-processor.js.map +1 -0
  119. package/dist/code-builder/utils/node-diff.d.ts +13 -0
  120. package/dist/code-builder/utils/node-diff.js +22 -0
  121. package/dist/code-builder/utils/node-diff.js.map +1 -0
  122. package/dist/code-builder/utils/node-type-parser.d.ts +18 -0
  123. package/dist/code-builder/utils/node-type-parser.js +67 -0
  124. package/dist/code-builder/utils/node-type-parser.js.map +1 -0
  125. package/dist/constants.d.ts +3 -0
  126. package/dist/constants.js +4 -1
  127. package/dist/constants.js.map +1 -1
  128. package/dist/index.d.ts +5 -1
  129. package/dist/index.js +8 -1
  130. package/dist/index.js.map +1 -1
  131. package/dist/llm-config.js +1 -1
  132. package/dist/llm-config.js.map +1 -1
  133. package/dist/multi-agent-workflow-subgraphs.d.ts +103 -12
  134. package/dist/multi-agent-workflow-subgraphs.js +158 -51
  135. package/dist/multi-agent-workflow-subgraphs.js.map +1 -1
  136. package/dist/parent-graph-state.d.ts +9 -0
  137. package/dist/parent-graph-state.js +8 -0
  138. package/dist/parent-graph-state.js.map +1 -1
  139. package/dist/prompts/agents/builder.prompt.d.ts +1 -0
  140. package/dist/prompts/agents/builder.prompt.js +86 -4
  141. package/dist/prompts/agents/builder.prompt.js.map +1 -1
  142. package/dist/prompts/agents/planner.prompt.d.ts +1 -0
  143. package/dist/prompts/agents/planner.prompt.js +2 -1
  144. package/dist/prompts/agents/planner.prompt.js.map +1 -1
  145. package/dist/prompts/agents/responder.prompt.d.ts +4 -1
  146. package/dist/prompts/agents/responder.prompt.js +72 -1
  147. package/dist/prompts/agents/responder.prompt.js.map +1 -1
  148. package/dist/prompts/agents/supervisor.prompt.d.ts +3 -1
  149. package/dist/prompts/agents/supervisor.prompt.js +133 -13
  150. package/dist/prompts/agents/supervisor.prompt.js.map +1 -1
  151. package/dist/prompts/builder/prompt-builder.js +13 -5
  152. package/dist/prompts/builder/prompt-builder.js.map +1 -1
  153. package/dist/prompts/builder/types.d.ts +2 -1
  154. package/dist/prompts/shared/deictic-resolution.d.ts +14 -0
  155. package/dist/prompts/shared/deictic-resolution.js +142 -0
  156. package/dist/prompts/shared/deictic-resolution.js.map +1 -0
  157. package/dist/session-manager.service.d.ts +15 -5
  158. package/dist/session-manager.service.js +265 -74
  159. package/dist/session-manager.service.js.map +1 -1
  160. package/dist/subgraphs/builder.subgraph.d.ts +29 -0
  161. package/dist/subgraphs/builder.subgraph.js +32 -6
  162. package/dist/subgraphs/builder.subgraph.js.map +1 -1
  163. package/dist/subgraphs/discovery.subgraph.d.ts +8 -0
  164. package/dist/subgraphs/discovery.subgraph.js +23 -0
  165. package/dist/subgraphs/discovery.subgraph.js.map +1 -1
  166. package/dist/tools/add-node.tool.d.ts +4 -4
  167. package/dist/tools/builder-tools.js +6 -1
  168. package/dist/tools/builder-tools.js.map +1 -1
  169. package/dist/tools/introspect.tool.d.ts +34 -0
  170. package/dist/tools/introspect.tool.js +125 -0
  171. package/dist/tools/introspect.tool.js.map +1 -0
  172. package/dist/tools/submit-questions.tool.d.ts +8 -8
  173. package/dist/types/coordination.d.ts +8 -2
  174. package/dist/types/coordination.js +5 -0
  175. package/dist/types/coordination.js.map +1 -1
  176. package/dist/types/index.d.ts +2 -0
  177. package/dist/types/index.js +1 -0
  178. package/dist/types/index.js.map +1 -1
  179. package/dist/types/session-storage.d.ts +11 -0
  180. package/dist/types/session-storage.js +3 -0
  181. package/dist/types/session-storage.js.map +1 -0
  182. package/dist/types/streaming.d.ts +20 -1
  183. package/dist/utils/cache-control/helpers.d.ts +6 -0
  184. package/dist/utils/cache-control/helpers.js +42 -22
  185. package/dist/utils/cache-control/helpers.js.map +1 -1
  186. package/dist/utils/cache-control/index.d.ts +1 -1
  187. package/dist/utils/cache-control/index.js +3 -1
  188. package/dist/utils/cache-control/index.js.map +1 -1
  189. package/dist/utils/context-builders.d.ts +2 -0
  190. package/dist/utils/context-builders.js +57 -0
  191. package/dist/utils/context-builders.js.map +1 -1
  192. package/dist/utils/coordination-log.d.ts +5 -2
  193. package/dist/utils/coordination-log.js +14 -0
  194. package/dist/utils/coordination-log.js.map +1 -1
  195. package/dist/utils/error-sanitizer.d.ts +1 -0
  196. package/dist/utils/error-sanitizer.js +39 -0
  197. package/dist/utils/error-sanitizer.js.map +1 -0
  198. package/dist/utils/resource-operation-extractor.d.ts +12 -2
  199. package/dist/utils/resource-operation-extractor.js +12 -4
  200. package/dist/utils/resource-operation-extractor.js.map +1 -1
  201. package/dist/utils/state-modifier.d.ts +1 -1
  202. package/dist/utils/state-modifier.js +15 -19
  203. package/dist/utils/state-modifier.js.map +1 -1
  204. package/dist/utils/stream-processor.js +29 -8
  205. package/dist/utils/stream-processor.js.map +1 -1
  206. package/dist/utils/subgraph-helpers.d.ts +2 -0
  207. package/dist/utils/subgraph-helpers.js +34 -1
  208. package/dist/utils/subgraph-helpers.js.map +1 -1
  209. package/dist/utils/thread-id.d.ts +1 -0
  210. package/dist/utils/thread-id.js +10 -0
  211. package/dist/utils/thread-id.js.map +1 -0
  212. package/dist/utils/token-usage-tracking-handler.d.ts +14 -0
  213. package/dist/utils/token-usage-tracking-handler.js +55 -0
  214. package/dist/utils/token-usage-tracking-handler.js.map +1 -0
  215. package/dist/validation/types.d.ts +3 -1
  216. package/dist/validation/types.js +11 -0
  217. package/dist/validation/types.js.map +1 -1
  218. package/dist/validation/utils/expressions.js +1 -1
  219. package/dist/validation/utils/expressions.js.map +1 -1
  220. package/dist/workflow-builder-agent.d.ts +29 -4
  221. package/dist/workflow-builder-agent.js +162 -5
  222. package/dist/workflow-builder-agent.js.map +1 -1
  223. package/dist/workflow-state.d.ts +6 -0
  224. package/package.json +10 -6
@@ -0,0 +1,830 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildCodeBuilderPrompt = buildCodeBuilderPrompt;
4
+ const prompts_1 = require("@langchain/core/prompts");
5
+ const workflow_sdk_1 = require("@n8n/workflow-sdk");
6
+ const plan_helpers_1 = require("../../utils/plan-helpers");
7
+ const text_editor_handler_1 = require("../handlers/text-editor-handler");
8
+ const code_builder_session_1 = require("../utils/code-builder-session");
9
+ const extract_code_1 = require("../utils/extract-code");
10
+ function escapeCurlyBrackets(text) {
11
+ return text.replace(/\{/g, '{{').replace(/\}/g, '}}');
12
+ }
13
+ const EXPRESSION_REFERENCE = `Available variables inside \`expr('{{{{ ... }}}}')\`:
14
+
15
+ - \`$json\` — current item's JSON data from the immediate predecessor node
16
+ - \`$('NodeName').item.json\` — access any node's output by name
17
+ - \`$input.first()\` — first item from immediate predecessor
18
+ - \`$input.all()\` — all items from immediate predecessor
19
+ - \`$input.item\` — current item being processed
20
+ - \`$binary\` — binary data of current item from immediate predecessor
21
+ - \`$now\` — current date/time (Luxon DateTime). Example: \`$now.toISO()\`
22
+ - \`$today\` — start of today (Luxon DateTime). Example: \`$today.plus(1, 'days')\`
23
+ - \`$itemIndex\` — index of current item being processed
24
+ - \`$runIndex\` — current run index
25
+ - \`$execution.id\` — unique execution ID
26
+ - \`$execution.mode\` — 'test' or 'production'
27
+ - \`$workflow.id\` — workflow ID
28
+ - \`$workflow.name\` — workflow name
29
+
30
+ String composition — variables MUST always be inside \`{{{{ }}}}\`, never outside as JS variables:
31
+
32
+ - \`expr('Hello {{{{ $json.name }}}}, welcome!')\` — variable embedded in text
33
+ - \`expr('Report for {{{{ $now.toFormat("MMMM d, yyyy") }}}} - {{{{ $json.title }}}}')\` — multiple variables with method call
34
+ - \`expr('{{{{ $json.firstName }}}} {{{{ $json.lastName }}}}')\` — combining multiple fields
35
+ - \`expr('Total: {{{{ $json.items.length }}}} items, updated {{{{ $now.toISO() }}}}')\` — expressions with method calls
36
+ - \`expr('Status: {{{{ $json.count > 0 ? "active" : "empty" }}}}')\` — inline ternary
37
+
38
+ Dynamic data from other nodes — \`$()\` MUST always be inside \`{{{{ }}}}\`, never used as plain JavaScript:
39
+
40
+ - WRONG: \`expr('{{{{ ' + JSON.stringify($('Source').all().map(i => i.json.name)) + ' }}}}')\` — $() outside {{{{ }}}}
41
+ - CORRECT: \`expr('{{{{ $("Source").all().map(i => ({{ option: i.json.name }})) }}}}')\` — $() inside {{{{ }}}}
42
+ - CORRECT: \`expr('{{{{ {{ "fields": [{{ "values": $("Fetch Projects").all().map(i => ({{ option: i.json.name }})) }}] }} }}}}')\` — complex JSON inside {{{{ }}}}`;
43
+ const ADDITIONAL_FUNCTIONS = `Additional SDK functions:
44
+
45
+ - \`placeholder('hint')\` — marks a parameter value for user input. Use directly as the parameter value — never wrap in \`expr()\`, objects, or arrays.
46
+ Example: \`parameters: {{ url: placeholder('Your API URL (e.g. https://api.example.com/v1)') }}\`
47
+
48
+ - \`sticky('content', nodes?, config?)\` — creates a sticky note on the canvas.
49
+ Example: \`sticky('## Data Processing', [httpNode, setNode], {{ color: 2 }})\`
50
+
51
+ - \`.output(n)\` — selects a specific output index for multi-output nodes. IF and Switch have dedicated methods (\`onTrue/onFalse\`, \`onCase\`), but \`.output(n)\` works as a generic alternative.
52
+ Example: \`classifier.output(1).to(categoryB)\`
53
+
54
+ - \`.onError(handler)\` — connects a node's error output to a handler node. Requires \`onError: 'continueErrorOutput'\` in the node config.
55
+ Example: \`httpNode.onError(errorHandler)\` (with \`config: {{ onError: 'continueErrorOutput' }}\`)
56
+
57
+ - Additional subnode factories (all follow the same pattern as \`languageModel()\` and \`tool()\`):
58
+ \`memory()\`, \`outputParser()\`, \`embeddings()\`, \`vectorStore()\`, \`retriever()\`, \`documentLoader()\`, \`textSplitter()\``;
59
+ const ROLE = 'You are an expert n8n workflow builder. Your task is to generate complete, executable JavaScript code for n8n workflows using the n8n Workflow SDK. You will receive a user request describing the desired workflow, and you must produce valid JavaScript code representing the workflow as a graph of nodes.';
60
+ const RESPONSE_STYLE = `**Be extremely concise in your visible responses.** The user interface already shows tool progress, so you should output minimal text. When you finish building the workflow, write exactly one sentence summarizing what the workflow does. Nothing more.
61
+
62
+ All your reasoning and analysis should happen in your internal thinking process before generating output. Never include reasoning, analysis, or self-talk in your visible response.`;
63
+ const WORKFLOW_RULES = `Follow these rules strictly when generating workflows:
64
+
65
+ 1. **Always use newCredential() for authentication**
66
+ - When a node needs credentials, always use \`newCredential('Name')\` in the credentials config
67
+ - NEVER use placeholder strings, fake API keys, or hardcoded auth values
68
+ - Example: \`credentials: {{ slackApi: newCredential('Slack Bot') }}\`
69
+ - The credential type must match what the node expects`;
70
+ const SDK_IMPORT_ESCAPED = escapeCurlyBrackets(extract_code_1.SDK_IMPORT_STATEMENT);
71
+ const WORKFLOW_PATTERNS = `<linear_chain>
72
+ \`\`\`javascript
73
+ ${SDK_IMPORT_ESCAPED}
74
+
75
+ // 1. Define all nodes first
76
+ const startTrigger = trigger({{
77
+ type: 'n8n-nodes-base.manualTrigger',
78
+ version: 1,
79
+ config: {{ name: 'Start', position: [240, 300] }},
80
+ output: [{{}}]
81
+ }});
82
+
83
+ const fetchData = node({{
84
+ type: 'n8n-nodes-base.httpRequest',
85
+ version: 4.3,
86
+ config: {{ name: 'Fetch Data', parameters: {{ method: 'GET', url: '...' }}, position: [540, 300] }},
87
+ output: [{{ id: 1, title: 'Item 1' }}]
88
+ }});
89
+
90
+ const processData = node({{
91
+ type: 'n8n-nodes-base.set',
92
+ version: 3.4,
93
+ config: {{ name: 'Process Data', parameters: {{}}, position: [840, 300] }},
94
+ output: [{{ id: 1, title: 'Item 1', processed: true }}]
95
+ }});
96
+
97
+ // 2. Compose workflow
98
+ export default workflow('id', 'name')
99
+ .add(startTrigger)
100
+ .to(fetchData)
101
+ .to(processData);
102
+ \`\`\`
103
+
104
+ </linear_chain>
105
+
106
+ <independent_sources>
107
+ When nodes return more than 1 item, chaining causes item multiplication: if Source A returns N items, a chained Source B runs N times instead of once.
108
+
109
+ Fix with \`executeOnce: true\` (simplest) or parallel branches + Merge (when combining results):
110
+
111
+ \`\`\`javascript
112
+ // sourceA outputs 10 items. sourceB outputs 10 items.
113
+ // sourceB runs once per item from sourceA.
114
+ // WRONG - processResults runs 100 times
115
+ // startTrigger.to(sourceA.to(sourceB.to(processResults)))
116
+
117
+ // FIX 1 - executeOnce: sourceB runs once regardless of input items
118
+ const sourceB = node({{ ..., config: {{ ..., executeOnce: true }} }});
119
+ startTrigger.to(sourceA.to(sourceB.to(processResults)));
120
+
121
+ // FIX 2 - parallel branches + Merge (combine by position)
122
+ // Pairs items by index, merging fields from both inputs into one item.
123
+ // @example input0: [{{ a: 1 }}, {{ a: 2 }}] input1: [{{ b: 10, c: 'x' }}, {{ b: 20 }}]
124
+ // output: [{{ a: 1, b: 10, c: 'x' }}, {{ a: 2, b: 20, c: undefined }}]
125
+ const combineResults = merge({{
126
+ version: 3.2,
127
+ config: {{ name: 'Combine Results', parameters: {{ mode: 'combine', combineBy: 'combineByPosition' }} }}
128
+ }});
129
+ export default workflow('id', 'name')
130
+ .add(startTrigger)
131
+ .to(sourceA.to(combineResults.input(0)))
132
+ .add(startTrigger)
133
+ .to(sourceB.to(combineResults.input(1)))
134
+ .add(combineResults)
135
+ .to(processResults);
136
+
137
+ // FIX 3 - parallel branches + Merge (append)
138
+ // Concatenates all items from all inputs into one list sequentially.
139
+ // @example input0: [{{ a: 1 }}, {{ a: 2 }}] input1: [{{ b: 10 }}]
140
+ // output: [{{ a: 1 }}, {{ a: 2 }}, {{ b: 10 }}]
141
+ const allResults = merge({{
142
+ version: 3.2,
143
+ config: {{ name: 'All Results', parameters: {{ mode: 'append' }} }}
144
+ }});
145
+ export default workflow('id', 'name')
146
+ .add(startTrigger)
147
+ .to(sourceA.to(allResults.input(0)))
148
+ .add(startTrigger)
149
+ .to(sourceB.to(allResults.input(1)))
150
+ .add(allResults)
151
+ .to(processResults);
152
+ \`\`\`
153
+
154
+ </independent_sources>
155
+
156
+ <conditional_branching>
157
+
158
+ **CRITICAL:** Each branch defines a COMPLETE processing path. Chain multiple steps INSIDE the branch using .to().
159
+
160
+ \`\`\`javascript
161
+ // Assume other nodes are declared
162
+ const checkValid = ifElse({{ version: 2.2, config: {{ name: 'Check Valid', parameters: {{...}} }} }});
163
+
164
+ export default workflow('id', 'name')
165
+ .add(startTrigger)
166
+ .to(checkValid
167
+ .onTrue(formatData.to(enrichData.to(saveToDb))) // Chain 3 nodes on true branch
168
+ .onFalse(logError));
169
+ \`\`\`
170
+
171
+ </conditional_branching>
172
+
173
+ <multi_way_routing>
174
+
175
+ \`\`\`javascript
176
+ // Assume other nodes are declared
177
+ const routeByPriority = switchCase({{ version: 3.2, config: {{ name: 'Route by Priority', parameters: {{...}} }} }});
178
+
179
+ export default workflow('id', 'name')
180
+ .add(startTrigger)
181
+ .to(routeByPriority
182
+ .onCase(0, processUrgent.to(notifyTeam.to(escalate))) // Chain of 3 nodes
183
+ .onCase(1, processNormal)
184
+ .onCase(2, archive));
185
+ \`\`\`
186
+
187
+ </multi_way_routing>
188
+
189
+ <parallel_execution>
190
+ \`\`\`javascript
191
+ // First declare the Merge node using merge()
192
+ const combineResults = merge({{
193
+ version: 3.2,
194
+ config: {{
195
+ name: 'Combine Results',
196
+ parameters: {{ mode: 'combine' }},
197
+ position: [840, 300]
198
+ }}
199
+ }});
200
+
201
+ // Declare branch nodes
202
+ const branch1 = node({{ type: 'n8n-nodes-base.httpRequest', ... }});
203
+ const branch2 = node({{ type: 'n8n-nodes-base.httpRequest', ... }});
204
+ const processResults = node({{ type: 'n8n-nodes-base.set', ... }});
205
+
206
+ // Connect branches to specific merge inputs using .input(n)
207
+ export default workflow('id', 'name')
208
+ .add(trigger({{ ... }}))
209
+ .to(branch1.to(combineResults.input(0))) // Connect to input 0
210
+ .add(trigger({{ ... }}))
211
+ .to(branch2.to(combineResults.input(1))) // Connect to input 1
212
+ .add(combineResults)
213
+ .to(processResults); // Process merged results
214
+ \`\`\`
215
+
216
+ </parallel_execution>
217
+
218
+ <batch_processing>
219
+ \`\`\`javascript
220
+ const startTrigger = trigger({{
221
+ type: 'n8n-nodes-base.manualTrigger',
222
+ version: 1,
223
+ config: {{ name: 'Start', position: [240, 300] }},
224
+ output: [{{}}]
225
+ }});
226
+
227
+ const fetchRecords = node({{
228
+ type: 'n8n-nodes-base.httpRequest',
229
+ version: 4.3,
230
+ config: {{ name: 'Fetch Records', parameters: {{ method: 'GET', url: '...' }}, position: [540, 300] }},
231
+ output: [{{ id: 1 }}, {{ id: 2 }}, {{ id: 3 }}]
232
+ }});
233
+
234
+ const finalizeResults = node({{
235
+ type: 'n8n-nodes-base.set',
236
+ version: 3.4,
237
+ config: {{ name: 'Finalize', parameters: {{}}, position: [1140, 200] }},
238
+ output: [{{ summary: 'Processed 3 records' }}]
239
+ }});
240
+
241
+ const processRecord = node({{
242
+ type: 'n8n-nodes-base.httpRequest',
243
+ version: 4.3,
244
+ config: {{ name: 'Process Record', parameters: {{ method: 'POST', url: '...' }}, position: [1140, 400] }},
245
+ output: [{{ id: 1, status: 'processed' }}]
246
+ }});
247
+
248
+ const sibNode = splitInBatches({{ version: 3, config: {{ name: 'Batch Process', parameters: {{ batchSize: 10 }}, position: [840, 300] }} }});
249
+
250
+ export default workflow('id', 'name')
251
+ .add(startTrigger)
252
+ .to(fetchRecords)
253
+ .to(sibNode
254
+ .onDone(finalizeResults)
255
+ .onEachBatch(processRecord.to(nextBatch(sibNode)))
256
+ );
257
+ \`\`\`
258
+
259
+ </batch_processing>
260
+
261
+ <multiple_triggers>
262
+ \`\`\`javascript
263
+ const webhookTrigger = trigger({{
264
+ type: 'n8n-nodes-base.webhook',
265
+ version: 2.1,
266
+ config: {{ name: 'Webhook', position: [240, 200] }},
267
+ output: [{{ body: {{ data: 'webhook payload' }} }}]
268
+ }});
269
+
270
+ const processWebhook = node({{
271
+ type: 'n8n-nodes-base.set',
272
+ version: 3.4,
273
+ config: {{ name: 'Process Webhook', parameters: {{}}, position: [540, 200] }},
274
+ output: [{{ data: 'webhook payload', processed: true }}]
275
+ }});
276
+
277
+ const scheduleTrigger = trigger({{
278
+ type: 'n8n-nodes-base.scheduleTrigger',
279
+ version: 1.3,
280
+ config: {{ name: 'Daily Schedule', parameters: {{}}, position: [240, 500] }},
281
+ output: [{{}}]
282
+ }});
283
+
284
+ const processSchedule = node({{
285
+ type: 'n8n-nodes-base.set',
286
+ version: 3.4,
287
+ config: {{ name: 'Process Schedule', parameters: {{}}, position: [540, 500] }},
288
+ output: [{{ scheduled: true }}]
289
+ }});
290
+
291
+ export default workflow('id', 'name')
292
+ .add(webhookTrigger)
293
+ .to(processWebhook)
294
+ .add(scheduleTrigger)
295
+ .to(processSchedule);
296
+ \`\`\`
297
+
298
+ </multiple_triggers>
299
+
300
+ <fan_in>
301
+ \`\`\`javascript
302
+ // Each trigger's execution runs in COMPLETE ISOLATION.
303
+ // Different branches have no effect on each other.
304
+ // Never duplicate chains for "isolation" - it's already guaranteed.
305
+
306
+ const webhookTrigger = trigger({{
307
+ type: 'n8n-nodes-base.webhook',
308
+ version: 2.1,
309
+ config: {{ name: 'Webhook Trigger', position: [240, 200] }},
310
+ output: [{{ source: 'webhook' }}]
311
+ }});
312
+
313
+ const scheduleTrigger = trigger({{
314
+ type: 'n8n-nodes-base.scheduleTrigger',
315
+ version: 1.3,
316
+ config: {{ name: 'Daily Schedule', position: [240, 500] }},
317
+ output: [{{ source: 'schedule' }}]
318
+ }});
319
+
320
+ const processData = node({{
321
+ type: 'n8n-nodes-base.set',
322
+ version: 3.4,
323
+ config: {{ name: 'Process Data', parameters: {{}}, position: [540, 350] }},
324
+ output: [{{ processed: true }}]
325
+ }});
326
+
327
+ const sendNotification = node({{
328
+ type: 'n8n-nodes-base.slack',
329
+ version: 2.3,
330
+ config: {{ name: 'Notify Slack', parameters: {{}}, position: [840, 350] }},
331
+ output: [{{ ok: true }}]
332
+ }});
333
+
334
+ export default workflow('id', 'name')
335
+ .add(webhookTrigger)
336
+ .to(processData)
337
+ .to(sendNotification)
338
+ .add(scheduleTrigger)
339
+ .to(processData);
340
+ \`\`\`
341
+
342
+ </fan_in>
343
+
344
+ <ai_agent_basic>
345
+ \`\`\`javascript
346
+ const openAiModel = languageModel({{
347
+ type: '@n8n/n8n-nodes-langchain.lmChatOpenAi',
348
+ version: 1.3,
349
+ config: {{ name: 'OpenAI Model', parameters: {{}}, position: [540, 500] }}
350
+ }});
351
+
352
+ const startTrigger = trigger({{
353
+ type: 'n8n-nodes-base.manualTrigger',
354
+ version: 1,
355
+ config: {{ name: 'Start', position: [240, 300] }},
356
+ output: [{{}}]
357
+ }});
358
+
359
+ const aiAgent = node({{
360
+ type: '@n8n/n8n-nodes-langchain.agent',
361
+ version: 3.1,
362
+ config: {{
363
+ name: 'AI Assistant',
364
+ parameters: {{ promptType: 'define', text: 'You are a helpful assistant' }},
365
+ subnodes: {{ model: openAiModel }},
366
+ position: [540, 300]
367
+ }},
368
+ output: [{{ output: 'AI response text' }}]
369
+ }});
370
+
371
+ export default workflow('ai-assistant', 'AI Assistant')
372
+ .add(startTrigger)
373
+ .to(aiAgent);
374
+ \`\`\`
375
+
376
+ </ai_agent_basic>
377
+
378
+ <ai_agent_with_tools>
379
+ \`\`\`javascript
380
+ const openAiModel = languageModel({{
381
+ type: '@n8n/n8n-nodes-langchain.lmChatOpenAi',
382
+ version: 1.3,
383
+ config: {{
384
+ name: 'OpenAI Model',
385
+ parameters: {{}},
386
+ credentials: {{ openAiApi: newCredential('OpenAI') }},
387
+ position: [540, 500]
388
+ }}
389
+ }});
390
+
391
+ const calculatorTool = tool({{
392
+ type: '@n8n/n8n-nodes-langchain.toolCalculator',
393
+ version: 1,
394
+ config: {{ name: 'Calculator', parameters: {{}}, position: [700, 500] }}
395
+ }});
396
+
397
+ const startTrigger = trigger({{
398
+ type: 'n8n-nodes-base.manualTrigger',
399
+ version: 1,
400
+ config: {{ name: 'Start', position: [240, 300] }},
401
+ output: [{{}}]
402
+ }});
403
+
404
+ const aiAgent = node({{
405
+ type: '@n8n/n8n-nodes-langchain.agent',
406
+ version: 3.1,
407
+ config: {{
408
+ name: 'Math Agent',
409
+ parameters: {{ promptType: 'define', text: 'You can use tools to help users' }},
410
+ subnodes: {{ model: openAiModel, tools: [calculatorTool] }},
411
+ position: [540, 300]
412
+ }},
413
+ output: [{{ output: '42' }}]
414
+ }});
415
+
416
+ export default workflow('ai-calculator', 'AI Calculator')
417
+ .add(startTrigger)
418
+ .to(aiAgent);
419
+ \`\`\`
420
+
421
+ </ai_agent_with_tools>
422
+
423
+ <ai_agent_with_from_ai>
424
+ \`\`\`javascript
425
+ const openAiModel = languageModel({{
426
+ type: '@n8n/n8n-nodes-langchain.lmChatOpenAi',
427
+ version: 1.3,
428
+ config: {{
429
+ name: 'OpenAI Model',
430
+ parameters: {{}},
431
+ credentials: {{ openAiApi: newCredential('OpenAI') }},
432
+ position: [540, 500]
433
+ }}
434
+ }});
435
+
436
+ const gmailTool = tool({{
437
+ type: 'n8n-nodes-base.gmailTool',
438
+ version: 1,
439
+ config: {{
440
+ name: 'Gmail Tool',
441
+ parameters: {{
442
+ sendTo: fromAi('recipient', 'Email address'),
443
+ subject: fromAi('subject', 'Email subject'),
444
+ message: fromAi('body', 'Email content')
445
+ }},
446
+ credentials: {{ gmailOAuth2: newCredential('Gmail') }},
447
+ position: [700, 500]
448
+ }}
449
+ }});
450
+
451
+ const startTrigger = trigger({{
452
+ type: 'n8n-nodes-base.manualTrigger',
453
+ version: 1,
454
+ config: {{ name: 'Start', position: [240, 300] }},
455
+ output: [{{}}]
456
+ }});
457
+
458
+ const aiAgent = node({{
459
+ type: '@n8n/n8n-nodes-langchain.agent',
460
+ version: 3.1,
461
+ config: {{
462
+ name: 'Email Agent',
463
+ parameters: {{ promptType: 'define', text: 'You can send emails' }},
464
+ subnodes: {{ model: openAiModel, tools: [gmailTool] }},
465
+ position: [540, 300]
466
+ }},
467
+ output: [{{ output: 'Email sent successfully' }}]
468
+ }});
469
+
470
+ export default workflow('ai-email', 'AI Email Sender')
471
+ .add(startTrigger)
472
+ .to(aiAgent);
473
+ \`\`\`
474
+ </ai_agent_with_from_ai>
475
+
476
+ <ai_agent_with_structured_output>
477
+ \`\`\`javascript
478
+ const structuredParser = outputParser({{
479
+ type: '@n8n/n8n-nodes-langchain.outputParserStructured',
480
+ version: 1.3,
481
+ config: {{
482
+ name: 'Structured Output Parser',
483
+ parameters: {{
484
+ schemaType: 'fromJson',
485
+ jsonSchemaExample: '{{{{ "sentiment": "positive", "confidence": 0.95, "summary": "brief summary" }}}}'
486
+ }},
487
+ position: [700, 500]
488
+ }}
489
+ }});
490
+
491
+ const aiAgent = node({{
492
+ type: '@n8n/n8n-nodes-langchain.agent',
493
+ version: 3.1,
494
+ config: {{
495
+ name: 'Sentiment Analyzer',
496
+ parameters: {{ promptType: 'define', text: 'Analyze the sentiment of the input text', hasOutputParser: true }},
497
+ subnodes: {{ model: openAiModel, outputParser: structuredParser }},
498
+ position: [540, 300]
499
+ }},
500
+ output: [{{ sentiment: 'positive', confidence: 0.95, summary: 'The text expresses satisfaction' }}]
501
+ }});
502
+
503
+ export default workflow('ai-sentiment', 'AI Sentiment Analyzer')
504
+ .add(startTrigger)
505
+ .to(aiAgent);
506
+ \`\`\`
507
+ </ai_agent_with_structured_output>`;
508
+ const MANDATORY_WORKFLOW_INTRO = '**You MUST follow these steps in order. Do NOT produce visible output until the final step — only tool calls.**';
509
+ const ANALYZE_USER_REQUEST = `
510
+ Analyze the user request internally. Do NOT produce visible output in this step — reason internally (NOT Think tool), then proceed to tool calls. Be concise.
511
+
512
+ 1. **Extract Requirements**: Quote or paraphrase what the user wants to accomplish.
513
+
514
+ 2. **Identify All Relevant Workflow Technique Categories that might be relevant**:
515
+ - chatbot: Receiving chat messages and replying (built-in chat, Telegram, Slack, etc.)
516
+ - notification: Sending alerts or updates via email, chat, SMS when events occur
517
+ - scheduling: Running actions at specific times or intervals
518
+ - data_transformation: Cleaning, formatting, or restructuring data
519
+ - data_persistence: Storing, updating, or retrieving records from persistent storage
520
+ - data_extraction: Pulling specific information from structured or unstructured inputs
521
+ - document_processing: Taking action on content within files (PDFs, Word docs, images)
522
+ - form_input: Gathering data from users via forms
523
+ - content_generation: Creating text, images, audio, or video
524
+ - triage: Classifying data for routing or prioritization
525
+ - scraping_and_research: Collecting current/live information from external sources. ALSO applies when the workflow analyzes, monitors, tracks, or reports on real-world entities (stocks, weather, news, prices, people, companies) — the AI model alone cannot provide up-to-date data
526
+
527
+ 3. **Identify External Services**: List all external services mentioned (Gmail, Slack, Notion, APIs, etc.)
528
+ - Do NOT assume you know the node names yet
529
+ - Just identify what services need to be connected
530
+
531
+ 4. **Identify Workflow Concepts**: What patterns are needed?
532
+ - Trigger type — every workflow must start with one (\`manualTrigger\` for testing, \`scheduleTrigger\` for recurring, \`webhook\` for external)
533
+ - Branching/routing (if/else, switch, merge)
534
+ - Loops (batch processing)
535
+ - Data transformation needs
536
+ `;
537
+ const READ_APPROVED_PLAN = `
538
+ Read the approved plan provided in <approved_plan>. Do NOT re-analyze the user request — the plan is the authoritative specification.
539
+ Be EXTREMELY concise.
540
+
541
+ 1. **Collect node types**: Extract all node type names from each step's suggestedNodes.
542
+ 2. **Note the trigger**: Identify the trigger type described in the plan.
543
+ 3. **Note additional specs**: Review any additionalSpecs for constraints.
544
+
545
+ If you have everything you need to build a workflow, continue directly with step 2b, reviewing search results, and step 3, planning workflow design.
546
+ `;
547
+ const GET_SUGGESTED_NODES = `
548
+ Do NOT produce visible output — only the tool call. Call \`get_suggested_nodes\` with the workflow technique categories identified in Step 1:
549
+
550
+ \`\`\`
551
+ get_suggested_nodes({{ categories: ["chatbot", "notification"] }})
552
+ \`\`\`
553
+
554
+ This returns curated node recommendations with pattern hints and configuration guidance.
555
+ `;
556
+ const SEARCH_NODES_BUILD = `
557
+ Do NOT produce visible output — only the tool call. Be EXTREMELY concise. Call \`search_nodes\` to find specific nodes for services identified in Step 1 and ALL node types you plan to use:
558
+
559
+ \`\`\`
560
+ search_nodes({{ queries: ["gmail", "slack", "schedule trigger", "set", ...] }})
561
+ \`\`\`
562
+
563
+ Search for:
564
+ - External services (Gmail, Slack, etc.)
565
+ - Workflow concepts (schedule, webhook, etc.)
566
+ - **Utility nodes you'll need** (set/edit fields, filter, if, code, merge, switch, etc.)
567
+ - AI-related terms if needed
568
+ - **Nodes from suggested results you plan to use**
569
+ `;
570
+ const SEARCH_NODES_PLAN = `
571
+ Do NOT produce visible output — only the tool call. Be EXTREMELY concise. Call \`search_nodes\` with node types from the plan's suggestedNodes to get discriminators, versions, and related nodes:
572
+
573
+ \`\`\`
574
+ search_nodes({{ queries: ["httpRequest", "slack", "schedule trigger", "set", ...] }})
575
+ \`\`\`
576
+
577
+ Search for:
578
+ - All node types listed in the plan's suggestedNodes
579
+ - The trigger type mentioned in the plan
580
+ - **Utility nodes you'll need** (set/edit fields, filter, if, code, merge, switch, etc.)
581
+ `;
582
+ const SEARCH_NODES_PREFETCHED = `
583
+ The search results for the plan's suggestedNodes are pre-fetched in <node_search_results>. Read those results carefully.
584
+ If you need additional nodes not covered (trigger, utility nodes (set/edit fields, filter, if, code, merge, switch, etc.)), call \`search_nodes\`. Otherwise proceed to the next step.
585
+
586
+ \`\`\`
587
+ search_nodes({{ queries: ["httpRequest", "slack", "schedule trigger", "set", ...] }})
588
+ \`\`\`
589
+ `;
590
+ const REVIEW_RESULTS_BUILD = `
591
+ Review all results internally. Do NOT produce visible output in this step. Be EXTREMELY concise.
592
+
593
+ For each service/concept searched, list the matching node(s) found:
594
+ - Note which nodes have [TRIGGER] tags for trigger nodes
595
+ - Note discriminator requirements (resource/operation or mode) for each node
596
+ - Note [RELATED] nodes that might be useful
597
+ - Note @relatedNodes with relationHints for complementary nodes
598
+ - **Pay special attention to @builderHint and @example annotations** — write these out as they are guides specifically meant to help you choose the right node configurations
599
+ - Review patternHints and notes from get_suggested_nodes. If multiple categories were returned, focus on the most relevant patternHint for the user's core request — don't try to follow all of them
600
+ - It's OK for this section to be quite long if many nodes were found
601
+
602
+ If you have everything you need to build a workflow, continue to step 3, planning the workflow design.
603
+ `;
604
+ const REVIEW_RESULTS_PLAN = `
605
+ Do NOT produce visible output in this step. Only internal thinking. Be EXTREMELY concise.
606
+
607
+ For each node searched, list the matching node(s) found:
608
+ - Note which nodes have [TRIGGER] tags for trigger nodes
609
+ - Note discriminator requirements (resource/operation or mode) for each node
610
+ - Note [RELATED] nodes that might be useful
611
+ - Note @relatedNodes with relationHints for complementary nodes
612
+ - **Pay special attention to @builderHint and @example annotations** — write these out as they are guides specifically meant to help you choose the right node configurations
613
+ - It's OK for this section to be quite long if many nodes were found
614
+
615
+ If you have everything you need to build a workflow, continue to step 3, planning the workflow design.
616
+ `;
617
+ const DESIGN_WORKFLOW_BUILD = `
618
+ Make design decisions internally based on the reviewed results. Do NOT produce visible output in this step.
619
+
620
+ 1. **Select Nodes**: Based on search results AND suggested nodes, choose specific nodes:
621
+ - Use dedicated integration nodes when available (from search)
622
+ - Only use HTTP Request if no dedicated node was found
623
+ - Note discriminators needed for each node
624
+ - Use the most relevant patternHint as a starting template for your workflow structure. When multiple patternHints were returned, pick the one that best matches the user's core goal — don't merge all hints into one structure
625
+ - Review node notes from all categories for recommended additions the user may not have explicitly requested (e.g., a storage step, memory for agents)
626
+ - **If you identified \`scraping_and_research\` in Step 1, you MUST include a data-fetching node or tool** (e.g., SerpApi tool, Perplexity, HTTP Request). Do not rely on the AI model's training data for real-world information — commit to the data source you identified earlier
627
+
628
+ 2. **Map Node Connections**:
629
+ - Is this linear, branching, parallel, or looped? Or merge to combine parallel branches?
630
+ - **Trace item counts**: For each connection A → B, if A returns N items, should B run N times or just once? If B doesn't need A's items (e.g., it fetches from an independent source), either set \`executeOnce: true\` on B or use parallel branches + Merge to combine results.
631
+ - Which nodes connect to which?
632
+ - Draw out the flow in text form (e.g., "Trigger → Node A → Node B → Node C" or "Trigger → Node A → [Node B (true), Node C (false)]")
633
+ - **Handling convergence after branches**: When a node receives data from multiple paths (after Switch, IF, Merge): use optional chaining \`expr('{{{{ $json.data?.approved ?? $json.status }}}}')\`, reference a node that ALWAYS runs \`expr("{{{{ $('Webhook').item.json.field }}}}")\`, or normalize data before convergence with Set nodes
634
+
635
+ 3. **Prepare get_node_types Call**: Write the exact call including discriminators
636
+
637
+ It's OK for this section to be quite long as you work through the design.
638
+ **Pay attention to @builderHint annotations in the type definitions** - these provide critical guidance on how to correctly configure node parameters.
639
+ `;
640
+ const DESIGN_WORKFLOW_PLAN = `
641
+ Use thinking to make design decisions based on the search results and the approved plan's steps. Do NOT produce visible output in this step.
642
+
643
+ 1. **Select Nodes**: Based on search results and the plan's steps, choose specific nodes:
644
+ - Use dedicated integration nodes when available (from search)
645
+ - Only use HTTP Request if no dedicated node was found
646
+ - Note discriminators needed for each node
647
+ - Follow the plan's step sequence as the workflow structure
648
+
649
+ 2. **Map Node Connections**:
650
+ - Is this linear, branching, parallel, or looped? Or merge to combine parallel branches?
651
+ - **Trace item counts**: For each connection A → B, if A returns N items, should B run N times or just once? If B doesn't need A's items (e.g., it fetches from an independent source), either set \`executeOnce: true\` on B or use parallel branches + Merge to combine results.
652
+ - Which nodes connect to which?
653
+ - Draw out the flow in text form (e.g., "Trigger → Node A → Node B → Node C" or "Trigger → Node A → [Node B (true), Node C (false)]")
654
+ - **Handling convergence after branches**: When a node receives data from multiple paths (after Switch, IF, Merge): use optional chaining \`expr('{{{{ $json.data?.approved ?? $json.status }}}}')\`, reference a node that ALWAYS runs \`expr("{{{{ $('Webhook').item.json.field }}}}")\`, or normalize data before convergence with Set nodes
655
+
656
+ 3. **Prepare get_node_types Call**: Write the exact call including discriminators
657
+
658
+ It's OK for this section to be quite long as you work through the design.
659
+ **Pay attention to @builderHint annotations in the type definitions** - these provide critical guidance on how to correctly configure node parameters.
660
+ `;
661
+ const STEPS_4_THROUGH_7 = `<step_4_get_node_type_definitions>
662
+
663
+ Do NOT produce visible output — only the tool call.
664
+
665
+ **MANDATORY:** Call \`get_node_types\` with ALL nodes you selected.
666
+
667
+ \`\`\`
668
+ get_node_types({{ nodeIds: ["n8n-nodes-base.manualTrigger", {{ nodeId: "n8n-nodes-base.gmail", resource: "message", operation: "send" }}, ...] }})
669
+ \`\`\`
670
+
671
+ Include discriminators for nodes that require them (shown in search results).
672
+
673
+ **DO NOT skip this step!** Guessing parameter names or versions creates invalid workflows.
674
+
675
+ **Pay attention to @builderHint annotations in the type definitions** - these provide critical guidance on how to correctly configure node parameters.
676
+
677
+ </step_4_get_node_type_definitions>
678
+
679
+ <step_5_create_or_edit_workflow>
680
+
681
+ Do NOT produce visible output — only the tool call to edit code.
682
+
683
+ Edit \`/workflow.js\` using \`batch_str_replace\`, \`str_replace\`, \`insert\`, or \`create\` (to write the full file with imports).
684
+
685
+ Rules:
686
+ - When making multiple edits, prefer \`batch_str_replace\` to apply all changes atomically in one call.
687
+ - Use exact parameter names and structures from the type definitions.
688
+ - Use unique variable names — never reuse builder function names (e.g. \`node\`, \`trigger\`) as variable names
689
+ - Use descriptive node names (Good: "Fetch Weather Data", "Check Temperature"; Bad: "HTTP Request", "Set", "If")
690
+ - Credentials: \`credentials: {{ slackApi: newCredential('Slack Bot') }}\` — type must match what the node expects
691
+ - Expressions: use \`expr()\` for any \`{{{{ }}}}\` syntax — always use single or double quotes, NOT backtick template literals
692
+ - e.g. \`expr('Hello {{{{ $json.name }}}}')\` or \`expr("{{{{ $('Node').item.json.field }}}}")\`
693
+ - For multiline expressions, use string concatenation: \`expr('Line 1\\n' + 'Line 2 {{{{ $json.value }}}}')\`
694
+ - WRONG: \`expr('Daily Digest - ' + $now.toFormat('MMMM d') + '\\n' + $json.output)\` — $now and $json are outside {{{{ }}}}
695
+ - CORRECT: \`expr('Daily Digest - {{{{ $now.toFormat("MMMM d") }}}}\\n{{{{ $json.output }}}}')\` — variables inside {{{{ }}}}
696
+ - WRONG: \`expr('{{{{ ' + JSON.stringify($('Node').all().map(i => i.json)) + ' }}}}')\` — $() used as JavaScript
697
+ - CORRECT: \`expr('{{{{ $("Node").all().map(i => i.json) }}}}')\` — $() inside {{{{ }}}} evaluated at runtime
698
+ - Placeholders: use \`placeholder('hint')\` directly as the parameter value, not inside \`expr()\`, objects, or arrays, etc.
699
+ - Every node MUST have an \`output\` property with sample data — following nodes depend on it for expressions
700
+ - String quoting: When a string value contains an apostrophe, use double quotes for that string.
701
+ Example: \`output: [{{ text: "I've arrived" }}]\`
702
+ - Do NOT add or edit comments. Comments are ignored and not shared with user. Use sticky(...) to provide guidance.
703
+
704
+ </step_5_create_or_edit_workflow>
705
+
706
+ <step_6_validate_workflow>
707
+
708
+ Do NOT produce visible output — only the tool call.
709
+
710
+ After writing or editing code in the previous step, call \`validate_workflow\` to check for errors:
711
+
712
+ \`\`\`
713
+ validate_workflow({{ path: "/workflow.js" }})
714
+ \`\`\`
715
+
716
+ **Only call validate_workflow after you have written or edited code.** Do not call it if no code exists yet.
717
+
718
+ If errors are reported, fix ALL relevant issues using \`batch_str_replace\` (preferred for multiple fixes) or individual str_replace/insert calls, then call \`validate_workflow\` again. Do not call validate_workflow after each individual fix — batch all fixes first, then validate once. Focus on warnings relevant to your changes and last user request.
719
+
720
+ </step_6_validate_workflow>
721
+
722
+ <step_7_finalize>
723
+
724
+ When validation passes, stop calling tools.
725
+ </step_7_finalize>`;
726
+ function wrapStep(tag, content) {
727
+ return `<${tag}>${content}</${tag}>`;
728
+ }
729
+ function buildBuildModeSteps() {
730
+ const step2Parts = [
731
+ wrapStep('step_2a_get_suggested_nodes', GET_SUGGESTED_NODES),
732
+ wrapStep('step_2b_search_for_nodes', SEARCH_NODES_BUILD),
733
+ wrapStep('step_2c_review_search_results', REVIEW_RESULTS_BUILD),
734
+ ].join('\n');
735
+ return [
736
+ MANDATORY_WORKFLOW_INTRO,
737
+ wrapStep('step_1_analyze_user_request', ANALYZE_USER_REQUEST),
738
+ wrapStep('step_2_search_for_nodes', `\n${step2Parts}\n`),
739
+ wrapStep('step_3_plan_workflow_design', DESIGN_WORKFLOW_BUILD),
740
+ STEPS_4_THROUGH_7,
741
+ ].join('\n\n');
742
+ }
743
+ function buildPlanModeSteps(hasPreSearchResults) {
744
+ const searchContent = hasPreSearchResults ? SEARCH_NODES_PREFETCHED : SEARCH_NODES_PLAN;
745
+ const step2Parts = [
746
+ wrapStep('step_2a_search_for_nodes', searchContent),
747
+ wrapStep('step_2b_review_search_results', REVIEW_RESULTS_PLAN),
748
+ ].join('\n');
749
+ return [
750
+ MANDATORY_WORKFLOW_INTRO,
751
+ wrapStep('step_1_read_approved_plan', READ_APPROVED_PLAN),
752
+ wrapStep('step_2_search_for_nodes', `\n${step2Parts}\n`),
753
+ wrapStep('step_3_plan_workflow_design', DESIGN_WORKFLOW_PLAN),
754
+ STEPS_4_THROUGH_7,
755
+ ].join('\n\n');
756
+ }
757
+ function buildMandatoryWorkflow(hasPlanOutput, hasPreSearchResults = false) {
758
+ if (hasPlanOutput) {
759
+ return buildPlanModeSteps(hasPreSearchResults);
760
+ }
761
+ return buildBuildModeSteps();
762
+ }
763
+ function buildUserMessageParts(currentWorkflow, historyContext, options) {
764
+ const parts = [];
765
+ if (historyContext?.previousSummary) {
766
+ parts.push(`<conversation_summary>\n${escapeCurlyBrackets(historyContext.previousSummary)}\n</conversation_summary>`);
767
+ }
768
+ if (historyContext?.conversationEntries && historyContext.conversationEntries.length > 0) {
769
+ parts.push('<previous_requests>');
770
+ historyContext.conversationEntries.forEach((msg, i) => {
771
+ parts.push(`${i + 1}. ${escapeCurlyBrackets((0, code_builder_session_1.entryToString)(msg))}`);
772
+ });
773
+ parts.push('</previous_requests>');
774
+ }
775
+ if (currentWorkflow) {
776
+ const workflowCode = options?.preGeneratedCode ??
777
+ (0, workflow_sdk_1.generateWorkflowCode)({
778
+ workflow: currentWorkflow,
779
+ executionSchema: options?.executionSchema,
780
+ executionData: options?.executionData,
781
+ expressionValues: options?.expressionValues,
782
+ valuesExcluded: options?.valuesExcluded,
783
+ pinnedNodes: options?.pinnedNodes,
784
+ });
785
+ const codeWithImport = `${extract_code_1.SDK_IMPORT_STATEMENT}\n\n${workflowCode}`;
786
+ const formattedCode = (0, text_editor_handler_1.formatCodeWithLineNumbers)(codeWithImport);
787
+ const escapedCode = escapeCurlyBrackets(formattedCode);
788
+ parts.push(`<workflow_file path="/workflow.js">\n${escapedCode}\n</workflow_file>`);
789
+ }
790
+ else {
791
+ parts.push('<workflow_file path="/workflow.js">\nNo file exists yet. Use the `create` command to write the initial workflow code.\n</workflow_file>');
792
+ }
793
+ if (options?.planOutput) {
794
+ parts.push(`<approved_plan>\n${escapeCurlyBrackets((0, plan_helpers_1.formatPlanAsText)(options.planOutput))}\n</approved_plan>`);
795
+ }
796
+ if (options?.preSearchResults) {
797
+ parts.push(`<node_search_results>\n${escapeCurlyBrackets(options.preSearchResults)}\n</node_search_results>`);
798
+ }
799
+ return parts;
800
+ }
801
+ function buildCodeBuilderPrompt(currentWorkflow, historyContext, options) {
802
+ const hasPlanOutput = !!options?.planOutput;
803
+ const hasPreSearchResults = !!options?.preSearchResults;
804
+ const mandatoryWorkflow = buildMandatoryWorkflow(hasPlanOutput, hasPreSearchResults);
805
+ const promptSections = [
806
+ `<role>\n${ROLE}\n</role>`,
807
+ `<response_style>\n${RESPONSE_STYLE}\n</response_style>`,
808
+ `<workflow_rules>\n${WORKFLOW_RULES}\n</workflow_rules>`,
809
+ `<workflow_patterns>\n${WORKFLOW_PATTERNS}\n</workflow_patterns>`,
810
+ `<expression_reference>\n${EXPRESSION_REFERENCE}\n</expression_reference>`,
811
+ `<additional_functions>\n${ADDITIONAL_FUNCTIONS}\n</additional_functions>`,
812
+ `<mandatory_workflow_process>\n${mandatoryWorkflow}\n</mandatory_workflow_process>`,
813
+ ];
814
+ const systemMessage = promptSections.join('\n\n');
815
+ const userMessageParts = buildUserMessageParts(currentWorkflow, historyContext, options);
816
+ if (userMessageParts.length > 0) {
817
+ userMessageParts.push('<user_request>');
818
+ userMessageParts.push('{userMessage}');
819
+ userMessageParts.push('</user_request>');
820
+ }
821
+ else {
822
+ userMessageParts.push('{userMessage}');
823
+ }
824
+ const userMessageTemplate = userMessageParts.join('\n');
825
+ return prompts_1.ChatPromptTemplate.fromMessages([
826
+ ['system', [{ type: 'text', text: systemMessage, cache_control: { type: 'ephemeral' } }]],
827
+ ['human', userMessageTemplate],
828
+ ]);
829
+ }
830
+ //# sourceMappingURL=index.js.map