@elizaos/plugin-workflow 2.0.0-beta.1

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 (294) hide show
  1. package/README.md +71 -0
  2. package/auto-enable.ts +18 -0
  3. package/dist/actions/index.d.ts +2 -0
  4. package/dist/actions/index.d.ts.map +1 -0
  5. package/dist/actions/index.js +2 -0
  6. package/dist/actions/index.js.map +1 -0
  7. package/dist/actions/workflow.d.ts +23 -0
  8. package/dist/actions/workflow.d.ts.map +1 -0
  9. package/dist/actions/workflow.js +425 -0
  10. package/dist/actions/workflow.js.map +1 -0
  11. package/dist/data/defaultNodes.json +9887 -0
  12. package/dist/data/schemaIndex.json +1 -0
  13. package/dist/data/triggerSchemaIndex.json +1 -0
  14. package/dist/db/index.d.ts +2 -0
  15. package/dist/db/index.d.ts.map +1 -0
  16. package/dist/db/index.js +2 -0
  17. package/dist/db/index.js.map +1 -0
  18. package/dist/db/schema.d.ts +588 -0
  19. package/dist/db/schema.d.ts.map +1 -0
  20. package/dist/db/schema.js +59 -0
  21. package/dist/db/schema.js.map +1 -0
  22. package/dist/index.d.ts +34 -0
  23. package/dist/index.d.ts.map +1 -0
  24. package/dist/index.js +126 -0
  25. package/dist/index.js.map +1 -0
  26. package/dist/lib/automations-builder.d.ts +21 -0
  27. package/dist/lib/automations-builder.d.ts.map +1 -0
  28. package/dist/lib/automations-builder.js +557 -0
  29. package/dist/lib/automations-builder.js.map +1 -0
  30. package/dist/lib/automations-types.d.ts +153 -0
  31. package/dist/lib/automations-types.d.ts.map +1 -0
  32. package/dist/lib/automations-types.js +191 -0
  33. package/dist/lib/automations-types.js.map +1 -0
  34. package/dist/lib/index.d.ts +3 -0
  35. package/dist/lib/index.d.ts.map +1 -0
  36. package/dist/lib/index.js +3 -0
  37. package/dist/lib/index.js.map +1 -0
  38. package/dist/lib/legacy-task-migration.d.ts +20 -0
  39. package/dist/lib/legacy-task-migration.d.ts.map +1 -0
  40. package/dist/lib/legacy-task-migration.js +110 -0
  41. package/dist/lib/legacy-task-migration.js.map +1 -0
  42. package/dist/lib/legacy-text-trigger-migration.d.ts +18 -0
  43. package/dist/lib/legacy-text-trigger-migration.d.ts.map +1 -0
  44. package/dist/lib/legacy-text-trigger-migration.js +131 -0
  45. package/dist/lib/legacy-text-trigger-migration.js.map +1 -0
  46. package/dist/lib/workflow-clarification.d.ts +113 -0
  47. package/dist/lib/workflow-clarification.d.ts.map +1 -0
  48. package/dist/lib/workflow-clarification.js +425 -0
  49. package/dist/lib/workflow-clarification.js.map +1 -0
  50. package/dist/plugin-routes.d.ts +9 -0
  51. package/dist/plugin-routes.d.ts.map +1 -0
  52. package/dist/plugin-routes.js +147 -0
  53. package/dist/plugin-routes.js.map +1 -0
  54. package/dist/providers/activeWorkflows.d.ts +11 -0
  55. package/dist/providers/activeWorkflows.d.ts.map +1 -0
  56. package/dist/providers/activeWorkflows.js +72 -0
  57. package/dist/providers/activeWorkflows.js.map +1 -0
  58. package/dist/providers/index.d.ts +4 -0
  59. package/dist/providers/index.d.ts.map +1 -0
  60. package/dist/providers/index.js +4 -0
  61. package/dist/providers/index.js.map +1 -0
  62. package/dist/providers/pendingDraft.d.ts +9 -0
  63. package/dist/providers/pendingDraft.d.ts.map +1 -0
  64. package/dist/providers/pendingDraft.js +48 -0
  65. package/dist/providers/pendingDraft.js.map +1 -0
  66. package/dist/providers/workflowStatus.d.ts +3 -0
  67. package/dist/providers/workflowStatus.d.ts.map +1 -0
  68. package/dist/providers/workflowStatus.js +69 -0
  69. package/dist/providers/workflowStatus.js.map +1 -0
  70. package/dist/register-routes.d.ts +2 -0
  71. package/dist/register-routes.d.ts.map +1 -0
  72. package/dist/register-routes.js +6 -0
  73. package/dist/register-routes.js.map +1 -0
  74. package/dist/routes/_helpers.d.ts +11 -0
  75. package/dist/routes/_helpers.d.ts.map +1 -0
  76. package/dist/routes/_helpers.js +22 -0
  77. package/dist/routes/_helpers.js.map +1 -0
  78. package/dist/routes/automations.d.ts +19 -0
  79. package/dist/routes/automations.d.ts.map +1 -0
  80. package/dist/routes/automations.js +32 -0
  81. package/dist/routes/automations.js.map +1 -0
  82. package/dist/routes/embedded-webhooks.d.ts +3 -0
  83. package/dist/routes/embedded-webhooks.d.ts.map +1 -0
  84. package/dist/routes/embedded-webhooks.js +47 -0
  85. package/dist/routes/embedded-webhooks.js.map +1 -0
  86. package/dist/routes/executions.d.ts +3 -0
  87. package/dist/routes/executions.d.ts.map +1 -0
  88. package/dist/routes/executions.js +58 -0
  89. package/dist/routes/executions.js.map +1 -0
  90. package/dist/routes/index.d.ts +4 -0
  91. package/dist/routes/index.d.ts.map +1 -0
  92. package/dist/routes/index.js +14 -0
  93. package/dist/routes/index.js.map +1 -0
  94. package/dist/routes/nodes.d.ts +3 -0
  95. package/dist/routes/nodes.d.ts.map +1 -0
  96. package/dist/routes/nodes.js +168 -0
  97. package/dist/routes/nodes.js.map +1 -0
  98. package/dist/routes/validation.d.ts +3 -0
  99. package/dist/routes/validation.d.ts.map +1 -0
  100. package/dist/routes/validation.js +41 -0
  101. package/dist/routes/validation.js.map +1 -0
  102. package/dist/routes/workflow-routes.d.ts +27 -0
  103. package/dist/routes/workflow-routes.d.ts.map +1 -0
  104. package/dist/routes/workflow-routes.js +326 -0
  105. package/dist/routes/workflow-routes.js.map +1 -0
  106. package/dist/routes/workflows.d.ts +3 -0
  107. package/dist/routes/workflows.d.ts.map +1 -0
  108. package/dist/routes/workflows.js +252 -0
  109. package/dist/routes/workflows.js.map +1 -0
  110. package/dist/schemas/draftIntent.d.ts +22 -0
  111. package/dist/schemas/draftIntent.d.ts.map +1 -0
  112. package/dist/schemas/draftIntent.js +22 -0
  113. package/dist/schemas/draftIntent.js.map +1 -0
  114. package/dist/schemas/feasibility.d.ts +13 -0
  115. package/dist/schemas/feasibility.d.ts.map +1 -0
  116. package/dist/schemas/feasibility.js +9 -0
  117. package/dist/schemas/feasibility.js.map +1 -0
  118. package/dist/schemas/index.d.ts +5 -0
  119. package/dist/schemas/index.d.ts.map +1 -0
  120. package/dist/schemas/index.js +5 -0
  121. package/dist/schemas/index.js.map +1 -0
  122. package/dist/schemas/keywordExtraction.d.ts +14 -0
  123. package/dist/schemas/keywordExtraction.d.ts.map +1 -0
  124. package/dist/schemas/keywordExtraction.js +12 -0
  125. package/dist/schemas/keywordExtraction.js.map +1 -0
  126. package/dist/schemas/workflowMatching.d.ts +36 -0
  127. package/dist/schemas/workflowMatching.d.ts.map +1 -0
  128. package/dist/schemas/workflowMatching.js +30 -0
  129. package/dist/schemas/workflowMatching.js.map +1 -0
  130. package/dist/services/embedded-workflow-service.d.ts +106 -0
  131. package/dist/services/embedded-workflow-service.d.ts.map +1 -0
  132. package/dist/services/embedded-workflow-service.js +1900 -0
  133. package/dist/services/embedded-workflow-service.js.map +1 -0
  134. package/dist/services/index.d.ts +5 -0
  135. package/dist/services/index.d.ts.map +1 -0
  136. package/dist/services/index.js +5 -0
  137. package/dist/services/index.js.map +1 -0
  138. package/dist/services/workflow-credential-store.d.ts +27 -0
  139. package/dist/services/workflow-credential-store.d.ts.map +1 -0
  140. package/dist/services/workflow-credential-store.js +92 -0
  141. package/dist/services/workflow-credential-store.js.map +1 -0
  142. package/dist/services/workflow-dispatch.d.ts +41 -0
  143. package/dist/services/workflow-dispatch.d.ts.map +1 -0
  144. package/dist/services/workflow-dispatch.js +86 -0
  145. package/dist/services/workflow-dispatch.js.map +1 -0
  146. package/dist/services/workflow-service.d.ts +63 -0
  147. package/dist/services/workflow-service.d.ts.map +1 -0
  148. package/dist/services/workflow-service.js +492 -0
  149. package/dist/services/workflow-service.js.map +1 -0
  150. package/dist/trigger-routes.d.ts +153 -0
  151. package/dist/trigger-routes.d.ts.map +1 -0
  152. package/dist/trigger-routes.js +424 -0
  153. package/dist/trigger-routes.js.map +1 -0
  154. package/dist/types/index.d.ts +457 -0
  155. package/dist/types/index.d.ts.map +1 -0
  156. package/dist/types/index.js +59 -0
  157. package/dist/types/index.js.map +1 -0
  158. package/dist/utils/catalog.d.ts +16 -0
  159. package/dist/utils/catalog.d.ts.map +1 -0
  160. package/dist/utils/catalog.js +211 -0
  161. package/dist/utils/catalog.js.map +1 -0
  162. package/dist/utils/clarification.d.ts +17 -0
  163. package/dist/utils/clarification.d.ts.map +1 -0
  164. package/dist/utils/clarification.js +46 -0
  165. package/dist/utils/clarification.js.map +1 -0
  166. package/dist/utils/context.d.ts +4 -0
  167. package/dist/utils/context.d.ts.map +1 -0
  168. package/dist/utils/context.js +18 -0
  169. package/dist/utils/context.js.map +1 -0
  170. package/dist/utils/credentialResolver.d.ts +22 -0
  171. package/dist/utils/credentialResolver.d.ts.map +1 -0
  172. package/dist/utils/credentialResolver.js +146 -0
  173. package/dist/utils/credentialResolver.js.map +1 -0
  174. package/dist/utils/generation.d.ts +36 -0
  175. package/dist/utils/generation.d.ts.map +1 -0
  176. package/dist/utils/generation.js +701 -0
  177. package/dist/utils/generation.js.map +1 -0
  178. package/dist/utils/host-capabilities.d.ts +27 -0
  179. package/dist/utils/host-capabilities.d.ts.map +1 -0
  180. package/dist/utils/host-capabilities.js +59 -0
  181. package/dist/utils/host-capabilities.js.map +1 -0
  182. package/dist/utils/inferSyntheticOutputSchema.d.ts +20 -0
  183. package/dist/utils/inferSyntheticOutputSchema.d.ts.map +1 -0
  184. package/dist/utils/inferSyntheticOutputSchema.js +151 -0
  185. package/dist/utils/inferSyntheticOutputSchema.js.map +1 -0
  186. package/dist/utils/outputSchema.d.ts +26 -0
  187. package/dist/utils/outputSchema.d.ts.map +1 -0
  188. package/dist/utils/outputSchema.js +297 -0
  189. package/dist/utils/outputSchema.js.map +1 -0
  190. package/dist/utils/validateAndRepair.d.ts +41 -0
  191. package/dist/utils/validateAndRepair.d.ts.map +1 -0
  192. package/dist/utils/validateAndRepair.js +483 -0
  193. package/dist/utils/validateAndRepair.js.map +1 -0
  194. package/dist/utils/workflow-prompts/actionResponse.d.ts +2 -0
  195. package/dist/utils/workflow-prompts/actionResponse.d.ts.map +1 -0
  196. package/dist/utils/workflow-prompts/actionResponse.js +17 -0
  197. package/dist/utils/workflow-prompts/actionResponse.js.map +1 -0
  198. package/dist/utils/workflow-prompts/draftIntent.d.ts +2 -0
  199. package/dist/utils/workflow-prompts/draftIntent.d.ts.map +1 -0
  200. package/dist/utils/workflow-prompts/draftIntent.js +23 -0
  201. package/dist/utils/workflow-prompts/draftIntent.js.map +1 -0
  202. package/dist/utils/workflow-prompts/feasibilityCheck.d.ts +2 -0
  203. package/dist/utils/workflow-prompts/feasibilityCheck.d.ts.map +1 -0
  204. package/dist/utils/workflow-prompts/feasibilityCheck.js +21 -0
  205. package/dist/utils/workflow-prompts/feasibilityCheck.js.map +1 -0
  206. package/dist/utils/workflow-prompts/fieldCorrection.d.ts +3 -0
  207. package/dist/utils/workflow-prompts/fieldCorrection.d.ts.map +1 -0
  208. package/dist/utils/workflow-prompts/fieldCorrection.js +20 -0
  209. package/dist/utils/workflow-prompts/fieldCorrection.js.map +1 -0
  210. package/dist/utils/workflow-prompts/index.d.ts +8 -0
  211. package/dist/utils/workflow-prompts/index.d.ts.map +1 -0
  212. package/dist/utils/workflow-prompts/index.js +8 -0
  213. package/dist/utils/workflow-prompts/index.js.map +1 -0
  214. package/dist/utils/workflow-prompts/keywordExtraction.d.ts +2 -0
  215. package/dist/utils/workflow-prompts/keywordExtraction.d.ts.map +1 -0
  216. package/dist/utils/workflow-prompts/keywordExtraction.js +21 -0
  217. package/dist/utils/workflow-prompts/keywordExtraction.js.map +1 -0
  218. package/dist/utils/workflow-prompts/parameterCorrection.d.ts +3 -0
  219. package/dist/utils/workflow-prompts/parameterCorrection.d.ts.map +1 -0
  220. package/dist/utils/workflow-prompts/parameterCorrection.js +29 -0
  221. package/dist/utils/workflow-prompts/parameterCorrection.js.map +1 -0
  222. package/dist/utils/workflow-prompts/workflowGeneration.d.ts +2 -0
  223. package/dist/utils/workflow-prompts/workflowGeneration.d.ts.map +1 -0
  224. package/dist/utils/workflow-prompts/workflowGeneration.js +529 -0
  225. package/dist/utils/workflow-prompts/workflowGeneration.js.map +1 -0
  226. package/dist/utils/workflow-prompts/workflowMatching.d.ts +2 -0
  227. package/dist/utils/workflow-prompts/workflowMatching.d.ts.map +1 -0
  228. package/dist/utils/workflow-prompts/workflowMatching.js +23 -0
  229. package/dist/utils/workflow-prompts/workflowMatching.js.map +1 -0
  230. package/dist/utils/workflow.d.ts +62 -0
  231. package/dist/utils/workflow.d.ts.map +1 -0
  232. package/dist/utils/workflow.js +712 -0
  233. package/dist/utils/workflow.js.map +1 -0
  234. package/package.json +87 -0
  235. package/src/actions/index.ts +1 -0
  236. package/src/actions/workflow.ts +494 -0
  237. package/src/data/defaultNodes.json +9887 -0
  238. package/src/data/schemaIndex.json +1 -0
  239. package/src/data/triggerSchemaIndex.json +1 -0
  240. package/src/db/index.ts +8 -0
  241. package/src/db/schema.ts +94 -0
  242. package/src/index.ts +179 -0
  243. package/src/lib/automations-builder.ts +679 -0
  244. package/src/lib/automations-types.ts +391 -0
  245. package/src/lib/index.ts +8 -0
  246. package/src/lib/legacy-task-migration.ts +143 -0
  247. package/src/lib/legacy-text-trigger-migration.ts +178 -0
  248. package/src/lib/workflow-clarification.ts +497 -0
  249. package/src/plugin-routes.ts +164 -0
  250. package/src/providers/activeWorkflows.ts +81 -0
  251. package/src/providers/index.ts +3 -0
  252. package/src/providers/pendingDraft.ts +55 -0
  253. package/src/providers/workflowStatus.ts +88 -0
  254. package/src/register-routes.ts +6 -0
  255. package/src/routes/_helpers.ts +27 -0
  256. package/src/routes/automations.ts +46 -0
  257. package/src/routes/embedded-webhooks.ts +64 -0
  258. package/src/routes/executions.ts +75 -0
  259. package/src/routes/index.ts +16 -0
  260. package/src/routes/nodes.ts +211 -0
  261. package/src/routes/validation.ts +51 -0
  262. package/src/routes/workflow-routes.ts +469 -0
  263. package/src/routes/workflows.ts +310 -0
  264. package/src/schemas/draftIntent.ts +21 -0
  265. package/src/schemas/feasibility.ts +8 -0
  266. package/src/schemas/index.ts +4 -0
  267. package/src/schemas/keywordExtraction.ts +11 -0
  268. package/src/schemas/workflowMatching.ts +29 -0
  269. package/src/services/embedded-workflow-service.ts +2224 -0
  270. package/src/services/index.ts +17 -0
  271. package/src/services/workflow-credential-store.ts +132 -0
  272. package/src/services/workflow-dispatch.ts +121 -0
  273. package/src/services/workflow-service.ts +839 -0
  274. package/src/trigger-routes.ts +714 -0
  275. package/src/types/index.ts +562 -0
  276. package/src/utils/catalog.ts +260 -0
  277. package/src/utils/clarification.ts +52 -0
  278. package/src/utils/context.ts +22 -0
  279. package/src/utils/credentialResolver.ts +234 -0
  280. package/src/utils/generation.ts +987 -0
  281. package/src/utils/host-capabilities.ts +81 -0
  282. package/src/utils/inferSyntheticOutputSchema.ts +163 -0
  283. package/src/utils/outputSchema.ts +372 -0
  284. package/src/utils/validateAndRepair.ts +610 -0
  285. package/src/utils/workflow-prompts/actionResponse.ts +16 -0
  286. package/src/utils/workflow-prompts/draftIntent.ts +22 -0
  287. package/src/utils/workflow-prompts/feasibilityCheck.ts +20 -0
  288. package/src/utils/workflow-prompts/fieldCorrection.ts +20 -0
  289. package/src/utils/workflow-prompts/index.ts +10 -0
  290. package/src/utils/workflow-prompts/keywordExtraction.ts +20 -0
  291. package/src/utils/workflow-prompts/parameterCorrection.ts +29 -0
  292. package/src/utils/workflow-prompts/workflowGeneration.ts +528 -0
  293. package/src/utils/workflow-prompts/workflowMatching.ts +22 -0
  294. package/src/utils/workflow.ts +895 -0
