@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,260 @@
1
+ import defaultNodesData from '../data/defaultNodes.json';
2
+ import type {
3
+ IntegrationFilterResult,
4
+ NodeDefinition,
5
+ NodeProperty,
6
+ NodeSearchResult,
7
+ } from '../types/index';
8
+
9
+ /**
10
+ * workflows node catalog with keyword-based search
11
+ * @note Uses embedded catalog (457 nodes as of April 2025)
12
+ * @todo Add dynamic refresh via GET /node-types in v2
13
+ */
14
+
15
+ const NODE_CATALOG = defaultNodesData as NodeDefinition[];
16
+
17
+ /** Get all nodes in the catalog. Used by route handlers for unfiltered listing. */
18
+ export function getAllNodes(): NodeDefinition[] {
19
+ return NODE_CATALOG;
20
+ }
21
+
22
+ /**
23
+ * Look up a node definition by its type name.
24
+ *
25
+ * Handles full names ("workflows-nodes-base.httpRequest") and bare names ("httpRequest").
26
+ */
27
+ export function getNodeDefinition(typeName: string): NodeDefinition | undefined {
28
+ const exact = NODE_CATALOG.find((n) => n.name === typeName);
29
+ if (exact) {
30
+ return exact;
31
+ }
32
+
33
+ const bare = typeName.replace(/^workflows-nodes-base\./, '');
34
+ return NODE_CATALOG.find((n) => {
35
+ const catalogBare = n.name.replace(/^workflows-nodes-base\./, '');
36
+ return catalogBare === bare || n.name === bare;
37
+ });
38
+ }
39
+
40
+ /** Split a name into lowercase tokens on camelCase / dot / hyphen / underscore / @ / slash boundaries */
41
+ function tokenize(name: string): string[] {
42
+ return name
43
+ .replace(/([a-z])([A-Z])/g, '$1 $2') // camelCase → words
44
+ .split(/[\s.\-_@/]+/)
45
+ .map((t) => t.toLowerCase())
46
+ .filter(Boolean);
47
+ }
48
+
49
+ /**
50
+ * Scoring: exact name 10, word-boundary 7, substring 3, category 3, description 2, word 1
51
+ */
52
+ export function searchNodes(keywords: string[], limit = 15): NodeSearchResult[] {
53
+ if (keywords.length === 0) {
54
+ return [];
55
+ }
56
+
57
+ const normalizedKeywords = keywords.map((kw) => kw.toLowerCase().trim());
58
+
59
+ const scoredNodes: NodeSearchResult[] = NODE_CATALOG.filter(
60
+ (node) => node.name && node.displayName
61
+ ).map((node) => {
62
+ let score = 0;
63
+ const matchReasons: string[] = [];
64
+
65
+ const nodeName = node.name.toLowerCase();
66
+ const nodeDisplayName = node.displayName.toLowerCase();
67
+ const nodeDescription = node.description?.toLowerCase() || '';
68
+ const nameTokens = tokenize(node.name);
69
+ const displayTokens = tokenize(node.displayName);
70
+
71
+ for (const keyword of normalizedKeywords) {
72
+ if (nodeName === keyword || nodeDisplayName === keyword) {
73
+ score += 10;
74
+ matchReasons.push(`exact match: "${keyword}"`);
75
+ continue;
76
+ }
77
+
78
+ // Word-boundary match: keyword equals a token in the name
79
+ const isWordMatch =
80
+ nameTokens.some((t) => t === keyword) || displayTokens.some((t) => t === keyword);
81
+
82
+ if (isWordMatch) {
83
+ score += 7;
84
+ matchReasons.push(`word match: "${keyword}"`);
85
+ } else if (nodeName.includes(keyword) || nodeDisplayName.includes(keyword)) {
86
+ score += 3;
87
+ matchReasons.push(`name contains: "${keyword}"`);
88
+ }
89
+
90
+ if (nodeDescription.includes(keyword)) {
91
+ score += 2;
92
+ matchReasons.push(`description contains: "${keyword}"`);
93
+ }
94
+
95
+ const descriptionWords = nodeDescription.split(/\s+/);
96
+ if (descriptionWords.some((word) => word.includes(keyword))) {
97
+ score += 1;
98
+ }
99
+
100
+ if (node.group.some((group) => group.toLowerCase().includes(keyword))) {
101
+ score += 3;
102
+ matchReasons.push(`category: "${keyword}"`);
103
+ }
104
+ }
105
+
106
+ return {
107
+ node,
108
+ score,
109
+ matchReason: matchReasons.join(', ') || 'no strong match',
110
+ };
111
+ });
112
+
113
+ return scoredNodes
114
+ .filter((result) => result.score > 0)
115
+ .sort((a, b) => b.score - a.score)
116
+ .slice(0, limit);
117
+ }
118
+
119
+ export function filterNodesByIntegrationSupport(
120
+ nodes: NodeSearchResult[],
121
+ supportedCredTypes: Set<string>
122
+ ): IntegrationFilterResult {
123
+ const remaining: NodeSearchResult[] = [];
124
+ const removed: NodeSearchResult[] = [];
125
+
126
+ for (const result of nodes) {
127
+ const creds = result.node.credentials;
128
+
129
+ // No credentials → utility node → always keep
130
+ if (!creds || creds.length === 0) {
131
+ remaining.push(result);
132
+ continue;
133
+ }
134
+
135
+ // Service node: keep if ANY credential type is supported
136
+ const hasSupported = creds.some((c) => supportedCredTypes.has(c.name));
137
+ if (hasSupported) {
138
+ remaining.push(result);
139
+ } else {
140
+ removed.push(result);
141
+ }
142
+ }
143
+
144
+ return { remaining, removed };
145
+ }
146
+
147
+ const NOISE_TYPES = new Set(['notice', 'hidden']);
148
+ const STRIP_KEYS = new Set([
149
+ 'routing',
150
+ 'displayOptions',
151
+ 'typeOptions',
152
+ 'hint',
153
+ 'isNodeSetting',
154
+ 'noDataExpression',
155
+ 'validateType',
156
+ 'ignoreValidationDuringExecution',
157
+ 'requiresDataPath',
158
+ 'disabledOptions',
159
+ 'credentialTypes',
160
+ 'modes',
161
+ ]);
162
+
163
+ type NodePropertyOption = NonNullable<NodeProperty['options']>[number];
164
+
165
+ function isPropertyCollectionOption(
166
+ option: NodePropertyOption
167
+ ): option is Extract<NodePropertyOption, { values: NodeProperty[] }> {
168
+ return Array.isArray(option.values);
169
+ }
170
+
171
+ function simplifyProperty(prop: NodeProperty): NodeProperty | null {
172
+ if (NOISE_TYPES.has(prop.type)) {
173
+ return null;
174
+ }
175
+
176
+ const slim: NodeProperty = { ...prop };
177
+ for (const key of STRIP_KEYS) {
178
+ delete slim[key];
179
+ }
180
+
181
+ if (prop.type === 'resourceLocator') {
182
+ slim.type = 'string';
183
+ slim.default = '';
184
+ slim.description = slim.description || `${prop.displayName} ID`;
185
+ }
186
+
187
+ if (prop.options && Array.isArray(prop.options)) {
188
+ slim.options = prop.options.map((opt) => {
189
+ if (isPropertyCollectionOption(opt)) {
190
+ return {
191
+ name: opt.name,
192
+ displayName: opt.displayName,
193
+ values: opt.values.map(simplifyProperty).filter((v): v is NodeProperty => v !== null),
194
+ };
195
+ }
196
+ const { description: _d, ...rest } = opt;
197
+ return rest;
198
+ });
199
+ }
200
+
201
+ return slim;
202
+ }
203
+
204
+ /**
205
+ * Derive a `{ credType: requiredAuthValue }` map from a node's catalog
206
+ * credential entries. So Gmail's catalog produces
207
+ * `{ gmailOAuth2: "oAuth2", googleApi: "serviceAccount" }`. The LLM uses
208
+ * this to set `parameters.authentication` correctly when attaching a
209
+ * credentials block (Session 21 anti-hallucination Layer 2).
210
+ *
211
+ * Returns undefined when no credential entries gate on `authentication`.
212
+ */
213
+ function buildCredentialAuthMatrix(node: NodeDefinition): Record<string, string> | undefined {
214
+ if (!node.credentials?.length) {
215
+ return undefined;
216
+ }
217
+ const out: Record<string, string> = {};
218
+ for (const cred of node.credentials) {
219
+ const authValues = (cred.displayOptions as { show?: { authentication?: string[] } } | undefined)
220
+ ?.show?.authentication;
221
+ if (Array.isArray(authValues) && authValues.length === 1) {
222
+ out[cred.name] = authValues[0];
223
+ }
224
+ }
225
+ return Object.keys(out).length > 0 ? out : undefined;
226
+ }
227
+
228
+ export function simplifyNodeForLLM(node: NodeDefinition): NodeDefinition {
229
+ const cleaned = node.properties
230
+ .map(simplifyProperty)
231
+ .filter((p): p is NodeProperty => p !== null);
232
+
233
+ const seen = new Set<string>();
234
+ const deduped: NodeProperty[] = [];
235
+ for (const prop of cleaned) {
236
+ if (seen.has(prop.name)) {
237
+ continue;
238
+ }
239
+ seen.add(prop.name);
240
+ deduped.push(prop);
241
+ }
242
+
243
+ // Layer 2 (Session 21): always emit `version` as an array so the LLM
244
+ // sees the EXACT set of valid values (catches typeVersion hallucinations
245
+ // like 2.2 when only [1, 2, 2.1] exist). Pair with the prompt rule
246
+ // "pick the highest from version[]; never invent versions".
247
+ const versions: number[] = Array.isArray(node.version) ? [...node.version] : [node.version];
248
+
249
+ // Layer 2 (Session 21): expose the credential→authentication mapping
250
+ // so the LLM sets `parameters.authentication` correctly when it
251
+ // attaches a credentials block.
252
+ const credentialAuthMatrix = buildCredentialAuthMatrix(node);
253
+
254
+ return {
255
+ ...node,
256
+ version: versions,
257
+ properties: deduped,
258
+ ...(credentialAuthMatrix ? { credentialAuthMatrix } : {}),
259
+ } as NodeDefinition & { credentialAuthMatrix?: Record<string, string> };
260
+ }
@@ -0,0 +1,52 @@
1
+ import type { ClarificationRequest } from '../types';
2
+
3
+ /**
4
+ * Marker used by the service to flag clarifications produced by post-LLM
5
+ * catalog validation (vs. clarifications emitted by the LLM itself). Hosts
6
+ * may surface these differently if needed.
7
+ */
8
+ export const CATALOG_CLARIFICATION_SUFFIX =
9
+ '— please provide this value or clarify your requirements';
10
+
11
+ export function isCatalogClarificationString(value: string): boolean {
12
+ return value.endsWith(CATALOG_CLARIFICATION_SUFFIX);
13
+ }
14
+
15
+ export function isCatalogClarification(item: string | ClarificationRequest): boolean {
16
+ return typeof item === 'string'
17
+ ? isCatalogClarificationString(item)
18
+ : isCatalogClarificationString(item.question);
19
+ }
20
+
21
+ /**
22
+ * Normalize a mixed-shape clarifications array into structured
23
+ * `ClarificationRequest` objects. Legacy strings become `kind: 'free_text'`
24
+ * with an empty `paramPath` (host renders a free-form input instead of a
25
+ * picker). Structured items pass through unchanged.
26
+ */
27
+ export function coerceClarificationRequests(
28
+ items: ReadonlyArray<string | ClarificationRequest> | undefined | null
29
+ ): ClarificationRequest[] {
30
+ if (!items || items.length === 0) {
31
+ return [];
32
+ }
33
+ const out: ClarificationRequest[] = [];
34
+ for (const item of items) {
35
+ if (typeof item === 'string') {
36
+ const trimmed = item.trim();
37
+ if (trimmed.length === 0) {
38
+ continue;
39
+ }
40
+ out.push({ kind: 'free_text', question: trimmed, paramPath: '' });
41
+ } else if (item && typeof item === 'object' && typeof item.question === 'string') {
42
+ out.push({
43
+ kind: item.kind ?? 'free_text',
44
+ platform: item.platform,
45
+ scope: item.scope,
46
+ question: item.question,
47
+ paramPath: typeof item.paramPath === 'string' ? item.paramPath : '',
48
+ });
49
+ }
50
+ }
51
+ return out;
52
+ }
@@ -0,0 +1,22 @@
1
+ import type { IAgentRuntime, Memory, State, UUID } from '@elizaos/core';
2
+
3
+ export function buildConversationContext(message: Memory, state: State | undefined): string {
4
+ const raw = state?.values?.recentMessages;
5
+ const recentMessages = typeof raw === 'string' ? raw : '';
6
+ const currentText = message.content?.text ?? '';
7
+
8
+ if (!recentMessages) {
9
+ return currentText;
10
+ }
11
+
12
+ return `${recentMessages}\n\nCurrent request: ${currentText}`;
13
+ }
14
+
15
+ export async function getUserTagName(runtime: IAgentRuntime, userId: string): Promise<string> {
16
+ const entity = await runtime.getEntityById(userId as UUID);
17
+ const shortId = userId.replace(/-/g, '').slice(0, 8);
18
+ const name = entity?.names?.[0];
19
+ // ElizaOS default name is "User" + UUID — not useful for a tag
20
+ const isRealName = name && !name.includes(userId.slice(0, 8));
21
+ return isRealName ? `${name}_${shortId}` : `user_${shortId}`;
22
+ }
@@ -0,0 +1,234 @@
1
+ import { logger } from '@elizaos/core';
2
+ import type {
3
+ CredentialProvider,
4
+ CredentialResolutionResult,
5
+ MissingConnection,
6
+ WorkflowCredentialStoreApi,
7
+ WorkflowDefinition,
8
+ WorkflowPluginConfig,
9
+ } from '../types/index';
10
+
11
+ interface CredentialApiClient {
12
+ createCredential(credential: {
13
+ name: string;
14
+ type: string;
15
+ data: Record<string, unknown>;
16
+ }): Promise<{ id: string }>;
17
+ }
18
+
19
+ /**
20
+ * Resolve and inject credentials into workflow.
21
+ *
22
+ * Resolution chain (first match wins):
23
+ * 1. Credential store DB — cached mappings from previous resolutions
24
+ * 2. Static config — character.settings.workflows.credentials
25
+ * 3. External provider — registered CredentialProvider service (e.g. cloud OAuth)
26
+ * 4. Missing — reported for manual configuration in workflows
27
+ */
28
+ export async function resolveCredentials(
29
+ workflow: WorkflowDefinition,
30
+ userId: string,
31
+ config: WorkflowPluginConfig,
32
+ credStore: WorkflowCredentialStoreApi | null,
33
+ credProvider: CredentialProvider | null,
34
+ apiClient: CredentialApiClient | null,
35
+ tagName: string
36
+ ): Promise<CredentialResolutionResult> {
37
+ const requiredCredTypes = extractRequiredCredentialTypes(workflow);
38
+
39
+ if (requiredCredTypes.size === 0) {
40
+ return {
41
+ workflow,
42
+ missingConnections: [],
43
+ injectedCredentials: new Map(),
44
+ };
45
+ }
46
+
47
+ const injectedCredentials = new Map<string, string>();
48
+ const missingConnections: MissingConnection[] = [];
49
+
50
+ for (const credType of requiredCredTypes) {
51
+ const credId = await resolveOneCredential(
52
+ credType,
53
+ userId,
54
+ config,
55
+ credStore,
56
+ credProvider,
57
+ apiClient,
58
+ missingConnections,
59
+ tagName
60
+ );
61
+
62
+ if (credId) {
63
+ injectedCredentials.set(credType, credId);
64
+ }
65
+ }
66
+
67
+ const resolvedWorkflow = injectCredentialIds(workflow, injectedCredentials);
68
+
69
+ return {
70
+ workflow: resolvedWorkflow,
71
+ missingConnections,
72
+ injectedCredentials,
73
+ };
74
+ }
75
+
76
+ async function resolveOneCredential(
77
+ credType: string,
78
+ userId: string,
79
+ config: WorkflowPluginConfig,
80
+ credStore: WorkflowCredentialStoreApi | null,
81
+ credProvider: CredentialProvider | null,
82
+ apiClient: CredentialApiClient | null,
83
+ missingConnections: MissingConnection[],
84
+ tagName: string
85
+ ): Promise<string | null> {
86
+ // 1. Credential store DB
87
+ const cachedId = await credStore?.get(userId, credType);
88
+ if (cachedId) {
89
+ logger.debug(
90
+ { src: 'plugin:workflow:utils:credentials' },
91
+ `Resolved ${credType} from credential store`
92
+ );
93
+ return cachedId;
94
+ }
95
+
96
+ // 2. Static config
97
+ if (config.credentials) {
98
+ const configId = findCredentialId(config.credentials, credType);
99
+ if (configId) {
100
+ return configId;
101
+ }
102
+ }
103
+
104
+ // 3. External provider
105
+ if (credProvider) {
106
+ try {
107
+ const result = await credProvider.resolve(userId, credType);
108
+
109
+ if (result?.status === 'credential_data') {
110
+ if (!apiClient) {
111
+ logger.error(
112
+ { src: 'plugin:workflow:utils:credentials' },
113
+ `Received credential_data for ${credType} but no apiClient available`
114
+ );
115
+ missingConnections.push({ credType });
116
+ return null;
117
+ }
118
+ const credName = `${credType}_${tagName}`;
119
+ const workflowsCred = await apiClient.createCredential({
120
+ name: credName,
121
+ type: credType,
122
+ data: result.data,
123
+ });
124
+ try {
125
+ await credStore?.set(userId, credType, workflowsCred.id);
126
+ } catch (cacheError) {
127
+ logger.warn(
128
+ { src: 'plugin:workflow:utils:credentials' },
129
+ `Failed to cache credential mapping for ${credType} (credential still usable): ${cacheError instanceof Error ? cacheError.message : String(cacheError)}`
130
+ );
131
+ }
132
+ logger.info(
133
+ { src: 'plugin:workflow:utils:credentials' },
134
+ `Created workflows credential for ${credType}: ${workflowsCred.id}`
135
+ );
136
+ return workflowsCred.id;
137
+ }
138
+
139
+ if (result?.status === 'needs_auth') {
140
+ missingConnections.push({ credType, authUrl: result.authUrl });
141
+ return null;
142
+ }
143
+ } catch (error) {
144
+ logger.error(
145
+ { src: 'plugin:workflow:utils:credentials' },
146
+ `Credential provider failed for ${credType}: ${error instanceof Error ? error.message : String(error)}`
147
+ );
148
+ }
149
+ }
150
+
151
+ // 4. Missing
152
+ missingConnections.push({ credType });
153
+ return null;
154
+ }
155
+
156
+ /**
157
+ * Look up a credential ID from config, tolerating naming mismatches
158
+ * (e.g. LLM generates "gmailOAuth2Api" but config has "gmailOAuth2", or vice-versa).
159
+ */
160
+ function findCredentialId(credentials: Record<string, string>, credType: string): string | null {
161
+ if (credentials[credType]) {
162
+ return credentials[credType];
163
+ }
164
+
165
+ // Fuzzy: try without "Api" suffix (e.g. "gmailOAuth2Api" → "gmailOAuth2")
166
+ const withoutApi = credType.replace(/Api$/, '');
167
+ if (withoutApi !== credType && credentials[withoutApi]) {
168
+ logger.debug(
169
+ { src: 'plugin:workflow:utils:credentials' },
170
+ `Fuzzy credential match: "${credType}" → "${withoutApi}" (removed Api suffix)`
171
+ );
172
+ return credentials[withoutApi];
173
+ }
174
+
175
+ // Fuzzy: try with "Api" suffix (e.g. "gmailOAuth2" → "gmailOAuth2Api")
176
+ const withApi = `${credType}Api`;
177
+ if (credentials[withApi]) {
178
+ logger.debug(
179
+ { src: 'plugin:workflow:utils:credentials' },
180
+ `Fuzzy credential match: "${credType}" → "${withApi}" (added Api suffix)`
181
+ );
182
+ return credentials[withApi];
183
+ }
184
+
185
+ return null;
186
+ }
187
+
188
+ function extractRequiredCredentialTypes(workflow: WorkflowDefinition): Set<string> {
189
+ const credTypes = new Set<string>();
190
+
191
+ for (const node of workflow.nodes) {
192
+ if (node.credentials) {
193
+ for (const credType of Object.keys(node.credentials)) {
194
+ credTypes.add(credType);
195
+ }
196
+ }
197
+ }
198
+
199
+ return credTypes;
200
+ }
201
+
202
+ function injectCredentialIds(
203
+ workflow: WorkflowDefinition,
204
+ credentialMap: Map<string, string>
205
+ ): WorkflowDefinition {
206
+ const injected = { ...workflow };
207
+ injected.nodes = workflow.nodes.map((node) => {
208
+ if (!node.credentials) {
209
+ return node;
210
+ }
211
+
212
+ const updatedCredentials: typeof node.credentials = {};
213
+
214
+ for (const [credType, credRef] of Object.entries(node.credentials)) {
215
+ const credId = credentialMap.get(credType);
216
+
217
+ if (credId) {
218
+ updatedCredentials[credType] = {
219
+ id: credId,
220
+ name: credRef.name,
221
+ };
222
+ } else {
223
+ updatedCredentials[credType] = credRef;
224
+ }
225
+ }
226
+
227
+ return {
228
+ ...node,
229
+ credentials: updatedCredentials,
230
+ };
231
+ });
232
+
233
+ return injected;
234
+ }