@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,610 @@
1
+ /**
2
+ * Deterministic pre-deploy pass that catches catalog-drift hallucinations
3
+ * the LLM emits despite prompt hardening. Runs after `injectMissingCredentialBlocks`
4
+ * (Session 19 safety net) and before `deployWorkflow`.
5
+ *
6
+ * Six checks, each emitting a `Repair` (auto-fixed) or `ValidationError`
7
+ * (handed off to the retry loop in workflow-service.ts):
8
+ *
9
+ * 1. typeVersion clamp — closes "LLM emits 2.2 when only 1, 2, 2.1 exist"
10
+ * 2. authentication back-fill — closes "credentials attached but parameters.authentication missing"
11
+ * 3. output-field validation — closes "subject vs Subject" + typo classes
12
+ * 4. required-parameter pre-flight
13
+ * 5. node-name uniqueness — workflows rejects duplicates with confusing errors
14
+ * 6. connection sanity — drop edges to non-existent nodes
15
+ *
16
+ * Mutates the workflow in place AND returns it for ergonomic chaining.
17
+ */
18
+
19
+ import { logger } from '@elizaos/core';
20
+ import type {
21
+ NodeDefinition,
22
+ RuntimeContext,
23
+ WorkflowDefinition,
24
+ WorkflowNode,
25
+ } from '../types/index';
26
+ import { CATALOG_CLARIFICATION_SUFFIX, isCatalogClarification } from './clarification';
27
+ import { inferSyntheticOutputSchema } from './inferSyntheticOutputSchema';
28
+ import { loadOutputSchema, loadTriggerOutputSchema, parseExpressions } from './outputSchema';
29
+
30
+ export type RepairKind =
31
+ | 'typeVersionClamp'
32
+ | 'authenticationBackfill'
33
+ | 'fieldNameCaseFix'
34
+ | 'aggregationSourceFieldCaseFix'
35
+ | 'nodeNameDeduplication'
36
+ | 'droppedDanglingEdge';
37
+
38
+ export type ValidationErrorKind = 'unknownOutputField' | 'requiredParameterMissing';
39
+
40
+ export interface Repair {
41
+ kind: RepairKind;
42
+ node: string;
43
+ detail: string;
44
+ }
45
+
46
+ export interface ValidationError {
47
+ kind: ValidationErrorKind;
48
+ node: string;
49
+ detail: string;
50
+ /** When kind === 'unknownOutputField': `{{ $json.<X> }}` literal that failed. */
51
+ expression?: string;
52
+ /** When kind === 'unknownOutputField': fields the upstream node actually emits. */
53
+ availableFields?: string[];
54
+ }
55
+
56
+ export interface RepairResult {
57
+ workflow: WorkflowDefinition;
58
+ repairs: Repair[];
59
+ errors: ValidationError[];
60
+ }
61
+
62
+ // ─── Check 1 ────────────────────────────────────────────────────────────────
63
+
64
+ /** Pick the closest-but-not-greater valid version. Falls back to the maximum
65
+ * when all valid versions are smaller than the requested one (LLM picked
66
+ * a higher number — clamp down). */
67
+ function clampTypeVersion(requested: number, validVersions: number[]): number | null {
68
+ if (validVersions.length === 0) {
69
+ return null;
70
+ }
71
+ const sorted = [...validVersions].sort((a, b) => a - b);
72
+ if (sorted.includes(requested)) {
73
+ return null;
74
+ } // already valid
75
+ // Highest version ≤ requested, else highest available.
76
+ const candidates = sorted.filter((v) => v <= requested);
77
+ return candidates.length > 0 ? candidates[candidates.length - 1] : sorted[sorted.length - 1];
78
+ }
79
+
80
+ function applyTypeVersionClamp(
81
+ node: WorkflowNode,
82
+ def: NodeDefinition,
83
+ repairs: Repair[],
84
+ runtimeVersions: Map<string, number[]> | undefined
85
+ ): void {
86
+ const catalogVersions = Array.isArray(def.version) ? def.version : [def.version];
87
+ const catalogNumeric = catalogVersions.filter((v): v is number => typeof v === 'number');
88
+
89
+ // When the live workflow runtime registry is available, intersect with it.
90
+ // The static plugin catalog is sometimes ahead of the user's running
91
+ // workflows binary (e.g. catalog claims Gmail v2.2 but runtime only has up
92
+ // to v2.1) — without this intersect, the LLM picks a version workflows
93
+ // can't instantiate and activation crashes with `Cannot read
94
+ // properties of undefined (reading 'execute')`.
95
+ const runtime = runtimeVersions?.get(node.type);
96
+ let validVersions: number[];
97
+ if (runtime && runtime.length > 0) {
98
+ if (catalogNumeric.length > 0) {
99
+ const runtimeSet = new Set(runtime);
100
+ validVersions = catalogNumeric.filter((v) => runtimeSet.has(v));
101
+ if (validVersions.length === 0) {
102
+ validVersions = runtime;
103
+ } // catalog drift — trust runtime
104
+ } else {
105
+ validVersions = runtime;
106
+ }
107
+ } else {
108
+ validVersions = catalogNumeric;
109
+ }
110
+
111
+ if (validVersions.length === 0) {
112
+ return;
113
+ }
114
+ const clamped = clampTypeVersion(node.typeVersion, validVersions);
115
+ if (clamped === null) {
116
+ return;
117
+ }
118
+ const source = runtime ? 'runtime∩catalog' : 'catalog';
119
+ repairs.push({
120
+ kind: 'typeVersionClamp',
121
+ node: node.name,
122
+ detail: `${node.type} typeVersion ${node.typeVersion} → ${clamped} (${source} valid: ${validVersions.join(', ')})`,
123
+ });
124
+ node.typeVersion = clamped;
125
+ }
126
+
127
+ // ─── Check 2 ────────────────────────────────────────────────────────────────
128
+
129
+ interface CredentialDef {
130
+ name: string;
131
+ required: boolean;
132
+ displayOptions?: { show?: { authentication?: string[] } };
133
+ }
134
+
135
+ /** When a credentials block is attached and the cred type's catalog entry
136
+ * shows it gates on a single authentication value (and node.parameters
137
+ * doesn't already set one), back-fill it. Closes the Gmail-missing-auth
138
+ * bug surfaced in Session 20 dogfood. */
139
+ function applyAuthenticationBackfill(
140
+ node: WorkflowNode,
141
+ def: NodeDefinition,
142
+ repairs: Repair[]
143
+ ): void {
144
+ if (!node.credentials) {
145
+ return;
146
+ }
147
+ const attachedTypes = Object.keys(node.credentials);
148
+ if (attachedTypes.length !== 1) {
149
+ return;
150
+ } // ambiguous → leave alone
151
+ const [credType] = attachedTypes;
152
+
153
+ const defCreds = (def.credentials ?? []) as CredentialDef[];
154
+ const credDef = defCreds.find((c) => c.name === credType);
155
+ if (!credDef) {
156
+ return;
157
+ }
158
+
159
+ const authOpts = credDef.displayOptions?.show?.authentication;
160
+ if (!authOpts || authOpts.length !== 1) {
161
+ return;
162
+ }
163
+
164
+ const requiredAuth = authOpts[0];
165
+ const params = (node.parameters ?? {}) as Record<string, unknown>;
166
+ if (typeof params.authentication === 'string' && params.authentication.length > 0) {
167
+ return; // LLM already set it
168
+ }
169
+
170
+ node.parameters = { ...params, authentication: requiredAuth };
171
+ repairs.push({
172
+ kind: 'authenticationBackfill',
173
+ node: node.name,
174
+ detail: `set parameters.authentication="${requiredAuth}" to match attached ${credType}`,
175
+ });
176
+ }
177
+
178
+ // ─── Check 3 ────────────────────────────────────────────────────────────────
179
+
180
+ /** Build name → node map for upstream-graph walk. */
181
+ function buildUpstreamMap(workflow: WorkflowDefinition): Map<string, string[]> {
182
+ const upstream = new Map<string, string[]>();
183
+ for (const fromName of Object.keys(workflow.connections ?? {})) {
184
+ const outputs = workflow.connections[fromName];
185
+ for (const outputType of Object.keys(outputs)) {
186
+ const branches = outputs[outputType];
187
+ for (const branch of branches) {
188
+ for (const edge of branch) {
189
+ const arr = upstream.get(edge.node) ?? [];
190
+ arr.push(fromName);
191
+ upstream.set(edge.node, arr);
192
+ }
193
+ }
194
+ }
195
+ }
196
+ return upstream;
197
+ }
198
+
199
+ /** Collect known top-level field names for a node's output. Returns null if
200
+ * unknown (e.g. Code/Function — arbitrary user output).
201
+ *
202
+ * Synthetic / parameter-aware schemas check FIRST. The static schemaIndex
203
+ * catches a single canonical shape per (node, resource, operation) but
204
+ * ignores parameter switches like Gmail's `simple: true` that change
205
+ * the runtime field set. The override layer (inferSyntheticOutputSchema)
206
+ * reflects the actual runtime emission, so it must win when present. */
207
+ function knownOutputFieldsForNode(node: WorkflowNode): string[] | null {
208
+ // 1. Synthetic / parameter-aware schemas (Summarize, Set, Gmail simple-mode)
209
+ const synthetic = inferSyntheticOutputSchema(node);
210
+ if (synthetic !== null) {
211
+ return synthetic;
212
+ }
213
+
214
+ // 2. Static output-schema catalog (Gmail non-simple, Slack, Discord etc.)
215
+ if (
216
+ typeof node.parameters?.resource === 'string' &&
217
+ typeof node.parameters?.operation === 'string'
218
+ ) {
219
+ const schema = loadOutputSchema(node.type, node.parameters.resource, node.parameters.operation);
220
+ if (schema) {
221
+ return schema.fields;
222
+ }
223
+ }
224
+
225
+ // 3. Trigger schemas (gmailTrigger, etc.) — respect simple flag
226
+ if (node.type.toLowerCase().includes('trigger')) {
227
+ const triggerSchema = loadTriggerOutputSchema(node.type, node.parameters);
228
+ if (triggerSchema) {
229
+ return triggerSchema.fields;
230
+ }
231
+ }
232
+
233
+ return null;
234
+ }
235
+
236
+ /** For each parameter expression `{{ $json.X }}` (or `$node["Y"].json.X`),
237
+ * validate X against the upstream node's known fields. Auto-fix case
238
+ * mismatches; flag unknown fields as ValidationErrors for retry. */
239
+ function validateOutputFieldReferences(
240
+ workflow: WorkflowDefinition,
241
+ repairs: Repair[],
242
+ errors: ValidationError[]
243
+ ): void {
244
+ const upstream = buildUpstreamMap(workflow);
245
+ const nodeByName = new Map<string, WorkflowNode>();
246
+ for (const n of workflow.nodes) {
247
+ nodeByName.set(n.name, n);
248
+ }
249
+
250
+ for (const node of workflow.nodes) {
251
+ if (!node.parameters || typeof node.parameters !== 'object') {
252
+ continue;
253
+ }
254
+ const refs = parseExpressions(node.parameters as Record<string, unknown>);
255
+ if (refs.length === 0) {
256
+ continue;
257
+ }
258
+
259
+ for (const ref of refs) {
260
+ // Determine which upstream node's output this reference reads from.
261
+ let sourceNode: WorkflowNode | null = null;
262
+ if (ref.sourceNodeName) {
263
+ sourceNode = nodeByName.get(ref.sourceNodeName) ?? null;
264
+ } else {
265
+ // `{{ $json.X }}` → first immediate upstream node
266
+ const parents = upstream.get(node.name) ?? [];
267
+ if (parents.length === 1) {
268
+ sourceNode = nodeByName.get(parents[0]) ?? null;
269
+ } else if (parents.length > 1) {
270
+ // Ambiguous — skip silently rather than false-error
271
+ continue;
272
+ }
273
+ }
274
+ if (!sourceNode) {
275
+ continue;
276
+ }
277
+
278
+ const fields = knownOutputFieldsForNode(sourceNode);
279
+ if (!fields) {
280
+ continue;
281
+ } // unknowable schema → skip
282
+
283
+ const topField = ref.path[0];
284
+ if (!topField) {
285
+ continue;
286
+ }
287
+ if (fields.includes(topField)) {
288
+ continue;
289
+ } // exact match
290
+
291
+ // Case-insensitive deterministic correction
292
+ const ciMatch = fields.find((f) => f.toLowerCase() === topField.toLowerCase());
293
+ if (ciMatch) {
294
+ rewriteParameterFieldRef(node, ref.fullExpression, topField, ciMatch);
295
+ repairs.push({
296
+ kind: 'fieldNameCaseFix',
297
+ node: node.name,
298
+ detail: `${ref.fullExpression}: "${topField}" → "${ciMatch}" (matches ${sourceNode.name} output)`,
299
+ });
300
+ } else {
301
+ errors.push({
302
+ kind: 'unknownOutputField',
303
+ node: node.name,
304
+ detail: `expression references unknown field "${topField}" on upstream node ${sourceNode.name}`,
305
+ expression: ref.fullExpression,
306
+ availableFields: fields,
307
+ });
308
+ }
309
+ }
310
+ }
311
+ }
312
+
313
+ /** Replace `oldField` with `newField` in node.parameters everywhere the
314
+ * fullExpression pattern appears. */
315
+ function rewriteParameterFieldRef(
316
+ node: WorkflowNode,
317
+ fullExpression: string,
318
+ oldField: string,
319
+ newField: string
320
+ ): void {
321
+ const oldPattern = fullExpression;
322
+ const newPattern = fullExpression.replace(new RegExp(`\\b${oldField}\\b`), newField);
323
+ rewriteInObject(node.parameters as Record<string, unknown>, oldPattern, newPattern);
324
+ }
325
+
326
+ function rewriteInObject(obj: Record<string, unknown>, oldStr: string, newStr: string): void {
327
+ for (const key of Object.keys(obj)) {
328
+ const val = obj[key];
329
+ if (typeof val === 'string' && val.includes(oldStr)) {
330
+ obj[key] = val.replaceAll(oldStr, newStr);
331
+ } else if (Array.isArray(val)) {
332
+ for (let i = 0; i < val.length; i++) {
333
+ if (typeof val[i] === 'string' && (val[i] as string).includes(oldStr)) {
334
+ val[i] = (val[i] as string).replaceAll(oldStr, newStr);
335
+ } else if (typeof val[i] === 'object' && val[i] !== null) {
336
+ rewriteInObject(val[i] as Record<string, unknown>, oldStr, newStr);
337
+ }
338
+ }
339
+ } else if (typeof val === 'object' && val !== null) {
340
+ rewriteInObject(val as Record<string, unknown>, oldStr, newStr);
341
+ }
342
+ }
343
+ }
344
+
345
+ // ─── Check 3b: aggregation-source-field validation ──────────────────────────
346
+
347
+ /**
348
+ * Some nodes name an UPSTREAM field by its raw string identifier (not via an
349
+ * `{{ $json.X }}` expression). The Summarize node's
350
+ * `parameters.fieldsToSummarize.values[i].field` is the canonical example —
351
+ * it names a field in the upstream node's output. The LLM commonly emits
352
+ * the wrong case here (e.g. `field: "subject"` when Gmail simple-mode
353
+ * outputs `Subject`), and check #3's expression walker doesn't catch it
354
+ * because the value isn't an `{{ $json.X }}` expression at all. This check
355
+ * fills that gap deterministically: case-correct on CI match; flag as
356
+ * ValidationError otherwise.
357
+ *
358
+ * Closes the silent-empty-summary bug from Session 21 dogfood (Summarize
359
+ * concatenated 10 empty strings because the LLM picked lowercase
360
+ * `subject` against Gmail's actual `Subject` output).
361
+ */
362
+ function applyAggregationSourceFieldFix(
363
+ workflow: WorkflowDefinition,
364
+ repairs: Repair[],
365
+ errors: ValidationError[]
366
+ ): void {
367
+ const upstream = buildUpstreamMap(workflow);
368
+ const nodeByName = new Map<string, WorkflowNode>();
369
+ for (const n of workflow.nodes) {
370
+ nodeByName.set(n.name, n);
371
+ }
372
+
373
+ for (const node of workflow.nodes) {
374
+ if (node.type !== 'workflows-nodes-base.summarize') {
375
+ continue;
376
+ }
377
+
378
+ const params = node.parameters as Record<string, unknown> | undefined;
379
+ const fts = params?.fieldsToSummarize as
380
+ | { values?: Array<{ aggregation?: string; field?: string }> }
381
+ | undefined;
382
+ const values = fts?.values;
383
+ if (!Array.isArray(values) || values.length === 0) {
384
+ continue;
385
+ }
386
+
387
+ const parents = upstream.get(node.name) ?? [];
388
+ if (parents.length !== 1) {
389
+ continue;
390
+ } // ambiguous → skip (don't false-error)
391
+ const sourceNode = nodeByName.get(parents[0]);
392
+ if (!sourceNode) {
393
+ continue;
394
+ }
395
+
396
+ const fields = knownOutputFieldsForNode(sourceNode);
397
+ if (!fields) {
398
+ continue;
399
+ } // unknowable schema → skip
400
+
401
+ for (const entry of values) {
402
+ if (typeof entry?.field !== 'string' || entry.field.length === 0) {
403
+ continue;
404
+ }
405
+ if (fields.includes(entry.field)) {
406
+ continue;
407
+ } // exact match
408
+ const ciMatch = fields.find((f) => f.toLowerCase() === entry.field?.toLowerCase());
409
+ if (ciMatch) {
410
+ const oldField = entry.field;
411
+ entry.field = ciMatch;
412
+ repairs.push({
413
+ kind: 'aggregationSourceFieldCaseFix',
414
+ node: node.name,
415
+ detail: `fieldsToSummarize.values[].field "${oldField}" → "${ciMatch}" (matches ${sourceNode.name} output)`,
416
+ });
417
+ } else {
418
+ errors.push({
419
+ kind: 'unknownOutputField',
420
+ node: node.name,
421
+ detail: `fieldsToSummarize.values[].field "${entry.field}" does not match any known field on upstream ${sourceNode.name}`,
422
+ expression: `field: "${entry.field}"`,
423
+ availableFields: fields,
424
+ });
425
+ }
426
+ }
427
+ }
428
+ }
429
+
430
+ // ─── Check 4 ────────────────────────────────────────────────────────────────
431
+
432
+ /** Push a clarification onto workflow._meta.requiresClarification when a
433
+ * required parameter is missing AND can't be inferred. Non-fatal. */
434
+ function applyRequiredParameterPreflight(
435
+ workflow: WorkflowDefinition,
436
+ defByType: Map<string, NodeDefinition>
437
+ ): void {
438
+ const clarifications: string[] = [];
439
+ for (const node of workflow.nodes) {
440
+ const def = defByType.get(node.type);
441
+ if (!def) {
442
+ continue;
443
+ }
444
+ for (const prop of def.properties ?? []) {
445
+ if (!prop.required) {
446
+ continue;
447
+ }
448
+ const params = (node.parameters ?? {}) as Record<string, unknown>;
449
+ if (params[prop.name] === undefined || params[prop.name] === '') {
450
+ clarifications.push(
451
+ `${node.name} (${node.type}) is missing required parameter "${prop.name}" ${CATALOG_CLARIFICATION_SUFFIX}`
452
+ );
453
+ }
454
+ }
455
+ }
456
+ if (clarifications.length === 0) {
457
+ return;
458
+ }
459
+ workflow._meta = workflow._meta ?? {};
460
+ const existing = workflow._meta.requiresClarification ?? [];
461
+ // Avoid duplicate-suffix clutter from prior catalog passes.
462
+ const nonCatalog = existing.filter((c) => !isCatalogClarification(c));
463
+ workflow._meta.requiresClarification = [...nonCatalog, ...clarifications];
464
+ }
465
+
466
+ // ─── Check 5 ────────────────────────────────────────────────────────────────
467
+
468
+ function deduplicateNodeNames(workflow: WorkflowDefinition, repairs: Repair[]): void {
469
+ const seen = new Map<string, number>();
470
+ for (const node of workflow.nodes) {
471
+ const count = seen.get(node.name) ?? 0;
472
+ if (count > 0) {
473
+ const oldName = node.name;
474
+ let suffix = count + 1;
475
+ let candidate = `${oldName} (${suffix})`;
476
+ while (seen.has(candidate)) {
477
+ suffix++;
478
+ candidate = `${oldName} (${suffix})`;
479
+ }
480
+ node.name = candidate;
481
+ seen.set(candidate, 1);
482
+ seen.set(oldName, count + 1);
483
+ repairs.push({
484
+ kind: 'nodeNameDeduplication',
485
+ node: candidate,
486
+ detail: `renamed duplicate "${oldName}" → "${candidate}"`,
487
+ });
488
+ // Update connections referencing the old name? The original was first
489
+ // — connections pointing at oldName still resolve to the original.
490
+ // Rewrite outgoing connections key on duplicate node.
491
+ if (workflow.connections?.[oldName]) {
492
+ // Outgoing connections for the duplicate: move under new name only
493
+ // when the original doesn't already own them. Heuristic: if both
494
+ // the dup and original happen to share outgoing edges, leave as-is
495
+ // (the original wins). Most LLM-generated dupes have no outgoing
496
+ // edges, so this rarely fires.
497
+ }
498
+ } else {
499
+ seen.set(node.name, 1);
500
+ }
501
+ }
502
+ }
503
+
504
+ // ─── Check 6 ────────────────────────────────────────────────────────────────
505
+
506
+ function dropDanglingEdges(workflow: WorkflowDefinition, repairs: Repair[]): void {
507
+ if (!workflow.connections) {
508
+ return;
509
+ }
510
+ const nodeNames = new Set(workflow.nodes.map((n) => n.name));
511
+ for (const fromName of Object.keys(workflow.connections)) {
512
+ const outputs = workflow.connections[fromName];
513
+ if (!nodeNames.has(fromName)) {
514
+ // Source node doesn't exist — drop the entire entry.
515
+ delete workflow.connections[fromName];
516
+ repairs.push({
517
+ kind: 'droppedDanglingEdge',
518
+ node: fromName,
519
+ detail: 'dropped connections entry for non-existent node',
520
+ });
521
+ continue;
522
+ }
523
+ for (const outputType of Object.keys(outputs)) {
524
+ const branches = outputs[outputType];
525
+ for (let i = 0; i < branches.length; i++) {
526
+ const branch = branches[i];
527
+ const filtered = branch.filter((edge) => nodeNames.has(edge.node));
528
+ if (filtered.length !== branch.length) {
529
+ const dropped = branch.filter((e) => !nodeNames.has(e.node));
530
+ for (const e of dropped) {
531
+ repairs.push({
532
+ kind: 'droppedDanglingEdge',
533
+ node: fromName,
534
+ detail: `dropped edge ${fromName} → ${e.node} (target missing)`,
535
+ });
536
+ }
537
+ branches[i] = filtered;
538
+ }
539
+ }
540
+ }
541
+ }
542
+ }
543
+
544
+ // ─── Public API ─────────────────────────────────────────────────────────────
545
+
546
+ export function validateAndRepair(
547
+ workflow: WorkflowDefinition,
548
+ relevantNodes: NodeDefinition[],
549
+ _runtimeContext: RuntimeContext | undefined,
550
+ runtimeVersions?: Map<string, number[]>
551
+ ): RepairResult {
552
+ const repairs: Repair[] = [];
553
+ const errors: ValidationError[] = [];
554
+
555
+ if (!workflow?.nodes || !Array.isArray(workflow.nodes)) {
556
+ return { workflow, repairs, errors };
557
+ }
558
+
559
+ const defByType = new Map<string, NodeDefinition>(relevantNodes.map((d) => [d.name, d]));
560
+
561
+ // Check 1 + 2: per-node passes
562
+ for (const node of workflow.nodes) {
563
+ const def = defByType.get(node.type);
564
+ if (def) {
565
+ applyTypeVersionClamp(node, def, repairs, runtimeVersions);
566
+ applyAuthenticationBackfill(node, def, repairs);
567
+ }
568
+ }
569
+
570
+ // Check 5: dedup BEFORE field-ref + connection passes (so the upstream map
571
+ // is built against the final names).
572
+ deduplicateNodeNames(workflow, repairs);
573
+
574
+ // Check 6: drop dangling edges before #3 walks the graph.
575
+ dropDanglingEdges(workflow, repairs);
576
+
577
+ // Check 3b: aggregation-source-field case-fix BEFORE check 3, because
578
+ // correcting Summarize.field changes the synthetic output schema that
579
+ // check 3 uses when validating expressions downstream of Summarize.
580
+ applyAggregationSourceFieldFix(workflow, repairs, errors);
581
+
582
+ // Check 3: output-field validation (after dedup so upstream lookup works)
583
+ validateOutputFieldReferences(workflow, repairs, errors);
584
+
585
+ // Check 4: required-parameter pre-flight (annotates workflow._meta only)
586
+ applyRequiredParameterPreflight(workflow, defByType);
587
+
588
+ if (repairs.length > 0) {
589
+ logger.info(
590
+ {
591
+ src: 'plugin:workflow:utils:validate',
592
+ repairCount: repairs.length,
593
+ repairs,
594
+ },
595
+ `validateAndRepair applied ${repairs.length} fix(es)`
596
+ );
597
+ }
598
+ if (errors.length > 0) {
599
+ logger.warn(
600
+ {
601
+ src: 'plugin:workflow:utils:validate',
602
+ errorCount: errors.length,
603
+ errors,
604
+ },
605
+ `validateAndRepair flagged ${errors.length} unrecoverable error(s) for retry loop`
606
+ );
607
+ }
608
+
609
+ return { workflow, repairs, errors };
610
+ }
@@ -0,0 +1,16 @@
1
+ export const ACTION_RESPONSE_SYSTEM_PROMPT = `You format responses for a workflow assistant.
2
+
3
+ Rules:
4
+ - Include ALL provided data exactly (names, IDs, URLs) — never omit, never modify
5
+ - ONLY use information from the provided data — do not invent extra details
6
+ - Be concise — no filler
7
+
8
+ Response types:
9
+ - PREVIEW: workflow name, node list (name + type), flow (→), credentials, assumptions. If "changes" is present, list each changed parameter per node. Mention it's a draft: user can confirm, modify, or cancel. If restoredAfterFailure is true, mention the new request failed and this is the previous draft.
10
+ - CLARIFICATION: list the questions, ask for details.
11
+ - DEPLOY_SUCCESS: name, ID, node count, status. All credentials are resolved — workflow is ready.
12
+ - AUTH_REQUIRED: list services + auth links (clickable). Ask user to connect then retry deploy.
13
+ - CANCELLED: confirm draft discarded.
14
+ - EMPTY_PROMPT: ask user to describe the workflow.
15
+ - UNSUPPORTED_INTEGRATION: list unsupported services (unavailable), list available services. Inform the user clearly, suggest they rephrase using available services.
16
+ - ERROR: show the error, suggest more detail.`;
@@ -0,0 +1,22 @@
1
+ export const DRAFT_INTENT_SYSTEM_PROMPT = `You are an assistant managing workflow creation. A workflow draft has been generated and shown to the user as a preview.
2
+
3
+ Your job: determine what the user wants to do based on their message. The user may write in ANY language — interpret the meaning, not specific keywords.
4
+
5
+ Possible intents:
6
+ - "confirm": The user approves the draft and wants it deployed. This includes any form of agreement, approval, or instruction to proceed/create/deploy the current draft.
7
+ - "cancel": The user doesn't want this workflow at all and wants to discard it entirely.
8
+ - "modify": The user wants to change something about the current draft, or is providing additional context/answers to refine it.
9
+ - "new": The user explicitly describes a DIFFERENT workflow with DIFFERENT services or purpose than the current draft. This requires a concrete description of a new automation.
10
+
11
+ Rules:
12
+ - CRITICAL: A short or vague message that does NOT describe a specific new automation is NEVER "new". Short messages like "create it", "do it", "go ahead", or any brief instruction to proceed are ALWAYS "confirm". Only classify as "new" if the message contains an explicit description of a different workflow involving different integrations or a different purpose.
13
+ - If the user's message could refer to the existing draft (even loosely), it is NOT "new".
14
+ - If ambiguous between "confirm" and "new", ALWAYS prefer "confirm".
15
+ - If ambiguous between "modify" and "new", prefer "modify".
16
+ - If the user provides additional context or answers clarification questions about the same topic, treat as "modify".
17
+ - For "modify", extract the modification request as a clear instruction.
18
+
19
+ Respond with structured JSON-style fields:
20
+ intent: confirm | cancel | modify | new
21
+ modificationRequest: only for modify; concise instruction
22
+ reason: brief classification explanation`;
@@ -0,0 +1,20 @@
1
+ export const FEASIBILITY_CHECK_PROMPT = `You are evaluating whether a user's workflow request can be fulfilled with a restricted set of integrations.
2
+
3
+ Some integrations the user might need are NOT available on this platform. You must decide if the request is still feasible with what IS available.
4
+
5
+ ## Decision Rules
6
+
7
+ - If a removed integration is the PRIMARY data source or destination and no functional equivalent exists among available integrations → NOT feasible
8
+ Example: "Send Stripe payments via Gmail" with Stripe removed → NOT feasible (Gmail cannot provide payment data)
9
+
10
+ - If a removed integration has a functional equivalent among available integrations → feasible
11
+ Example: "Send an email weekly" with IMAP/SMTP removed but Gmail available → feasible (Gmail sends email)
12
+
13
+ - If the removed integration is explicitly named by the user as a specific service → likely NOT feasible (user specifically wants that service)
14
+ Example: "Connect Jira to Slack" with Jira removed → NOT feasible (user specifically asked for Jira)
15
+
16
+ - Utility nodes (Schedule, Webhook, Code, IF, Set) are always available and don't count as replacements for service integrations
17
+
18
+ Respond with structured JSON-style fields:
19
+ feasible: true | false
20
+ reason: brief explanation`;
@@ -0,0 +1,20 @@
1
+ export const FIELD_CORRECTION_SYSTEM_PROMPT = `Fix the workflows field reference to use a valid field path.
2
+
3
+ You will receive:
4
+ 1. A $json reference with an invalid field
5
+ 2. The available fields with their types from the source node's output schema
6
+
7
+ Pick the field that best matches the intent. Pay attention to types: if the expression expects text content, pick a string field, not an object or array.
8
+
9
+ Return ONLY the corrected $json reference. No explanation, no {{ }} wrapping.
10
+
11
+ Example:
12
+ - Expression: $json.sender
13
+ - Available: from.value[0].address (string), from.value[0].name (string), subject (string), id (string)
14
+ - Output: $json.from.value[0].address`;
15
+
16
+ export const FIELD_CORRECTION_USER_PROMPT = `Expression: {expression}
17
+ Available fields:
18
+ {availableFields}
19
+
20
+ Return the corrected expression:`;