@@ -0,0 +1,712 @@
1
+ import { logger } from '@elizaos/core';
2
+ import { getNodeDefinition, simplifyNodeForLLM } from './catalog';
3
+ import { fieldExistsInSchema, getAllFieldPathsTyped, loadOutputSchema, loadTriggerOutputSchema, parseExpressions, } from './outputSchema';
4
+ function isTriggerNode(type) {
5
+ const t = type.toLowerCase();
6
+ return t.includes('trigger') || t.includes('webhook');
7
+ }
8
+ export function validateWorkflow(workflow) {
9
+ const errors = [];
10
+ const warnings = [];
11
+ // 1. Check nodes array exists and is non-empty
12
+ if (!workflow.nodes || !Array.isArray(workflow.nodes)) {
13
+ errors.push('Missing or invalid nodes array');
14
+ return { valid: false, errors, warnings };
15
+ }
16
+ if (workflow.nodes.length === 0) {
17
+ errors.push('Workflow must have at least one node');
18
+ return { valid: false, errors, warnings };
19
+ }
20
+ // 2. Check connections structure
21
+ if (!workflow.connections || typeof workflow.connections !== 'object') {
22
+ errors.push('Missing or invalid connections object');
23
+ return { valid: false, errors, warnings };
24
+ }
25
+ // 3. Validate each node
26
+ const nodeNames = new Set();
27
+ const nodeMap = new Map();
28
+ for (const node of workflow.nodes) {
29
+ // Check required fields
30
+ if (!node.name || typeof node.name !== 'string') {
31
+ errors.push('Node missing name');
32
+ continue;
33
+ }
34
+ if (!node.type || typeof node.type !== 'string') {
35
+ errors.push(`Node "${node.name}" missing type`);
36
+ continue;
37
+ }
38
+ // Check for duplicate names
39
+ if (nodeNames.has(node.name)) {
40
+ errors.push(`Duplicate node name: "${node.name}"`);
41
+ }
42
+ nodeNames.add(node.name);
43
+ nodeMap.set(node.name, node);
44
+ // Check position (positionNodes() will fix this after validation)
45
+ if (!node.position || !Array.isArray(node.position) || node.position.length !== 2) {
46
+ warnings.push(`Node "${node.name}" has invalid position, will be auto-positioned`);
47
+ }
48
+ // Check parameters
49
+ if (!node.parameters || typeof node.parameters !== 'object') {
50
+ warnings.push(`Node "${node.name}" missing parameters object`);
51
+ }
52
+ }
53
+ // 4. Validate connections reference existing nodes
54
+ for (const [sourceName, outputs] of Object.entries(workflow.connections)) {
55
+ if (!nodeNames.has(sourceName)) {
56
+ errors.push(`Connection references non-existent source node: "${sourceName}"`);
57
+ continue;
58
+ }
59
+ for (const [_outputType, connections] of Object.entries(outputs)) {
60
+ if (!Array.isArray(connections)) {
61
+ errors.push(`Invalid connection structure for node "${sourceName}"`);
62
+ continue;
63
+ }
64
+ for (const connectionGroup of connections) {
65
+ if (!Array.isArray(connectionGroup)) {
66
+ continue;
67
+ }
68
+ for (const connection of connectionGroup) {
69
+ if (!connection.node || typeof connection.node !== 'string') {
70
+ errors.push(`Invalid connection from "${sourceName}"`);
71
+ continue;
72
+ }
73
+ if (!nodeNames.has(connection.node)) {
74
+ errors.push(`Connection references non-existent target node: "${connection.node}" (from "${sourceName}")`);
75
+ }
76
+ }
77
+ }
78
+ }
79
+ }
80
+ // 5. Check for at least one trigger node
81
+ const hasTrigger = workflow.nodes.some((node) => isTriggerNode(node.type) || node.name.toLowerCase().includes('start'));
82
+ if (!hasTrigger) {
83
+ warnings.push('Workflow has no trigger node - it can only be executed manually');
84
+ }
85
+ // 6. Check for orphan nodes (nodes with no incoming connections, except triggers)
86
+ const nodesWithIncoming = new Set();
87
+ for (const outputs of Object.values(workflow.connections)) {
88
+ for (const connectionGroup of Object.values(outputs)) {
89
+ for (const connections of connectionGroup) {
90
+ for (const conn of connections) {
91
+ nodesWithIncoming.add(conn.node);
92
+ }
93
+ }
94
+ }
95
+ }
96
+ for (const node of workflow.nodes) {
97
+ if (!isTriggerNode(node.type) &&
98
+ !node.name.toLowerCase().includes('start') &&
99
+ !nodesWithIncoming.has(node.name)) {
100
+ warnings.push(`Node "${node.name}" has no incoming connections - it will never execute`);
101
+ }
102
+ }
103
+ if (errors.length > 0) {
104
+ return { valid: false, errors, warnings };
105
+ }
106
+ return {
107
+ valid: true,
108
+ errors: [],
109
+ warnings,
110
+ };
111
+ }
112
+ export function validateNodeParameters(workflow) {
113
+ const warnings = [];
114
+ for (const node of workflow.nodes) {
115
+ const nodeDef = getNodeDefinition(node.type);
116
+ if (!nodeDef) {
117
+ continue;
118
+ } // Unknown node type — skip
119
+ const effectiveParams = buildEffectiveParams(nodeDef, node);
120
+ for (const prop of nodeDef.properties) {
121
+ if (!prop.required) {
122
+ continue;
123
+ }
124
+ if (!isPropertyVisible(prop, effectiveParams)) {
125
+ continue;
126
+ }
127
+ const value = node.parameters?.[prop.name];
128
+ if (value === undefined || value === null || value === '') {
129
+ const label = prop.displayName || prop.name;
130
+ // Include the catalog property description in parentheses when
131
+ // present. The displayName alone is often opaque ("Name", "Type",
132
+ // "Mode") and the user has no way to know what the parameter
133
+ // actually governs. The description is the same hover-text the
134
+ // upstream node UI shows, so it carries real semantic information.
135
+ // Catalog descriptions sometimes contain raw HTML (e.g.
136
+ // <a href="...">expression</a>) sourced from the upstream
137
+ // node-types definitions; strip tags before interpolation so the
138
+ // clarification surfaces in plain-text contexts cleanly.
139
+ const description = prop.description?.replace(/<[^>]*>/g, '').trim();
140
+ const detail = description ? ` (${description})` : '';
141
+ warnings.push(`Node "${node.name}": missing required parameter "${label}"${detail}`);
142
+ }
143
+ }
144
+ }
145
+ return warnings;
146
+ }
147
+ /**
148
+ * Build effective parameters for visibility checks by applying property defaults in two passes.
149
+ *
150
+ * Pass 1: always-visible props (no displayOptions) — e.g. `resource` default "message".
151
+ * Pass 2: props whose displayOptions are satisfied by pass-1 defaults — e.g. `operation`
152
+ * default "send" becomes visible once `resource` is known.
153
+ *
154
+ * Two passes resolve the depth-2 chains present in workflows node definitions
155
+ * (root prop → one level of conditional). The `@version` key is injected as the
156
+ * node's typeVersion so displayOptions conditions that reference it work correctly.
157
+ */
158
+ function buildEffectiveParams(nodeDef, node) {
159
+ const effective = { '@version': node.typeVersion };
160
+ // Pass 1: always-visible properties (no displayOptions)
161
+ for (const prop of nodeDef.properties) {
162
+ if (!prop.displayOptions && !(prop.name in node.parameters) && prop.default !== undefined) {
163
+ effective[prop.name] = prop.default;
164
+ }
165
+ }
166
+ // Merge actual params so pass 2 sees LLM-provided values (e.g. resource set
167
+ // explicitly while operation is omitted — operation's displayOptions depends on resource).
168
+ Object.assign(effective, node.parameters);
169
+ // Pass 2: properties with displayOptions that are now satisfied by pass-1 defaults + actual params
170
+ for (const prop of nodeDef.properties) {
171
+ if (prop.displayOptions &&
172
+ !(prop.name in effective) &&
173
+ prop.default !== undefined &&
174
+ isPropertyVisible(prop, effective)) {
175
+ effective[prop.name] = prop.default;
176
+ }
177
+ }
178
+ return effective;
179
+ }
180
+ /**
181
+ * workflows displayOptions logic:
182
+ * - `show`: ALL conditions must match for visible
183
+ * - `hide`: ANY match hides the property
184
+ */
185
+ function isPropertyVisible(prop, parameters) {
186
+ if (!prop.displayOptions) {
187
+ return true;
188
+ }
189
+ const show = prop.displayOptions;
190
+ // If "show" is defined, ALL conditions must match
191
+ if (show.show) {
192
+ for (const [key, allowedValues] of Object.entries(show.show)) {
193
+ if (!Array.isArray(allowedValues)) {
194
+ continue;
195
+ }
196
+ const paramValue = parameters?.[key];
197
+ if (!allowedValues.includes(paramValue)) {
198
+ return false;
199
+ }
200
+ }
201
+ }
202
+ // If "hide" is defined, ANY match hides the property
203
+ if (show.hide) {
204
+ for (const [key, hiddenValues] of Object.entries(show.hide)) {
205
+ if (!Array.isArray(hiddenValues)) {
206
+ continue;
207
+ }
208
+ const paramValue = parameters?.[key];
209
+ if (hiddenValues.includes(paramValue)) {
210
+ return false;
211
+ }
212
+ }
213
+ }
214
+ return true;
215
+ }
216
+ export function validateNodeInputs(workflow) {
217
+ const warnings = [];
218
+ // Count incoming connections per node
219
+ const incomingCount = new Map();
220
+ for (const node of workflow.nodes) {
221
+ incomingCount.set(node.name, 0);
222
+ }
223
+ for (const outputs of Object.values(workflow.connections)) {
224
+ for (const connectionGroups of Object.values(outputs)) {
225
+ for (const connections of connectionGroups) {
226
+ for (const conn of connections) {
227
+ incomingCount.set(conn.node, (incomingCount.get(conn.node) || 0) + 1);
228
+ }
229
+ }
230
+ }
231
+ }
232
+ for (const node of workflow.nodes) {
233
+ const nodeDef = getNodeDefinition(node.type);
234
+ if (!nodeDef) {
235
+ continue;
236
+ }
237
+ if (isTriggerNode(node.type) || nodeDef.group.includes('trigger')) {
238
+ continue;
239
+ }
240
+ // Dynamic inputs (workflows expression string) can't be validated statically
241
+ if (!Array.isArray(nodeDef.inputs)) {
242
+ continue;
243
+ }
244
+ const expectedInputs = nodeDef.inputs.filter((i) => i === 'main').length;
245
+ const actualInputs = incomingCount.get(node.name) || 0;
246
+ if (expectedInputs > 0 && actualInputs < expectedInputs) {
247
+ warnings.push(`Node "${node.name}" expects ${expectedInputs} input(s) but has ${actualInputs}`);
248
+ }
249
+ }
250
+ return warnings;
251
+ }
252
+ export function positionNodes(workflow) {
253
+ // Clone workflow
254
+ const positioned = { ...workflow };
255
+ positioned.nodes = [...workflow.nodes];
256
+ // Check if all nodes already have valid positions
257
+ const allHavePositions = positioned.nodes.every((node) => node.position &&
258
+ Array.isArray(node.position) &&
259
+ node.position.length === 2 &&
260
+ typeof node.position[0] === 'number' &&
261
+ typeof node.position[1] === 'number');
262
+ if (allHavePositions) {
263
+ return positioned; // No changes needed
264
+ }
265
+ // Build node graph to understand flow structure
266
+ const nodeGraph = buildNodeGraph(positioned);
267
+ // Position nodes level by level (breadth-first from triggers)
268
+ const positionedNodes = positionByLevels(positioned.nodes, nodeGraph);
269
+ positioned.nodes = positionedNodes;
270
+ return positioned;
271
+ }
272
+ /** Ensure trigger nodes use simplified output when available. */
273
+ export function normalizeTriggerSimpleParam(workflow) {
274
+ for (const node of workflow.nodes) {
275
+ if (!isTriggerNode(node.type)) {
276
+ continue;
277
+ }
278
+ const def = getNodeDefinition(node.type);
279
+ const hasSimple = def?.properties?.some((p) => p.name === 'simple');
280
+ if (hasSimple) {
281
+ node.parameters = { ...node.parameters, simple: true };
282
+ }
283
+ }
284
+ }
285
+ /**
286
+ * Validates that $json expressions reference fields that exist in upstream node output schemas.
287
+ * Returns a list of invalid references that need correction.
288
+ */
289
+ export function validateOutputReferences(workflow) {
290
+ const invalidRefs = [];
291
+ const upstreamMap = buildUpstreamMap(workflow);
292
+ const nodeMap = new Map(workflow.nodes.map((n) => [n.name, n]));
293
+ const schemaCache = new Map();
294
+ function getSourceSchema(sourceName) {
295
+ if (schemaCache.has(sourceName)) {
296
+ const cached = schemaCache.get(sourceName);
297
+ return cached === undefined ? null : cached;
298
+ }
299
+ const sourceNode = nodeMap.get(sourceName);
300
+ if (!sourceNode) {
301
+ schemaCache.set(sourceName, null);
302
+ return null;
303
+ }
304
+ const resource = sourceNode.parameters?.resource || '';
305
+ const operation = sourceNode.parameters?.operation || '';
306
+ const schemaResult = isTriggerNode(sourceNode.type)
307
+ ? loadTriggerOutputSchema(sourceNode.type, sourceNode.parameters)
308
+ : loadOutputSchema(sourceNode.type, resource, operation);
309
+ if (!schemaResult) {
310
+ schemaCache.set(sourceName, null);
311
+ return null;
312
+ }
313
+ const entry = {
314
+ schema: schemaResult.schema,
315
+ fields: schemaResult.fields,
316
+ node: sourceNode,
317
+ };
318
+ schemaCache.set(sourceName, entry);
319
+ return entry;
320
+ }
321
+ for (const node of workflow.nodes) {
322
+ if (!node.parameters) {
323
+ continue;
324
+ }
325
+ const expressions = parseExpressions(node.parameters);
326
+ if (expressions.length === 0) {
327
+ continue;
328
+ }
329
+ const upstreamNames = upstreamMap.get(node.name) || [];
330
+ if (upstreamNames.length === 0) {
331
+ continue;
332
+ }
333
+ const defaultSourceName = upstreamNames[0];
334
+ for (const expr of expressions) {
335
+ const sourceName = expr.sourceNodeName || defaultSourceName;
336
+ const cached = getSourceSchema(sourceName);
337
+ if (!cached) {
338
+ continue;
339
+ }
340
+ const exists = fieldExistsInSchema(expr.path, cached.schema);
341
+ if (!exists) {
342
+ const resource = cached.node.parameters?.resource || '';
343
+ const operation = cached.node.parameters?.operation || '';
344
+ invalidRefs.push({
345
+ nodeName: node.name,
346
+ expression: expr.fullExpression,
347
+ field: expr.field,
348
+ sourceNodeName: sourceName,
349
+ sourceNodeType: cached.node.type,
350
+ resource,
351
+ operation,
352
+ availableFields: getAllFieldPathsTyped(cached.schema).map((f) => `${f.path} (${f.type})`),
353
+ });
354
+ }
355
+ }
356
+ }
357
+ return invalidRefs;
358
+ }
359
+ /**
360
+ * Correct invalid option parameter values and typeVersion against catalog definitions.
361
+ * Top-level options (resource) are fixed first so displayOptions cascading works for dependent ones (operation).
362
+ */
363
+ export function correctOptionParameters(workflow) {
364
+ let corrections = 0;
365
+ for (const node of workflow.nodes) {
366
+ const nodeDef = getNodeDefinition(node.type);
367
+ if (!nodeDef) {
368
+ continue;
369
+ }
370
+ if (node.type !== nodeDef.name) {
371
+ logger.warn({ src: 'plugin:workflow:correctOptions' }, `Node "${node.name}": type "${node.type}" → "${nodeDef.name}"`);
372
+ node.type = nodeDef.name;
373
+ corrections++;
374
+ }
375
+ const validVersions = Array.isArray(nodeDef.version) ? nodeDef.version : [nodeDef.version];
376
+ if (node.typeVersion && !validVersions.includes(node.typeVersion)) {
377
+ const maxVersion = Math.max(...validVersions);
378
+ logger.warn({ src: 'plugin:workflow:correctOptions' }, `Node "${node.name}": typeVersion ${node.typeVersion} → ${maxVersion}`);
379
+ node.typeVersion = maxVersion;
380
+ corrections++;
381
+ }
382
+ const topLevel = [];
383
+ const dependent = [];
384
+ for (const prop of nodeDef.properties) {
385
+ if (prop.type !== 'options' || !prop.options?.length) {
386
+ continue;
387
+ }
388
+ if (prop.displayOptions) {
389
+ dependent.push(prop);
390
+ }
391
+ else {
392
+ topLevel.push(prop);
393
+ }
394
+ }
395
+ for (const prop of topLevel) {
396
+ corrections += fixOptionValue(node, prop);
397
+ }
398
+ const effectiveParamsForDeps = buildEffectiveParams(nodeDef, node);
399
+ for (const prop of dependent) {
400
+ if (!isPropertyVisible(prop, effectiveParamsForDeps)) {
401
+ continue;
402
+ }
403
+ corrections += fixOptionValue(node, prop);
404
+ }
405
+ }
406
+ return corrections;
407
+ }
408
+ function fixOptionValue(node, prop) {
409
+ const currentValue = node.parameters[prop.name];
410
+ if (currentValue === undefined) {
411
+ return 0;
412
+ }
413
+ const allowedValues = prop.options?.map((o) => o.value) ?? [];
414
+ if (allowedValues.includes(currentValue)) {
415
+ return 0;
416
+ }
417
+ const corrected = prop.default !== undefined && allowedValues.includes(prop.default)
418
+ ? prop.default
419
+ : allowedValues[0];
420
+ logger.warn({ src: 'plugin:workflow:correctOptions' }, `Node "${node.name}": ${prop.name} "${currentValue}" → "${corrected}"`);
421
+ node.parameters[prop.name] = corrected;
422
+ return 1;
423
+ }
424
+ function buildUpstreamMap(workflow) {
425
+ const upstream = new Map();
426
+ for (const node of workflow.nodes) {
427
+ upstream.set(node.name, []);
428
+ }
429
+ for (const [sourceName, outputs] of Object.entries(workflow.connections)) {
430
+ for (const connectionGroups of Object.values(outputs)) {
431
+ for (const connections of connectionGroups) {
432
+ for (const conn of connections) {
433
+ const existing = upstream.get(conn.node) || [];
434
+ if (!existing.includes(sourceName)) {
435
+ existing.push(sourceName);
436
+ upstream.set(conn.node, existing);
437
+ }
438
+ }
439
+ }
440
+ }
441
+ }
442
+ return upstream;
443
+ }
444
+ function buildNodeGraph(workflow) {
445
+ const graph = new Map();
446
+ // Initialize all nodes
447
+ for (const node of workflow.nodes) {
448
+ graph.set(node.name, []);
449
+ }
450
+ // Build edges from connections
451
+ for (const [sourceName, outputs] of Object.entries(workflow.connections)) {
452
+ const targets = [];
453
+ for (const connectionGroups of Object.values(outputs)) {
454
+ for (const connections of connectionGroups) {
455
+ for (const conn of connections) {
456
+ if (conn.node) {
457
+ targets.push(conn.node);
458
+ }
459
+ }
460
+ }
461
+ }
462
+ graph.set(sourceName, targets);
463
+ }
464
+ return graph;
465
+ }
466
+ function positionByLevels(nodes, graph) {
467
+ // Find trigger/start nodes (nodes with no incoming connections)
468
+ const incomingCount = new Map();
469
+ for (const node of nodes) {
470
+ incomingCount.set(node.name, 0);
471
+ }
472
+ for (const targets of graph.values()) {
473
+ for (const target of targets) {
474
+ incomingCount.set(target, (incomingCount.get(target) || 0) + 1);
475
+ }
476
+ }
477
+ const triggerNodes = nodes.filter((node) => incomingCount.get(node.name) === 0);
478
+ // Organize into levels
479
+ const levels = [];
480
+ const visited = new Set();
481
+ const queue = [];
482
+ // Start with triggers at level 0
483
+ for (const trigger of triggerNodes) {
484
+ queue.push({ name: trigger.name, level: 0 });
485
+ }
486
+ while (queue.length > 0) {
487
+ const next = queue.shift();
488
+ if (!next) {
489
+ continue;
490
+ }
491
+ const { name, level } = next;
492
+ if (visited.has(name)) {
493
+ continue;
494
+ }
495
+ visited.add(name);
496
+ // Add to level
497
+ if (!levels[level]) {
498
+ levels[level] = [];
499
+ }
500
+ levels[level].push(name);
501
+ // Add children to next level
502
+ const children = graph.get(name) || [];
503
+ for (const child of children) {
504
+ if (!visited.has(child)) {
505
+ queue.push({ name: child, level: level + 1 });
506
+ }
507
+ }
508
+ }
509
+ // Position nodes based on levels
510
+ const positioned = [...nodes];
511
+ const nodeMap = new Map(nodes.map((node) => [node.name, node]));
512
+ const startX = 250;
513
+ const startY = 300;
514
+ const xSpacing = 250;
515
+ const ySpacing = 100;
516
+ for (let levelIndex = 0; levelIndex < levels.length; levelIndex++) {
517
+ const levelNodes = levels[levelIndex];
518
+ const x = startX + levelIndex * xSpacing;
519
+ // Center nodes vertically if multiple in same level
520
+ const totalHeight = levelNodes.length * ySpacing;
521
+ const startYForLevel = startY - totalHeight / 2;
522
+ for (let i = 0; i < levelNodes.length; i++) {
523
+ const nodeName = levelNodes[i];
524
+ const node = nodeMap.get(nodeName);
525
+ if (node) {
526
+ const y = startYForLevel + i * ySpacing;
527
+ const nodeIndex = positioned.findIndex((n) => n.name === nodeName);
528
+ if (nodeIndex !== -1) {
529
+ positioned[nodeIndex] = {
530
+ ...positioned[nodeIndex],
531
+ position: [x, y],
532
+ };
533
+ }
534
+ }
535
+ }
536
+ }
537
+ return positioned;
538
+ }
539
+ export function detectUnknownParameters(workflow) {
540
+ const detections = [];
541
+ for (const node of workflow.nodes) {
542
+ const nodeDef = getNodeDefinition(node.type);
543
+ if (!nodeDef || !node.parameters) {
544
+ continue;
545
+ }
546
+ // Compute visible property names using effective parameters (actual + defaults).
547
+ // Defaults are applied for always-visible props first, then for newly-visible props,
548
+ // so that chained displayOptions (resource → operation → field) resolve correctly.
549
+ const effectiveParams = buildEffectiveParams(nodeDef, node);
550
+ const visibleNames = new Set();
551
+ for (const prop of nodeDef.properties) {
552
+ if (isPropertyVisible(prop, effectiveParams)) {
553
+ visibleNames.add(prop.name);
554
+ }
555
+ }
556
+ const unknownKeys = [];
557
+ for (const key of Object.keys(node.parameters)) {
558
+ if (!visibleNames.has(key)) {
559
+ unknownKeys.push(key);
560
+ }
561
+ }
562
+ if (unknownKeys.length === 0) {
563
+ continue;
564
+ }
565
+ // Provide simplified visible properties for the LLM correction prompt
566
+ const simplified = simplifyNodeForLLM(nodeDef);
567
+ const visibleSimplified = simplified.properties.filter((p) => visibleNames.has(p.name));
568
+ detections.push({
569
+ nodeName: node.name,
570
+ nodeType: node.type,
571
+ currentParams: node.parameters,
572
+ unknownKeys,
573
+ propertyDefs: visibleSimplified,
574
+ });
575
+ }
576
+ return detections;
577
+ }
578
+ /**
579
+ * Prefix all string parameter values containing {{ }} with = so workflows evaluates them as expressions.
580
+ * Without =, workflows treats {{ }} as literal text.
581
+ * Returns the number of values prefixed.
582
+ */
583
+ /**
584
+ * Deterministically attach a `credentials` block to every node that requires
585
+ * one. Runs after LLM generation as a safety net: even with a hardened
586
+ * `MANDATORY INVARIANT` rule in the system prompt, the LLM occasionally omits
587
+ * the block — and resolveCredentials only fires when a block is present, so
588
+ * an omission means the credential never gets minted server-side and the user
589
+ * has to wire it in workflows's UI.
590
+ *
591
+ * Selection rule:
592
+ * 1. Skip nodes that already have at least one credentials entry.
593
+ * 2. Look up the node's catalog definition. If `def.credentials` is empty,
594
+ * the node doesn't need credentials — skip.
595
+ * 3. Pick the first credential type from `def.credentials` that:
596
+ * - is listed in `runtimeContext.supportedCredentials.nodeTypes` for
597
+ * this node's type, AND
598
+ * - matches the node's `parameters.authentication` (when the credential's
599
+ * displayOptions.show.authentication is set; otherwise unconditional).
600
+ * 4. Inject `node.credentials = { [credType]: { id: "{{CREDENTIAL_ID}}", name } }`.
601
+ * The plugin's `resolveCredentials` later replaces `{{CREDENTIAL_ID}}` with
602
+ * the real workflows credential id.
603
+ *
604
+ * Returns the number of nodes that received an injected block (for logging).
605
+ */
606
+ export function injectMissingCredentialBlocks(workflow, relevantNodes, runtimeContext) {
607
+ if (!runtimeContext?.supportedCredentials?.length) {
608
+ return 0;
609
+ }
610
+ // Build supportedCredType-by-nodeType lookup. Each supportedCredential entry
611
+ // applies to one or more node types; flip that map so we can ask
612
+ // "for this node type, which cred types does the host support?".
613
+ const supportedByNodeType = new Map();
614
+ for (const sc of runtimeContext.supportedCredentials) {
615
+ for (const nodeType of sc.nodeTypes) {
616
+ if (!supportedByNodeType.has(nodeType)) {
617
+ supportedByNodeType.set(nodeType, new Map());
618
+ }
619
+ supportedByNodeType.get(nodeType)?.set(sc.credType, sc.friendlyName);
620
+ }
621
+ }
622
+ if (supportedByNodeType.size === 0) {
623
+ return 0;
624
+ }
625
+ const defByType = new Map(relevantNodes.map((n) => [n.name, n]));
626
+ let injected = 0;
627
+ for (const node of workflow.nodes) {
628
+ if (node.credentials && Object.keys(node.credentials).length > 0) {
629
+ continue;
630
+ }
631
+ const def = defByType.get(node.type);
632
+ if (!def?.credentials?.length) {
633
+ continue;
634
+ }
635
+ const supportedForType = supportedByNodeType.get(node.type);
636
+ if (!supportedForType?.size) {
637
+ continue;
638
+ }
639
+ // Resolve which credential type matches this node's authentication choice.
640
+ // workflows nodes typically gate credentials by `displayOptions.show.authentication`
641
+ // (e.g. discord's discordBotApi shows when authentication=botToken).
642
+ const auth = typeof node.parameters?.authentication === 'string'
643
+ ? node.parameters.authentication
644
+ : null;
645
+ const candidate = def.credentials.find((c) => {
646
+ if (!supportedForType.has(c.name)) {
647
+ return false;
648
+ }
649
+ const showOpts = c.displayOptions
650
+ ?.show;
651
+ if (showOpts?.authentication && showOpts.authentication.length > 0) {
652
+ return auth ? showOpts.authentication.includes(auth) : false;
653
+ }
654
+ // Unconditional credential or no show-rule: take it.
655
+ return true;
656
+ });
657
+ if (!candidate) {
658
+ continue;
659
+ }
660
+ const friendlyName = supportedForType.get(candidate.name) ?? candidate.name;
661
+ node.credentials = {
662
+ [candidate.name]: {
663
+ id: '{{CREDENTIAL_ID}}',
664
+ name: friendlyName,
665
+ },
666
+ };
667
+ logger.debug({
668
+ src: 'plugin:workflow:utils:workflow',
669
+ node: node.name,
670
+ nodeType: node.type,
671
+ credType: candidate.name,
672
+ }, 'Injected missing credentials block on node (LLM omitted it)');
673
+ injected++;
674
+ }
675
+ return injected;
676
+ }
677
+ export function ensureExpressionPrefix(workflow) {
678
+ let count = 0;
679
+ for (const node of workflow.nodes) {
680
+ if (!node.parameters) {
681
+ continue;
682
+ }
683
+ count += prefixExpressions(node.parameters);
684
+ }
685
+ return count;
686
+ }
687
+ function prefixExpressions(obj) {
688
+ let count = 0;
689
+ for (const key of Object.keys(obj)) {
690
+ const value = obj[key];
691
+ if (typeof value === 'string' && value.includes('{{') && !value.startsWith('=')) {
692
+ obj[key] = `=${value}`;
693
+ count++;
694
+ }
695
+ else if (Array.isArray(value)) {
696
+ for (let i = 0; i < value.length; i++) {
697
+ if (typeof value[i] === 'string' && value[i].includes('{{') && !value[i].startsWith('=')) {
698
+ value[i] = `=${value[i]}`;
699
+ count++;
700
+ }
701
+ else if (typeof value[i] === 'object' && value[i] !== null) {
702
+ count += prefixExpressions(value[i]);
703
+ }
704
+ }
705
+ }
706
+ else if (typeof value === 'object' && value !== null) {
707
+ count += prefixExpressions(value);
708
+ }
709
+ }
710
+ return count;
711
+ }
712
+ //# sourceMappingURL=workflow.js.map