@open-mercato/core 0.7.1-develop.7193.1.910a5b0a1e → 0.7.1-develop.7195.1.b5efd1d51a

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 (1241) hide show
  1. package/.turbo/turbo-build.log +2 -2
  2. package/AGENTS.md +4 -3
  3. package/agentic/standalone-guide.md +1 -1
  4. package/build.mjs +16 -0
  5. package/dist/bootstrap.js +7 -3
  6. package/dist/bootstrap.js.map +2 -2
  7. package/dist/generated/entities/action_log/index.js +2 -0
  8. package/dist/generated/entities/action_log/index.js.map +2 -2
  9. package/dist/generated/entities/user/index.js +2 -0
  10. package/dist/generated/entities/user/index.js.map +2 -2
  11. package/dist/generated/entities/user_task/index.js +14 -0
  12. package/dist/generated/entities/user_task/index.js.map +2 -2
  13. package/dist/generated/entities/workflow_definition/index.js +6 -0
  14. package/dist/generated/entities/workflow_definition/index.js.map +2 -2
  15. package/dist/generated/entities/workflow_definition_draft/index.js +25 -0
  16. package/dist/generated/entities/workflow_definition_draft/index.js.map +7 -0
  17. package/dist/generated/entities/workflow_definition_metric_rollup/index.js +23 -0
  18. package/dist/generated/entities/workflow_definition_metric_rollup/index.js.map +7 -0
  19. package/dist/generated/entities/workflow_instance/index.js +4 -0
  20. package/dist/generated/entities/workflow_instance/index.js.map +2 -2
  21. package/dist/generated/entities.ids.generated.js +4 -1
  22. package/dist/generated/entities.ids.generated.js.map +2 -2
  23. package/dist/generated/entity-fields-registry.js +39 -0
  24. package/dist/generated/entity-fields-registry.js.map +2 -2
  25. package/dist/helpers/integration/workflowsFixtures.js +477 -2
  26. package/dist/helpers/integration/workflowsFixtures.js.map +2 -2
  27. package/dist/helpers/integration/workflowsUi.js +148 -0
  28. package/dist/helpers/integration/workflowsUi.js.map +7 -0
  29. package/dist/modules/attachments/lib/createFromBuffer.js +79 -0
  30. package/dist/modules/attachments/lib/createFromBuffer.js.map +7 -0
  31. package/dist/modules/audit_logs/data/entities.js +5 -0
  32. package/dist/modules/audit_logs/data/entities.js.map +2 -2
  33. package/dist/modules/audit_logs/data/validators.js +1 -0
  34. package/dist/modules/audit_logs/data/validators.js.map +2 -2
  35. package/dist/modules/audit_logs/lib/projections.js +2 -1
  36. package/dist/modules/audit_logs/lib/projections.js.map +2 -2
  37. package/dist/modules/audit_logs/migrations/Migration20260625120000.js +15 -0
  38. package/dist/modules/audit_logs/migrations/Migration20260625120000.js.map +7 -0
  39. package/dist/modules/audit_logs/services/actionLogService.js +3 -0
  40. package/dist/modules/audit_logs/services/actionLogService.js.map +2 -2
  41. package/dist/modules/auth/data/entities.js +6 -0
  42. package/dist/modules/auth/data/entities.js.map +2 -2
  43. package/dist/modules/auth/lib/executionPrincipal.js +140 -0
  44. package/dist/modules/auth/lib/executionPrincipal.js.map +7 -0
  45. package/dist/modules/auth/migrations/Migration20260625050000.js +13 -0
  46. package/dist/modules/auth/migrations/Migration20260625050000.js.map +7 -0
  47. package/dist/modules/business_rules/components/InlineRuleEditor.js +154 -0
  48. package/dist/modules/business_rules/components/InlineRuleEditor.js.map +7 -0
  49. package/dist/modules/business_rules/data/validators.js +6 -0
  50. package/dist/modules/business_rules/data/validators.js.map +2 -2
  51. package/dist/modules/business_rules/lib/rule-engine.js +5 -3
  52. package/dist/modules/business_rules/lib/rule-engine.js.map +2 -2
  53. package/dist/modules/catalog/commands/products.js +5 -0
  54. package/dist/modules/catalog/commands/products.js.map +2 -2
  55. package/dist/modules/catalog/workflows.js +19 -0
  56. package/dist/modules/catalog/workflows.js.map +7 -0
  57. package/dist/modules/customer_accounts/backend/customer_accounts/roles/[id]/page.js +3 -1
  58. package/dist/modules/customer_accounts/backend/customer_accounts/roles/[id]/page.js.map +2 -2
  59. package/dist/modules/customer_accounts/cli.js +80 -0
  60. package/dist/modules/customer_accounts/cli.js.map +7 -0
  61. package/dist/modules/customer_accounts/lib/customerRoleAclSync.js +59 -0
  62. package/dist/modules/customer_accounts/lib/customerRoleAclSync.js.map +7 -0
  63. package/dist/modules/customer_accounts/setup.js +4 -2
  64. package/dist/modules/customer_accounts/setup.js.map +2 -2
  65. package/dist/modules/customers/commands/deals.js +4 -0
  66. package/dist/modules/customers/commands/deals.js.map +2 -2
  67. package/dist/modules/customers/components/detail/ChangelogEntryRow.js +1 -1
  68. package/dist/modules/customers/components/detail/ChangelogEntryRow.js.map +2 -2
  69. package/dist/modules/customers/components/detail/utils.js +2 -0
  70. package/dist/modules/customers/components/detail/utils.js.map +2 -2
  71. package/dist/modules/customers/events.js +13 -2
  72. package/dist/modules/customers/events.js.map +2 -2
  73. package/dist/modules/customers/lib/link-workflow-task-handler.js +64 -0
  74. package/dist/modules/customers/lib/link-workflow-task-handler.js.map +7 -0
  75. package/dist/modules/customers/lib/todoCompatibility.js +1 -1
  76. package/dist/modules/customers/lib/todoCompatibility.js.map +2 -2
  77. package/dist/modules/customers/lib/workflowTaskLink.js +9 -0
  78. package/dist/modules/customers/lib/workflowTaskLink.js.map +7 -0
  79. package/dist/modules/customers/subscribers/link-workflow-task.js +12 -0
  80. package/dist/modules/customers/subscribers/link-workflow-task.js.map +7 -0
  81. package/dist/modules/customers/workflows.js +29 -0
  82. package/dist/modules/customers/workflows.js.map +7 -0
  83. package/dist/modules/entities/api/records.js +13 -33
  84. package/dist/modules/entities/api/records.js.map +3 -3
  85. package/dist/modules/entities/lib/entityClassification.js +60 -0
  86. package/dist/modules/entities/lib/entityClassification.js.map +7 -0
  87. package/dist/modules/sales/commands/documents.js +31 -0
  88. package/dist/modules/sales/commands/documents.js.map +2 -2
  89. package/dist/modules/sales/events.js +12 -1
  90. package/dist/modules/sales/events.js.map +2 -2
  91. package/dist/modules/sales/workflows.js +12 -1
  92. package/dist/modules/sales/workflows.js.map +2 -2
  93. package/dist/modules/seeds/cli.js +199 -0
  94. package/dist/modules/seeds/cli.js.map +7 -0
  95. package/dist/modules/seeds/examples/sample-seed.json +26 -0
  96. package/dist/modules/seeds/index.js +12 -0
  97. package/dist/modules/seeds/index.js.map +7 -0
  98. package/dist/modules/workflows/acl.js +96 -0
  99. package/dist/modules/workflows/acl.js.map +2 -2
  100. package/dist/modules/workflows/ai-agents.js +50 -0
  101. package/dist/modules/workflows/ai-agents.js.map +7 -0
  102. package/dist/modules/workflows/ai-tools/authoring-pack.js +582 -0
  103. package/dist/modules/workflows/ai-tools/authoring-pack.js.map +7 -0
  104. package/dist/modules/workflows/ai-tools/types.js +43 -0
  105. package/dist/modules/workflows/ai-tools/types.js.map +7 -0
  106. package/dist/modules/workflows/ai-tools.js +10 -0
  107. package/dist/modules/workflows/ai-tools.js.map +7 -0
  108. package/dist/modules/workflows/api/command-settings/route.js +137 -0
  109. package/dist/modules/workflows/api/command-settings/route.js.map +7 -0
  110. package/dist/modules/workflows/api/commands/route.js +72 -0
  111. package/dist/modules/workflows/api/commands/route.js.map +7 -0
  112. package/dist/modules/workflows/api/definitions/[id]/callers/route.js +76 -0
  113. package/dist/modules/workflows/api/definitions/[id]/callers/route.js.map +7 -0
  114. package/dist/modules/workflows/api/definitions/[id]/context-schema/route.js +134 -0
  115. package/dist/modules/workflows/api/definitions/[id]/context-schema/route.js.map +7 -0
  116. package/dist/modules/workflows/api/definitions/[id]/customize/route.js +5 -1
  117. package/dist/modules/workflows/api/definitions/[id]/customize/route.js.map +2 -2
  118. package/dist/modules/workflows/api/definitions/[id]/draft/route.js +286 -0
  119. package/dist/modules/workflows/api/definitions/[id]/draft/route.js.map +7 -0
  120. package/dist/modules/workflows/api/definitions/[id]/publish/route.js +207 -0
  121. package/dist/modules/workflows/api/definitions/[id]/publish/route.js.map +7 -0
  122. package/dist/modules/workflows/api/definitions/[id]/reset-to-code/route.js +5 -0
  123. package/dist/modules/workflows/api/definitions/[id]/reset-to-code/route.js.map +2 -2
  124. package/dist/modules/workflows/api/definitions/[id]/route.js +93 -9
  125. package/dist/modules/workflows/api/definitions/[id]/route.js.map +3 -3
  126. package/dist/modules/workflows/api/definitions/[id]/test-step/route.js +210 -0
  127. package/dist/modules/workflows/api/definitions/[id]/test-step/route.js.map +7 -0
  128. package/dist/modules/workflows/api/definitions/generate/route.js +233 -0
  129. package/dist/modules/workflows/api/definitions/generate/route.js.map +7 -0
  130. package/dist/modules/workflows/api/definitions/route.js +61 -113
  131. package/dist/modules/workflows/api/definitions/route.js.map +2 -2
  132. package/dist/modules/workflows/api/definitions/serialize.js +9 -0
  133. package/dist/modules/workflows/api/definitions/serialize.js.map +2 -2
  134. package/dist/modules/workflows/api/endpoints/route.js +69 -0
  135. package/dist/modules/workflows/api/endpoints/route.js.map +7 -0
  136. package/dist/modules/workflows/api/functions/route.js +73 -0
  137. package/dist/modules/workflows/api/functions/route.js.map +7 -0
  138. package/dist/modules/workflows/api/grantable-features/route.js +75 -0
  139. package/dist/modules/workflows/api/grantable-features/route.js.map +7 -0
  140. package/dist/modules/workflows/api/instances/[id]/context/route.js +180 -0
  141. package/dist/modules/workflows/api/instances/[id]/context/route.js.map +7 -0
  142. package/dist/modules/workflows/api/instances/[id]/rerun-step/route.js +184 -0
  143. package/dist/modules/workflows/api/instances/[id]/rerun-step/route.js.map +7 -0
  144. package/dist/modules/workflows/api/instances/[id]/retry/route.js +12 -2
  145. package/dist/modules/workflows/api/instances/[id]/retry/route.js.map +2 -2
  146. package/dist/modules/workflows/api/instances/[id]/step-through/route.js +148 -0
  147. package/dist/modules/workflows/api/instances/[id]/step-through/route.js.map +7 -0
  148. package/dist/modules/workflows/api/instances/[id]/steps/route.js +123 -0
  149. package/dist/modules/workflows/api/instances/[id]/steps/route.js.map +7 -0
  150. package/dist/modules/workflows/api/instances/[id]/would-do/route.js +172 -0
  151. package/dist/modules/workflows/api/instances/[id]/would-do/route.js.map +7 -0
  152. package/dist/modules/workflows/api/instances/bulk-replay/route.js +117 -0
  153. package/dist/modules/workflows/api/instances/bulk-replay/route.js.map +7 -0
  154. package/dist/modules/workflows/api/instances/failure-queue/route.js +165 -0
  155. package/dist/modules/workflows/api/instances/failure-queue/route.js.map +7 -0
  156. package/dist/modules/workflows/api/instances/route.js +94 -3
  157. package/dist/modules/workflows/api/instances/route.js.map +2 -2
  158. package/dist/modules/workflows/api/metrics/definitions/route.js +188 -0
  159. package/dist/modules/workflows/api/metrics/definitions/route.js.map +7 -0
  160. package/dist/modules/workflows/api/openapi.js +415 -4
  161. package/dist/modules/workflows/api/openapi.js.map +2 -2
  162. package/dist/modules/workflows/api/portal/tasks/[id]/complete/route.js +132 -0
  163. package/dist/modules/workflows/api/portal/tasks/[id]/complete/route.js.map +7 -0
  164. package/dist/modules/workflows/api/portal/tasks/[id]/route.js +97 -0
  165. package/dist/modules/workflows/api/portal/tasks/[id]/route.js.map +7 -0
  166. package/dist/modules/workflows/api/portal/tasks/route.js +126 -0
  167. package/dist/modules/workflows/api/portal/tasks/route.js.map +7 -0
  168. package/dist/modules/workflows/api/rule-usage/route.js +80 -0
  169. package/dist/modules/workflows/api/rule-usage/route.js.map +7 -0
  170. package/dist/modules/workflows/api/task-settings/route.js +107 -0
  171. package/dist/modules/workflows/api/task-settings/route.js.map +7 -0
  172. package/dist/modules/workflows/api/tasks/[id]/claim/route.js +27 -16
  173. package/dist/modules/workflows/api/tasks/[id]/claim/route.js.map +2 -2
  174. package/dist/modules/workflows/api/tasks/[id]/complete/route.js +40 -16
  175. package/dist/modules/workflows/api/tasks/[id]/complete/route.js.map +2 -2
  176. package/dist/modules/workflows/api/tasks/[id]/reassign/route.js +150 -0
  177. package/dist/modules/workflows/api/tasks/[id]/reassign/route.js.map +7 -0
  178. package/dist/modules/workflows/api/tasks/[id]/route.js +39 -6
  179. package/dist/modules/workflows/api/tasks/[id]/route.js.map +2 -2
  180. package/dist/modules/workflows/api/tasks/[id]/unclaim/route.js +100 -0
  181. package/dist/modules/workflows/api/tasks/[id]/unclaim/route.js.map +7 -0
  182. package/dist/modules/workflows/api/tasks/route.js +37 -6
  183. package/dist/modules/workflows/api/tasks/route.js.map +2 -2
  184. package/dist/modules/workflows/api/tasks/serialize.js +83 -0
  185. package/dist/modules/workflows/api/tasks/serialize.js.map +7 -0
  186. package/dist/modules/workflows/api/templates/route.js +69 -0
  187. package/dist/modules/workflows/api/templates/route.js.map +7 -0
  188. package/dist/modules/workflows/api/work-inbox/next/route.js +129 -0
  189. package/dist/modules/workflows/api/work-inbox/next/route.js.map +7 -0
  190. package/dist/modules/workflows/api/work-inbox/route.js +119 -0
  191. package/dist/modules/workflows/api/work-inbox/route.js.map +7 -0
  192. package/dist/modules/workflows/backend/config/workflows/page.js +10 -0
  193. package/dist/modules/workflows/backend/config/workflows/page.js.map +7 -0
  194. package/dist/modules/workflows/backend/config/workflows/page.meta.js +35 -0
  195. package/dist/modules/workflows/backend/config/workflows/page.meta.js.map +7 -0
  196. package/dist/modules/workflows/backend/definitions/[id]/page.js +6 -271
  197. package/dist/modules/workflows/backend/definitions/[id]/page.js.map +3 -3
  198. package/dist/modules/workflows/backend/definitions/create/page.js +7 -56
  199. package/dist/modules/workflows/backend/definitions/create/page.js.map +2 -2
  200. package/dist/modules/workflows/backend/definitions/create/page.meta.js +1 -1
  201. package/dist/modules/workflows/backend/definitions/create/page.meta.js.map +2 -2
  202. package/dist/modules/workflows/backend/definitions/page.js +131 -51
  203. package/dist/modules/workflows/backend/definitions/page.js.map +2 -2
  204. package/dist/modules/workflows/backend/definitions/visual-editor/page.js +2606 -677
  205. package/dist/modules/workflows/backend/definitions/visual-editor/page.js.map +3 -3
  206. package/dist/modules/workflows/backend/instances/[id]/page.js +532 -421
  207. package/dist/modules/workflows/backend/instances/[id]/page.js.map +2 -2
  208. package/dist/modules/workflows/backend/instances/[id]/page.meta.js +1 -1
  209. package/dist/modules/workflows/backend/instances/[id]/page.meta.js.map +1 -1
  210. package/dist/modules/workflows/backend/instances/failure-queue/page.js +211 -0
  211. package/dist/modules/workflows/backend/instances/failure-queue/page.js.map +7 -0
  212. package/dist/modules/workflows/backend/instances/failure-queue/page.meta.js +20 -0
  213. package/dist/modules/workflows/backend/instances/failure-queue/page.meta.js.map +7 -0
  214. package/dist/modules/workflows/backend/instances/page.js +159 -22
  215. package/dist/modules/workflows/backend/instances/page.js.map +2 -2
  216. package/dist/modules/workflows/backend/instances/page.meta.js +1 -1
  217. package/dist/modules/workflows/backend/instances/page.meta.js.map +1 -1
  218. package/dist/modules/workflows/backend/tasks/[id]/page.js +553 -392
  219. package/dist/modules/workflows/backend/tasks/[id]/page.js.map +3 -3
  220. package/dist/modules/workflows/backend/tasks/page.js +8 -250
  221. package/dist/modules/workflows/backend/tasks/page.js.map +2 -2
  222. package/dist/modules/workflows/backend/tasks/page.meta.js +1 -0
  223. package/dist/modules/workflows/backend/tasks/page.meta.js.map +2 -2
  224. package/dist/modules/workflows/backend/work-inbox/page.js +399 -0
  225. package/dist/modules/workflows/backend/work-inbox/page.js.map +7 -0
  226. package/dist/modules/workflows/backend/work-inbox/page.meta.js +19 -0
  227. package/dist/modules/workflows/backend/work-inbox/page.meta.js.map +7 -0
  228. package/dist/modules/workflows/cli.js +1 -1
  229. package/dist/modules/workflows/cli.js.map +2 -2
  230. package/dist/modules/workflows/commands/tasks.js +81 -0
  231. package/dist/modules/workflows/commands/tasks.js.map +7 -0
  232. package/dist/modules/workflows/components/ActivitiesEditor.js +281 -287
  233. package/dist/modules/workflows/components/ActivitiesEditor.js.map +2 -2
  234. package/dist/modules/workflows/components/ActivityEditDialog.js +220 -0
  235. package/dist/modules/workflows/components/ActivityEditDialog.js.map +7 -0
  236. package/dist/modules/workflows/components/AgentSelector.js +115 -0
  237. package/dist/modules/workflows/components/AgentSelector.js.map +7 -0
  238. package/dist/modules/workflows/components/AnnotationEditDialog.js +110 -0
  239. package/dist/modules/workflows/components/AnnotationEditDialog.js.map +7 -0
  240. package/dist/modules/workflows/components/BusinessRuleUsagePanel.js +71 -0
  241. package/dist/modules/workflows/components/BusinessRuleUsagePanel.js.map +7 -0
  242. package/dist/modules/workflows/components/BusinessRulesSelector.js +151 -93
  243. package/dist/modules/workflows/components/BusinessRulesSelector.js.map +2 -2
  244. package/dist/modules/workflows/components/ConfigJsonTextarea.js +85 -0
  245. package/dist/modules/workflows/components/ConfigJsonTextarea.js.map +7 -0
  246. package/dist/modules/workflows/components/ContextSchemaEditor.js +150 -0
  247. package/dist/modules/workflows/components/ContextSchemaEditor.js.map +7 -0
  248. package/dist/modules/workflows/components/DefinitionErrorHandlerField.js +102 -0
  249. package/dist/modules/workflows/components/DefinitionErrorHandlerField.js.map +7 -0
  250. package/dist/modules/workflows/components/DefinitionMetadataDrawer.js +377 -0
  251. package/dist/modules/workflows/components/DefinitionMetadataDrawer.js.map +7 -0
  252. package/dist/modules/workflows/components/DefinitionMetricsCells.js +132 -0
  253. package/dist/modules/workflows/components/DefinitionMetricsCells.js.map +7 -0
  254. package/dist/modules/workflows/components/DefinitionTriggersEditor.js +307 -216
  255. package/dist/modules/workflows/components/DefinitionTriggersEditor.js.map +2 -2
  256. package/dist/modules/workflows/components/EdgeEditDialog.js +11 -18
  257. package/dist/modules/workflows/components/EdgeEditDialog.js.map +2 -2
  258. package/dist/modules/workflows/components/EdgeEditDialogCrudForm.js +112 -72
  259. package/dist/modules/workflows/components/EdgeEditDialogCrudForm.js.map +2 -2
  260. package/dist/modules/workflows/components/InputDataPanel.js +128 -0
  261. package/dist/modules/workflows/components/InputDataPanel.js.map +7 -0
  262. package/dist/modules/workflows/components/InspectorPanel.js +161 -0
  263. package/dist/modules/workflows/components/InspectorPanel.js.map +7 -0
  264. package/dist/modules/workflows/components/NodeEditDialog.js +363 -41
  265. package/dist/modules/workflows/components/NodeEditDialog.js.map +2 -2
  266. package/dist/modules/workflows/components/NodeEditDialogCrudForm.js +714 -159
  267. package/dist/modules/workflows/components/NodeEditDialogCrudForm.js.map +2 -2
  268. package/dist/modules/workflows/components/StepsEditor.js.map +2 -2
  269. package/dist/modules/workflows/components/TemplateGalleryDialog.js +148 -0
  270. package/dist/modules/workflows/components/TemplateGalleryDialog.js.map +7 -0
  271. package/dist/modules/workflows/components/TransitionsEditor.js +80 -74
  272. package/dist/modules/workflows/components/TransitionsEditor.js.map +2 -2
  273. package/dist/modules/workflows/components/TriggersDialog.js +53 -0
  274. package/dist/modules/workflows/components/TriggersDialog.js.map +7 -0
  275. package/dist/modules/workflows/components/TriggersEditor.js +331 -0
  276. package/dist/modules/workflows/components/TriggersEditor.js.map +7 -0
  277. package/dist/modules/workflows/components/WorkflowAiDraftDialog.js +278 -0
  278. package/dist/modules/workflows/components/WorkflowAiDraftDialog.js.map +7 -0
  279. package/dist/modules/workflows/components/WorkflowCodeView.js +173 -0
  280. package/dist/modules/workflows/components/WorkflowCodeView.js.map +7 -0
  281. package/dist/modules/workflows/components/WorkflowCommandPalette.js +62 -0
  282. package/dist/modules/workflows/components/WorkflowCommandPalette.js.map +7 -0
  283. package/dist/modules/workflows/components/WorkflowCompensationGhostEdge.js +70 -0
  284. package/dist/modules/workflows/components/WorkflowCompensationGhostEdge.js.map +7 -0
  285. package/dist/modules/workflows/components/WorkflowDataMappingEdge.js +36 -0
  286. package/dist/modules/workflows/components/WorkflowDataMappingEdge.js.map +7 -0
  287. package/dist/modules/workflows/components/WorkflowGraph.js +4 -2
  288. package/dist/modules/workflows/components/WorkflowGraph.js.map +2 -2
  289. package/dist/modules/workflows/components/WorkflowGraphImpl.js +324 -86
  290. package/dist/modules/workflows/components/WorkflowGraphImpl.js.map +3 -3
  291. package/dist/modules/workflows/components/WorkflowIconPicker.js +109 -0
  292. package/dist/modules/workflows/components/WorkflowIconPicker.js.map +7 -0
  293. package/dist/modules/workflows/components/WorkflowLegend.js +45 -29
  294. package/dist/modules/workflows/components/WorkflowLegend.js.map +2 -2
  295. package/dist/modules/workflows/components/WorkflowNodeCard.js +221 -22
  296. package/dist/modules/workflows/components/WorkflowNodeCard.js.map +2 -2
  297. package/dist/modules/workflows/components/WorkflowRouteChips.js +111 -0
  298. package/dist/modules/workflows/components/WorkflowRouteChips.js.map +7 -0
  299. package/dist/modules/workflows/components/WorkflowSelector.js +108 -88
  300. package/dist/modules/workflows/components/WorkflowSelector.js.map +2 -2
  301. package/dist/modules/workflows/components/WorkflowTransitionEdge.js +61 -11
  302. package/dist/modules/workflows/components/WorkflowTransitionEdge.js.map +2 -2
  303. package/dist/modules/workflows/components/WorkflowTransitionLabel.js +15 -5
  304. package/dist/modules/workflows/components/WorkflowTransitionLabel.js.map +2 -2
  305. package/dist/modules/workflows/components/fields/ActivityArrayEditor.js +88 -21
  306. package/dist/modules/workflows/components/fields/ActivityArrayEditor.js.map +2 -2
  307. package/dist/modules/workflows/components/fields/ActivityConfigFields.js +615 -0
  308. package/dist/modules/workflows/components/fields/ActivityConfigFields.js.map +7 -0
  309. package/dist/modules/workflows/components/fields/ActivityTestPanel.js +191 -0
  310. package/dist/modules/workflows/components/fields/ActivityTestPanel.js.map +7 -0
  311. package/dist/modules/workflows/components/fields/AgentInvokeConfigField.js +488 -0
  312. package/dist/modules/workflows/components/fields/AgentInvokeConfigField.js.map +7 -0
  313. package/dist/modules/workflows/components/fields/AgentReviewOnBreachField.js +61 -0
  314. package/dist/modules/workflows/components/fields/AgentReviewOnBreachField.js.map +7 -0
  315. package/dist/modules/workflows/components/fields/BranchingRoutesEditor.js +227 -0
  316. package/dist/modules/workflows/components/fields/BranchingRoutesEditor.js.map +7 -0
  317. package/dist/modules/workflows/components/fields/BusinessRuleConditionsEditor.js +37 -4
  318. package/dist/modules/workflows/components/fields/BusinessRuleConditionsEditor.js.map +2 -2
  319. package/dist/modules/workflows/components/fields/CommandPicker.js +111 -0
  320. package/dist/modules/workflows/components/fields/CommandPicker.js.map +7 -0
  321. package/dist/modules/workflows/components/fields/EndpointPicker.js +235 -0
  322. package/dist/modules/workflows/components/fields/EndpointPicker.js.map +7 -0
  323. package/dist/modules/workflows/components/fields/FormFieldArrayEditor.js +2 -2
  324. package/dist/modules/workflows/components/fields/FormFieldArrayEditor.js.map +2 -2
  325. package/dist/modules/workflows/components/fields/FunctionPicker.js +55 -0
  326. package/dist/modules/workflows/components/fields/FunctionPicker.js.map +7 -0
  327. package/dist/modules/workflows/components/fields/MappingArrayEditor.js +38 -15
  328. package/dist/modules/workflows/components/fields/MappingArrayEditor.js.map +2 -2
  329. package/dist/modules/workflows/components/fields/PortFieldArrayEditor.js +206 -0
  330. package/dist/modules/workflows/components/fields/PortFieldArrayEditor.js.map +7 -0
  331. package/dist/modules/workflows/components/fields/RolesMultiSelect.js +53 -0
  332. package/dist/modules/workflows/components/fields/RolesMultiSelect.js.map +7 -0
  333. package/dist/modules/workflows/components/fields/RouteOrderEditor.js +130 -0
  334. package/dist/modules/workflows/components/fields/RouteOrderEditor.js.map +7 -0
  335. package/dist/modules/workflows/components/fields/SchemaBuilderDialog.js +104 -0
  336. package/dist/modules/workflows/components/fields/SchemaBuilderDialog.js.map +7 -0
  337. package/dist/modules/workflows/components/fields/StartPreConditionsEditor.js +2 -2
  338. package/dist/modules/workflows/components/fields/StartPreConditionsEditor.js.map +2 -2
  339. package/dist/modules/workflows/components/fields/TaskAssignmentField.js +210 -0
  340. package/dist/modules/workflows/components/fields/TaskAssignmentField.js.map +7 -0
  341. package/dist/modules/workflows/components/fields/TaskDeadlineFields.js +126 -0
  342. package/dist/modules/workflows/components/fields/TaskDeadlineFields.js.map +7 -0
  343. package/dist/modules/workflows/components/fields/TaskDecisionsField.js +218 -0
  344. package/dist/modules/workflows/components/fields/TaskDecisionsField.js.map +7 -0
  345. package/dist/modules/workflows/components/fields/TaskEntityBindingsField.js +114 -0
  346. package/dist/modules/workflows/components/fields/TaskEntityBindingsField.js.map +7 -0
  347. package/dist/modules/workflows/components/fields/TemplateTextControl.js +64 -0
  348. package/dist/modules/workflows/components/fields/TemplateTextControl.js.map +7 -0
  349. package/dist/modules/workflows/components/fields/VariablePickerButton.js +121 -0
  350. package/dist/modules/workflows/components/fields/VariablePickerButton.js.map +7 -0
  351. package/dist/modules/workflows/components/fields/ledgerDropTarget.js +35 -0
  352. package/dist/modules/workflows/components/fields/ledgerDropTarget.js.map +7 -0
  353. package/dist/modules/workflows/components/fields/useActivityTypeOptions.js +16 -0
  354. package/dist/modules/workflows/components/fields/useActivityTypeOptions.js.map +7 -0
  355. package/dist/modules/workflows/components/formConfig.js +39 -10
  356. package/dist/modules/workflows/components/formConfig.js.map +2 -2
  357. package/dist/modules/workflows/components/mobile/MobileDefinitionDetail.js.map +2 -2
  358. package/dist/modules/workflows/components/mobile/MobileMetadataSheet.js.map +2 -2
  359. package/dist/modules/workflows/components/mobile/MobileTaskForm.js +29 -2
  360. package/dist/modules/workflows/components/mobile/MobileTaskForm.js.map +2 -2
  361. package/dist/modules/workflows/components/mobile/MobileVisualEditor.js +64 -65
  362. package/dist/modules/workflows/components/mobile/MobileVisualEditor.js.map +2 -2
  363. package/dist/modules/workflows/components/nodes/AnnotationGroupNode.js +64 -0
  364. package/dist/modules/workflows/components/nodes/AnnotationGroupNode.js.map +7 -0
  365. package/dist/modules/workflows/components/nodes/AutomatedNode.js +24 -16
  366. package/dist/modules/workflows/components/nodes/AutomatedNode.js.map +2 -2
  367. package/dist/modules/workflows/components/nodes/EndNode.js +13 -12
  368. package/dist/modules/workflows/components/nodes/EndNode.js.map +2 -2
  369. package/dist/modules/workflows/components/nodes/ErrorOutputHandle.js +29 -0
  370. package/dist/modules/workflows/components/nodes/ErrorOutputHandle.js.map +7 -0
  371. package/dist/modules/workflows/components/nodes/IfElseNode.js +51 -0
  372. package/dist/modules/workflows/components/nodes/IfElseNode.js.map +7 -0
  373. package/dist/modules/workflows/components/nodes/InvokeAgentNode.js +122 -0
  374. package/dist/modules/workflows/components/nodes/InvokeAgentNode.js.map +7 -0
  375. package/dist/modules/workflows/components/nodes/NodeOutcomeRows.js +100 -0
  376. package/dist/modules/workflows/components/nodes/NodeOutcomeRows.js.map +7 -0
  377. package/dist/modules/workflows/components/nodes/ParallelForkNode.js +18 -14
  378. package/dist/modules/workflows/components/nodes/ParallelForkNode.js.map +2 -2
  379. package/dist/modules/workflows/components/nodes/ParallelJoinNode.js +18 -14
  380. package/dist/modules/workflows/components/nodes/ParallelJoinNode.js.map +2 -2
  381. package/dist/modules/workflows/components/nodes/StartNode.js +66 -15
  382. package/dist/modules/workflows/components/nodes/StartNode.js.map +2 -2
  383. package/dist/modules/workflows/components/nodes/StickyNoteNode.js +54 -0
  384. package/dist/modules/workflows/components/nodes/StickyNoteNode.js.map +7 -0
  385. package/dist/modules/workflows/components/nodes/SubWorkflowNode.js +107 -41
  386. package/dist/modules/workflows/components/nodes/SubWorkflowNode.js.map +2 -2
  387. package/dist/modules/workflows/components/nodes/SwitchNode.js +51 -0
  388. package/dist/modules/workflows/components/nodes/SwitchNode.js.map +7 -0
  389. package/dist/modules/workflows/components/nodes/TriggerCap.js +65 -0
  390. package/dist/modules/workflows/components/nodes/TriggerCap.js.map +7 -0
  391. package/dist/modules/workflows/components/nodes/TriggerNode.js +111 -0
  392. package/dist/modules/workflows/components/nodes/TriggerNode.js.map +7 -0
  393. package/dist/modules/workflows/components/nodes/UserTaskNode.js +43 -17
  394. package/dist/modules/workflows/components/nodes/UserTaskNode.js.map +2 -2
  395. package/dist/modules/workflows/components/nodes/WaitForConditionNode.js +53 -0
  396. package/dist/modules/workflows/components/nodes/WaitForConditionNode.js.map +7 -0
  397. package/dist/modules/workflows/components/nodes/WaitForSignalNode.js +21 -15
  398. package/dist/modules/workflows/components/nodes/WaitForSignalNode.js.map +2 -2
  399. package/dist/modules/workflows/components/nodes/WaitForTimerNode.js +21 -15
  400. package/dist/modules/workflows/components/nodes/WaitForTimerNode.js.map +2 -2
  401. package/dist/modules/workflows/components/nodes/index.js +14 -0
  402. package/dist/modules/workflows/components/nodes/index.js.map +2 -2
  403. package/dist/modules/workflows/components/portal/PortalTaskForm.js +196 -0
  404. package/dist/modules/workflows/components/portal/PortalTaskForm.js.map +7 -0
  405. package/dist/modules/workflows/components/run/AgentActivityPanel.js +22 -0
  406. package/dist/modules/workflows/components/run/AgentActivityPanel.js.map +7 -0
  407. package/dist/modules/workflows/components/run/RerunStepDialog.js +106 -0
  408. package/dist/modules/workflows/components/run/RerunStepDialog.js.map +7 -0
  409. package/dist/modules/workflows/components/run/RunEventBadge.js +30 -0
  410. package/dist/modules/workflows/components/run/RunEventBadge.js.map +7 -0
  411. package/dist/modules/workflows/components/run/RunGantt.js +130 -0
  412. package/dist/modules/workflows/components/run/RunGantt.js.map +7 -0
  413. package/dist/modules/workflows/components/run/RunStatusBadge.js +82 -0
  414. package/dist/modules/workflows/components/run/RunStatusBadge.js.map +7 -0
  415. package/dist/modules/workflows/components/run/RunStepInspector.js +116 -0
  416. package/dist/modules/workflows/components/run/RunStepInspector.js.map +7 -0
  417. package/dist/modules/workflows/components/run/WouldDoReport.js +52 -0
  418. package/dist/modules/workflows/components/run/WouldDoReport.js.map +7 -0
  419. package/dist/modules/workflows/components/run/useAgentLabels.js +28 -0
  420. package/dist/modules/workflows/components/run/useAgentLabels.js.map +7 -0
  421. package/dist/modules/workflows/components/run/useLastRunOverlay.js +89 -0
  422. package/dist/modules/workflows/components/run/useLastRunOverlay.js.map +7 -0
  423. package/dist/modules/workflows/components/run/useLiveAgentActions.js +41 -0
  424. package/dist/modules/workflows/components/run/useLiveAgentActions.js.map +7 -0
  425. package/dist/modules/workflows/components/run/useLiveRunUpdates.js +57 -0
  426. package/dist/modules/workflows/components/run/useLiveRunUpdates.js.map +7 -0
  427. package/dist/modules/workflows/components/settings/CommandSettingsPageClient.js +156 -0
  428. package/dist/modules/workflows/components/settings/CommandSettingsPageClient.js.map +7 -0
  429. package/dist/modules/workflows/components/work-inbox/EntityContextPanel.js +72 -0
  430. package/dist/modules/workflows/components/work-inbox/EntityContextPanel.js.map +7 -0
  431. package/dist/modules/workflows/components/work-inbox/ReassignTaskDialog.js +156 -0
  432. package/dist/modules/workflows/components/work-inbox/ReassignTaskDialog.js.map +7 -0
  433. package/dist/modules/workflows/components/work-inbox/StatusChip.js +46 -0
  434. package/dist/modules/workflows/components/work-inbox/StatusChip.js.map +7 -0
  435. package/dist/modules/workflows/components/work-inbox/TaskActionNotice.js +53 -0
  436. package/dist/modules/workflows/components/work-inbox/TaskActionNotice.js.map +7 -0
  437. package/dist/modules/workflows/components/work-inbox/TaskFormFields.js +261 -0
  438. package/dist/modules/workflows/components/work-inbox/TaskFormFields.js.map +7 -0
  439. package/dist/modules/workflows/components/work-inbox/WorkInboxEmptyState.js +27 -0
  440. package/dist/modules/workflows/components/work-inbox/WorkInboxEmptyState.js.map +7 -0
  441. package/dist/modules/workflows/data/activity-config-schemas.js +191 -0
  442. package/dist/modules/workflows/data/activity-config-schemas.js.map +7 -0
  443. package/dist/modules/workflows/data/activity-config-warnings.js +42 -0
  444. package/dist/modules/workflows/data/activity-config-warnings.js.map +7 -0
  445. package/dist/modules/workflows/data/branching-route-warnings.js +65 -0
  446. package/dist/modules/workflows/data/branching-route-warnings.js.map +7 -0
  447. package/dist/modules/workflows/data/entities.js +144 -4
  448. package/dist/modules/workflows/data/entities.js.map +2 -2
  449. package/dist/modules/workflows/data/metrics-validators.js +35 -0
  450. package/dist/modules/workflows/data/metrics-validators.js.map +7 -0
  451. package/dist/modules/workflows/data/task-primitives.js +14 -0
  452. package/dist/modules/workflows/data/task-primitives.js.map +7 -0
  453. package/dist/modules/workflows/data/validators.js +504 -61
  454. package/dist/modules/workflows/data/validators.js.map +3 -3
  455. package/dist/modules/workflows/di.js +16 -1
  456. package/dist/modules/workflows/di.js.map +2 -2
  457. package/dist/modules/workflows/events.js +66 -8
  458. package/dist/modules/workflows/events.js.map +2 -2
  459. package/dist/modules/workflows/examples/oze-residential-install-60-nodes.json +1545 -0
  460. package/dist/modules/workflows/examples/sales-pipeline-definition.json +35 -35
  461. package/dist/modules/workflows/examples/templates/lead-to-install.json +160 -0
  462. package/dist/modules/workflows/examples/templates/order-approval.json +230 -0
  463. package/dist/modules/workflows/examples/templates/task-escalation.json +110 -0
  464. package/dist/modules/workflows/examples/templates/webhook-integration.json +113 -0
  465. package/dist/modules/workflows/extension-points.js +1 -1
  466. package/dist/modules/workflows/extension-points.js.map +2 -2
  467. package/dist/modules/workflows/frontend/[orgSlug]/portal/tasks/[id]/page.js +144 -0
  468. package/dist/modules/workflows/frontend/[orgSlug]/portal/tasks/[id]/page.js.map +7 -0
  469. package/dist/modules/workflows/frontend/[orgSlug]/portal/tasks/[id]/page.meta.js +12 -0
  470. package/dist/modules/workflows/frontend/[orgSlug]/portal/tasks/[id]/page.meta.js.map +7 -0
  471. package/dist/modules/workflows/frontend/[orgSlug]/portal/tasks/page.js +114 -0
  472. package/dist/modules/workflows/frontend/[orgSlug]/portal/tasks/page.js.map +7 -0
  473. package/dist/modules/workflows/frontend/[orgSlug]/portal/tasks/page.meta.js +18 -0
  474. package/dist/modules/workflows/frontend/[orgSlug]/portal/tasks/page.meta.js.map +7 -0
  475. package/dist/modules/workflows/lib/activity-executor.js +505 -137
  476. package/dist/modules/workflows/lib/activity-executor.js.map +3 -3
  477. package/dist/modules/workflows/lib/activity-queue-types.js +3 -1
  478. package/dist/modules/workflows/lib/activity-queue-types.js.map +2 -2
  479. package/dist/modules/workflows/lib/activity-registry-bootstrap.js +2 -0
  480. package/dist/modules/workflows/lib/activity-registry-bootstrap.js.map +7 -0
  481. package/dist/modules/workflows/lib/activity-registry.js +27 -0
  482. package/dist/modules/workflows/lib/activity-registry.js.map +7 -0
  483. package/dist/modules/workflows/lib/activity-types.js +291 -0
  484. package/dist/modules/workflows/lib/activity-types.js.map +7 -0
  485. package/dist/modules/workflows/lib/activity-worker-handler.js +456 -32
  486. package/dist/modules/workflows/lib/activity-worker-handler.js.map +3 -3
  487. package/dist/modules/workflows/lib/advanced-config.js +31 -0
  488. package/dist/modules/workflows/lib/advanced-config.js.map +7 -0
  489. package/dist/modules/workflows/lib/agent-disposition-task.js +198 -0
  490. package/dist/modules/workflows/lib/agent-disposition-task.js.map +7 -0
  491. package/dist/modules/workflows/lib/agent-outcome-paths.js +82 -0
  492. package/dist/modules/workflows/lib/agent-outcome-paths.js.map +7 -0
  493. package/dist/modules/workflows/lib/agent-result-mapping.js +54 -0
  494. package/dist/modules/workflows/lib/agent-result-mapping.js.map +7 -0
  495. package/dist/modules/workflows/lib/agent-review-inspector.js +61 -0
  496. package/dist/modules/workflows/lib/agent-review-inspector.js.map +7 -0
  497. package/dist/modules/workflows/lib/agent-review.js +66 -0
  498. package/dist/modules/workflows/lib/agent-review.js.map +7 -0
  499. package/dist/modules/workflows/lib/ai-authoring.js +155 -0
  500. package/dist/modules/workflows/lib/ai-authoring.js.map +7 -0
  501. package/dist/modules/workflows/lib/ai-draft-runner.js +43 -0
  502. package/dist/modules/workflows/lib/ai-draft-runner.js.map +7 -0
  503. package/dist/modules/workflows/lib/annotation-events.js +10 -0
  504. package/dist/modules/workflows/lib/annotation-events.js.map +7 -0
  505. package/dist/modules/workflows/lib/branching-routes.js +182 -0
  506. package/dist/modules/workflows/lib/branching-routes.js.map +7 -0
  507. package/dist/modules/workflows/lib/breach-routing.js +50 -0
  508. package/dist/modules/workflows/lib/breach-routing.js.map +7 -0
  509. package/dist/modules/workflows/lib/bulk-replay.js +101 -0
  510. package/dist/modules/workflows/lib/bulk-replay.js.map +7 -0
  511. package/dist/modules/workflows/lib/business-rule-usage.js +91 -0
  512. package/dist/modules/workflows/lib/business-rule-usage.js.map +7 -0
  513. package/dist/modules/workflows/lib/caller-graph.js +53 -0
  514. package/dist/modules/workflows/lib/caller-graph.js.map +7 -0
  515. package/dist/modules/workflows/lib/code-view-apply.js +86 -0
  516. package/dist/modules/workflows/lib/code-view-apply.js.map +7 -0
  517. package/dist/modules/workflows/lib/collect-validation-issues.js +164 -0
  518. package/dist/modules/workflows/lib/collect-validation-issues.js.map +7 -0
  519. package/dist/modules/workflows/lib/compensation-ghosts.js +72 -0
  520. package/dist/modules/workflows/lib/compensation-ghosts.js.map +7 -0
  521. package/dist/modules/workflows/lib/component-guard.js +7 -0
  522. package/dist/modules/workflows/lib/component-guard.js.map +7 -0
  523. package/dist/modules/workflows/lib/condition-handler.js +443 -0
  524. package/dist/modules/workflows/lib/condition-handler.js.map +7 -0
  525. package/dist/modules/workflows/lib/context-ledger.js +448 -0
  526. package/dist/modules/workflows/lib/context-ledger.js.map +7 -0
  527. package/dist/modules/workflows/lib/crud-form-dialogs-flag.js +8 -0
  528. package/dist/modules/workflows/lib/crud-form-dialogs-flag.js.map +7 -0
  529. package/dist/modules/workflows/lib/data-edge-mapping.js +63 -0
  530. package/dist/modules/workflows/lib/data-edge-mapping.js.map +7 -0
  531. package/dist/modules/workflows/lib/definition-context-schema.js +56 -0
  532. package/dist/modules/workflows/lib/definition-context-schema.js.map +7 -0
  533. package/dist/modules/workflows/lib/definition-edit-safety.js +123 -0
  534. package/dist/modules/workflows/lib/definition-edit-safety.js.map +7 -0
  535. package/dist/modules/workflows/lib/definition-error-body.js +33 -0
  536. package/dist/modules/workflows/lib/definition-error-body.js.map +7 -0
  537. package/dist/modules/workflows/lib/definition-evaluation.js +79 -0
  538. package/dist/modules/workflows/lib/definition-evaluation.js.map +7 -0
  539. package/dist/modules/workflows/lib/definition-grant.js +90 -0
  540. package/dist/modules/workflows/lib/definition-grant.js.map +7 -0
  541. package/dist/modules/workflows/lib/definition-json-locations.js +139 -0
  542. package/dist/modules/workflows/lib/definition-json-locations.js.map +7 -0
  543. package/dist/modules/workflows/lib/definition-payload.js +31 -0
  544. package/dist/modules/workflows/lib/definition-payload.js.map +7 -0
  545. package/dist/modules/workflows/lib/draft-restore.js +49 -0
  546. package/dist/modules/workflows/lib/draft-restore.js.map +7 -0
  547. package/dist/modules/workflows/lib/dry-run.js +106 -0
  548. package/dist/modules/workflows/lib/dry-run.js.map +7 -0
  549. package/dist/modules/workflows/lib/duration.js +66 -17
  550. package/dist/modules/workflows/lib/duration.js.map +2 -2
  551. package/dist/modules/workflows/lib/edge-reattachment.js +79 -0
  552. package/dist/modules/workflows/lib/edge-reattachment.js.map +7 -0
  553. package/dist/modules/workflows/lib/edgeFormTransforms.js +7 -11
  554. package/dist/modules/workflows/lib/edgeFormTransforms.js.map +2 -2
  555. package/dist/modules/workflows/lib/editor-annotations.js +195 -0
  556. package/dist/modules/workflows/lib/editor-annotations.js.map +7 -0
  557. package/dist/modules/workflows/lib/editor-commands.js +103 -0
  558. package/dist/modules/workflows/lib/editor-commands.js.map +7 -0
  559. package/dist/modules/workflows/lib/editor-history.js +51 -0
  560. package/dist/modules/workflows/lib/editor-history.js.map +7 -0
  561. package/dist/modules/workflows/lib/endpoint-catalog.js +181 -0
  562. package/dist/modules/workflows/lib/endpoint-catalog.js.map +7 -0
  563. package/dist/modules/workflows/lib/endpoint-path.js +90 -0
  564. package/dist/modules/workflows/lib/endpoint-path.js.map +7 -0
  565. package/dist/modules/workflows/lib/engine-version.js +42 -0
  566. package/dist/modules/workflows/lib/engine-version.js.map +7 -0
  567. package/dist/modules/workflows/lib/error-handler.js +132 -0
  568. package/dist/modules/workflows/lib/error-handler.js.map +7 -0
  569. package/dist/modules/workflows/lib/error-routing.js +104 -0
  570. package/dist/modules/workflows/lib/error-routing.js.map +7 -0
  571. package/dist/modules/workflows/lib/event-logger.js +23 -0
  572. package/dist/modules/workflows/lib/event-logger.js.map +2 -2
  573. package/dist/modules/workflows/lib/expression-refs.js +168 -0
  574. package/dist/modules/workflows/lib/expression-refs.js.map +7 -0
  575. package/dist/modules/workflows/lib/failure-grouping.js +62 -0
  576. package/dist/modules/workflows/lib/failure-grouping.js.map +7 -0
  577. package/dist/modules/workflows/lib/find-definition.js +1 -0
  578. package/dist/modules/workflows/lib/find-definition.js.map +2 -2
  579. package/dist/modules/workflows/lib/flow-logic-warnings.js +338 -0
  580. package/dist/modules/workflows/lib/flow-logic-warnings.js.map +7 -0
  581. package/dist/modules/workflows/lib/graph-utils.js +328 -77
  582. package/dist/modules/workflows/lib/graph-utils.js.map +2 -2
  583. package/dist/modules/workflows/lib/icon-search.js +32 -0
  584. package/dist/modules/workflows/lib/icon-search.js.map +7 -0
  585. package/dist/modules/workflows/lib/insert-at-cursor.js +26 -0
  586. package/dist/modules/workflows/lib/insert-at-cursor.js.map +7 -0
  587. package/dist/modules/workflows/lib/instance-date-filter.js +28 -0
  588. package/dist/modules/workflows/lib/instance-date-filter.js.map +7 -0
  589. package/dist/modules/workflows/lib/interpolation-pipeline.js +271 -0
  590. package/dist/modules/workflows/lib/interpolation-pipeline.js.map +7 -0
  591. package/dist/modules/workflows/lib/ledger-drag.js +28 -0
  592. package/dist/modules/workflows/lib/ledger-drag.js.map +7 -0
  593. package/dist/modules/workflows/lib/ledger-entry-display.js +66 -0
  594. package/dist/modules/workflows/lib/ledger-entry-display.js.map +7 -0
  595. package/dist/modules/workflows/lib/ledger-schema-flatten.js +82 -0
  596. package/dist/modules/workflows/lib/ledger-schema-flatten.js.map +7 -0
  597. package/dist/modules/workflows/lib/metrics/definition-metrics.js +110 -0
  598. package/dist/modules/workflows/lib/metrics/definition-metrics.js.map +7 -0
  599. package/dist/modules/workflows/lib/metrics/metrics-display.js +26 -0
  600. package/dist/modules/workflows/lib/metrics/metrics-display.js.map +7 -0
  601. package/dist/modules/workflows/lib/metrics/rollup-schedule.js +7 -0
  602. package/dist/modules/workflows/lib/metrics/rollup-schedule.js.map +7 -0
  603. package/dist/modules/workflows/lib/metrics/rollup-service.js +160 -0
  604. package/dist/modules/workflows/lib/metrics/rollup-service.js.map +7 -0
  605. package/dist/modules/workflows/lib/node-config-summary.js +128 -0
  606. package/dist/modules/workflows/lib/node-config-summary.js.map +7 -0
  607. package/dist/modules/workflows/lib/node-geometry.js +53 -0
  608. package/dist/modules/workflows/lib/node-geometry.js.map +7 -0
  609. package/dist/modules/workflows/lib/node-nudge.js +44 -0
  610. package/dist/modules/workflows/lib/node-nudge.js.map +7 -0
  611. package/dist/modules/workflows/lib/node-outcome-rows.js +88 -0
  612. package/dist/modules/workflows/lib/node-outcome-rows.js.map +7 -0
  613. package/dist/modules/workflows/lib/node-placement.js +55 -0
  614. package/dist/modules/workflows/lib/node-placement.js.map +7 -0
  615. package/dist/modules/workflows/lib/node-type-icons.js +54 -13
  616. package/dist/modules/workflows/lib/node-type-icons.js.map +2 -2
  617. package/dist/modules/workflows/lib/nodeFormTransforms.js +329 -19
  618. package/dist/modules/workflows/lib/nodeFormTransforms.js.map +2 -2
  619. package/dist/modules/workflows/lib/outcome-routing.js +158 -0
  620. package/dist/modules/workflows/lib/outcome-routing.js.map +7 -0
  621. package/dist/modules/workflows/lib/owned-definition.js +107 -0
  622. package/dist/modules/workflows/lib/owned-definition.js.map +7 -0
  623. package/dist/modules/workflows/lib/owned-node-changes.js +10 -0
  624. package/dist/modules/workflows/lib/owned-node-changes.js.map +7 -0
  625. package/dist/modules/workflows/lib/palette-drag.js +36 -0
  626. package/dist/modules/workflows/lib/palette-drag.js.map +7 -0
  627. package/dist/modules/workflows/lib/palette-drop.js +45 -0
  628. package/dist/modules/workflows/lib/palette-drop.js.map +7 -0
  629. package/dist/modules/workflows/lib/parallel-handler.js +57 -2
  630. package/dist/modules/workflows/lib/parallel-handler.js.map +2 -2
  631. package/dist/modules/workflows/lib/port-contract.js +54 -0
  632. package/dist/modules/workflows/lib/port-contract.js.map +7 -0
  633. package/dist/modules/workflows/lib/portal-task-access.js +89 -0
  634. package/dist/modules/workflows/lib/portal-task-access.js.map +7 -0
  635. package/dist/modules/workflows/lib/rerun-from-step.js +65 -0
  636. package/dist/modules/workflows/lib/rerun-from-step.js.map +7 -0
  637. package/dist/modules/workflows/lib/route-chip-events.js +23 -0
  638. package/dist/modules/workflows/lib/route-chip-events.js.map +7 -0
  639. package/dist/modules/workflows/lib/route-chips.js +91 -0
  640. package/dist/modules/workflows/lib/route-chips.js.map +7 -0
  641. package/dist/modules/workflows/lib/route-edge.js +17 -0
  642. package/dist/modules/workflows/lib/route-edge.js.map +7 -0
  643. package/dist/modules/workflows/lib/route-kinds.js +67 -0
  644. package/dist/modules/workflows/lib/route-kinds.js.map +7 -0
  645. package/dist/modules/workflows/lib/route-priority.js +104 -0
  646. package/dist/modules/workflows/lib/route-priority.js.map +7 -0
  647. package/dist/modules/workflows/lib/run-duration.js +44 -0
  648. package/dist/modules/workflows/lib/run-duration.js.map +7 -0
  649. package/dist/modules/workflows/lib/run-event-tone.js +25 -0
  650. package/dist/modules/workflows/lib/run-event-tone.js.map +7 -0
  651. package/dist/modules/workflows/lib/run-execution.js +305 -0
  652. package/dist/modules/workflows/lib/run-execution.js.map +7 -0
  653. package/dist/modules/workflows/lib/run-gantt.js +180 -0
  654. package/dist/modules/workflows/lib/run-gantt.js.map +7 -0
  655. package/dist/modules/workflows/lib/run-outcome-evidence.js +33 -0
  656. package/dist/modules/workflows/lib/run-outcome-evidence.js.map +7 -0
  657. package/dist/modules/workflows/lib/run-outcome.js +48 -0
  658. package/dist/modules/workflows/lib/run-outcome.js.map +7 -0
  659. package/dist/modules/workflows/lib/sample-resolver.js +71 -0
  660. package/dist/modules/workflows/lib/sample-resolver.js.map +7 -0
  661. package/dist/modules/workflows/lib/seeds.js +1 -1
  662. package/dist/modules/workflows/lib/seeds.js.map +2 -2
  663. package/dist/modules/workflows/lib/server-output-contract.js +65 -0
  664. package/dist/modules/workflows/lib/server-output-contract.js.map +7 -0
  665. package/dist/modules/workflows/lib/set-variable.js +47 -0
  666. package/dist/modules/workflows/lib/set-variable.js.map +7 -0
  667. package/dist/modules/workflows/lib/signal-handler.js +63 -4
  668. package/dist/modules/workflows/lib/signal-handler.js.map +2 -2
  669. package/dist/modules/workflows/lib/start-fixtures.js +42 -0
  670. package/dist/modules/workflows/lib/start-fixtures.js.map +7 -0
  671. package/dist/modules/workflows/lib/status-colors.js +153 -22
  672. package/dist/modules/workflows/lib/status-colors.js.map +2 -2
  673. package/dist/modules/workflows/lib/step-handler.js +529 -53
  674. package/dist/modules/workflows/lib/step-handler.js.map +3 -3
  675. package/dist/modules/workflows/lib/step-presentation.js +147 -0
  676. package/dist/modules/workflows/lib/step-presentation.js.map +7 -0
  677. package/dist/modules/workflows/lib/step-through.js +26 -0
  678. package/dist/modules/workflows/lib/step-through.js.map +7 -0
  679. package/dist/modules/workflows/lib/step-type-conversion.js +167 -0
  680. package/dist/modules/workflows/lib/step-type-conversion.js.map +7 -0
  681. package/dist/modules/workflows/lib/subgraph-clipboard.js +114 -0
  682. package/dist/modules/workflows/lib/subgraph-clipboard.js.map +7 -0
  683. package/dist/modules/workflows/lib/task-decision-context.js +38 -0
  684. package/dist/modules/workflows/lib/task-decision-context.js.map +7 -0
  685. package/dist/modules/workflows/lib/task-decisions.js +49 -0
  686. package/dist/modules/workflows/lib/task-decisions.js.map +7 -0
  687. package/dist/modules/workflows/lib/task-entity-access.js +62 -0
  688. package/dist/modules/workflows/lib/task-entity-access.js.map +7 -0
  689. package/dist/modules/workflows/lib/task-entity-aliases.js +39 -0
  690. package/dist/modules/workflows/lib/task-entity-aliases.js.map +7 -0
  691. package/dist/modules/workflows/lib/task-entity-types.js +36 -0
  692. package/dist/modules/workflows/lib/task-entity-types.js.map +7 -0
  693. package/dist/modules/workflows/lib/task-form-registry.js +33 -0
  694. package/dist/modules/workflows/lib/task-form-registry.js.map +7 -0
  695. package/dist/modules/workflows/lib/task-form-schema.js +75 -0
  696. package/dist/modules/workflows/lib/task-form-schema.js.map +7 -0
  697. package/dist/modules/workflows/lib/task-handler.js +251 -32
  698. package/dist/modules/workflows/lib/task-handler.js.map +2 -2
  699. package/dist/modules/workflows/lib/task-inspector-config.js +182 -0
  700. package/dist/modules/workflows/lib/task-inspector-config.js.map +7 -0
  701. package/dist/modules/workflows/lib/task-list-query.js +19 -0
  702. package/dist/modules/workflows/lib/task-list-query.js.map +7 -0
  703. package/dist/modules/workflows/lib/task-notifications.js +119 -0
  704. package/dist/modules/workflows/lib/task-notifications.js.map +7 -0
  705. package/dist/modules/workflows/lib/task-permission-settings.js +43 -0
  706. package/dist/modules/workflows/lib/task-permission-settings.js.map +7 -0
  707. package/dist/modules/workflows/lib/task-presets.js +35 -0
  708. package/dist/modules/workflows/lib/task-presets.js.map +7 -0
  709. package/dist/modules/workflows/lib/task-quick-action.js +45 -0
  710. package/dist/modules/workflows/lib/task-quick-action.js.map +7 -0
  711. package/dist/modules/workflows/lib/task-resolution.js +120 -0
  712. package/dist/modules/workflows/lib/task-resolution.js.map +7 -0
  713. package/dist/modules/workflows/lib/task-sla.js +357 -0
  714. package/dist/modules/workflows/lib/task-sla.js.map +7 -0
  715. package/dist/modules/workflows/lib/task-visibility-request.js +266 -0
  716. package/dist/modules/workflows/lib/task-visibility-request.js.map +7 -0
  717. package/dist/modules/workflows/lib/task-visibility.js +194 -0
  718. package/dist/modules/workflows/lib/task-visibility.js.map +7 -0
  719. package/dist/modules/workflows/lib/timer-handler.js +3 -1
  720. package/dist/modules/workflows/lib/timer-handler.js.map +2 -2
  721. package/dist/modules/workflows/lib/transition-handler.js +148 -20
  722. package/dist/modules/workflows/lib/transition-handler.js.map +2 -2
  723. package/dist/modules/workflows/lib/trigger-node.js +121 -0
  724. package/dist/modules/workflows/lib/trigger-node.js.map +7 -0
  725. package/dist/modules/workflows/lib/visual-editor-navigation.js +14 -0
  726. package/dist/modules/workflows/lib/visual-editor-navigation.js.map +7 -0
  727. package/dist/modules/workflows/lib/work-inbox/empty-state.js +52 -0
  728. package/dist/modules/workflows/lib/work-inbox/empty-state.js.map +7 -0
  729. package/dist/modules/workflows/lib/work-inbox/entity-links.js +63 -0
  730. package/dist/modules/workflows/lib/work-inbox/entity-links.js.map +7 -0
  731. package/dist/modules/workflows/lib/work-inbox/navigation.js +11 -0
  732. package/dist/modules/workflows/lib/work-inbox/navigation.js.map +7 -0
  733. package/dist/modules/workflows/lib/work-inbox/presentation.js +90 -0
  734. package/dist/modules/workflows/lib/work-inbox/presentation.js.map +7 -0
  735. package/dist/modules/workflows/lib/work-inbox/provider.js +118 -0
  736. package/dist/modules/workflows/lib/work-inbox/provider.js.map +7 -0
  737. package/dist/modules/workflows/lib/work-inbox/query.js +70 -0
  738. package/dist/modules/workflows/lib/work-inbox/query.js.map +7 -0
  739. package/dist/modules/workflows/lib/work-inbox/service.js +56 -0
  740. package/dist/modules/workflows/lib/work-inbox/service.js.map +7 -0
  741. package/dist/modules/workflows/lib/work-inbox/user-task-source.js +149 -0
  742. package/dist/modules/workflows/lib/work-inbox/user-task-source.js.map +7 -0
  743. package/dist/modules/workflows/lib/workflow-command-enablement.js +57 -0
  744. package/dist/modules/workflows/lib/workflow-command-enablement.js.map +7 -0
  745. package/dist/modules/workflows/lib/workflow-command-settings.js +54 -0
  746. package/dist/modules/workflows/lib/workflow-command-settings.js.map +7 -0
  747. package/dist/modules/workflows/lib/workflow-executor.js +658 -6
  748. package/dist/modules/workflows/lib/workflow-executor.js.map +2 -2
  749. package/dist/modules/workflows/lib/workflow-function-registry.js +40 -0
  750. package/dist/modules/workflows/lib/workflow-function-registry.js.map +7 -0
  751. package/dist/modules/workflows/lib/workflow-safe-commands.js +11 -1
  752. package/dist/modules/workflows/lib/workflow-safe-commands.js.map +2 -2
  753. package/dist/modules/workflows/lib/workflow-templates.js +69 -0
  754. package/dist/modules/workflows/lib/workflow-templates.js.map +7 -0
  755. package/dist/modules/workflows/migrations/Migration20260626214855_workflows.js +15 -0
  756. package/dist/modules/workflows/migrations/Migration20260626214855_workflows.js.map +7 -0
  757. package/dist/modules/workflows/migrations/Migration20260626222424_workflows.js +17 -0
  758. package/dist/modules/workflows/migrations/Migration20260626222424_workflows.js.map +7 -0
  759. package/dist/modules/workflows/migrations/Migration20260727074335_workflows.js +15 -0
  760. package/dist/modules/workflows/migrations/Migration20260727074335_workflows.js.map +7 -0
  761. package/dist/modules/workflows/migrations/Migration20260728104038_workflows.js +13 -0
  762. package/dist/modules/workflows/migrations/Migration20260728104038_workflows.js.map +7 -0
  763. package/dist/modules/workflows/migrations/Migration20260728163001_workflows.js +15 -0
  764. package/dist/modules/workflows/migrations/Migration20260728163001_workflows.js.map +7 -0
  765. package/dist/modules/workflows/migrations/Migration20260729061736_workflows.js +13 -0
  766. package/dist/modules/workflows/migrations/Migration20260729061736_workflows.js.map +7 -0
  767. package/dist/modules/workflows/migrations/Migration20260729192652_workflows.js +12 -0
  768. package/dist/modules/workflows/migrations/Migration20260729192652_workflows.js.map +7 -0
  769. package/dist/modules/workflows/migrations/Migration20260730085000_workflows.js +13 -0
  770. package/dist/modules/workflows/migrations/Migration20260730085000_workflows.js.map +7 -0
  771. package/dist/modules/workflows/migrations/Migration20260730092706_workflows.js +13 -0
  772. package/dist/modules/workflows/migrations/Migration20260730092706_workflows.js.map +7 -0
  773. package/dist/modules/workflows/notifications.js +38 -10
  774. package/dist/modules/workflows/notifications.js.map +2 -2
  775. package/dist/modules/workflows/setup.js +81 -1
  776. package/dist/modules/workflows/setup.js.map +2 -2
  777. package/dist/modules/workflows/subscribers/task-assigned-notification.js +19 -27
  778. package/dist/modules/workflows/subscribers/task-assigned-notification.js.map +2 -2
  779. package/dist/modules/workflows/subscribers/task-deadline-breached-notification.js +24 -0
  780. package/dist/modules/workflows/subscribers/task-deadline-breached-notification.js.map +7 -0
  781. package/dist/modules/workflows/subscribers/task-reminder-notification.js +24 -0
  782. package/dist/modules/workflows/subscribers/task-reminder-notification.js.map +7 -0
  783. package/dist/modules/workflows/widgets/injection/pending-work/widget.client.js +73 -0
  784. package/dist/modules/workflows/widgets/injection/pending-work/widget.client.js.map +7 -0
  785. package/dist/modules/workflows/widgets/injection/pending-work/widget.js +17 -0
  786. package/dist/modules/workflows/widgets/injection/pending-work/widget.js.map +7 -0
  787. package/dist/modules/workflows/widgets/injection-table.js +26 -0
  788. package/dist/modules/workflows/widgets/injection-table.js.map +2 -2
  789. package/dist/modules/workflows/workers/workflow-activities.worker.js +61 -39
  790. package/dist/modules/workflows/workers/workflow-activities.worker.js.map +2 -2
  791. package/dist/modules/workflows/workers/workflow-definition-metric-rollup.worker.js +40 -0
  792. package/dist/modules/workflows/workers/workflow-definition-metric-rollup.worker.js.map +7 -0
  793. package/dist/modules/workflows/workers/workflow-instance-bulk-replay.js +43 -0
  794. package/dist/modules/workflows/workers/workflow-instance-bulk-replay.js.map +7 -0
  795. package/dist/modules/workflows/workers/workflow-invoke-agent.worker.js +30 -0
  796. package/dist/modules/workflows/workers/workflow-invoke-agent.worker.js.map +7 -0
  797. package/generated/entities/action_log/index.ts +1 -0
  798. package/generated/entities/user/index.ts +1 -0
  799. package/generated/entities/user_task/index.ts +7 -0
  800. package/generated/entities/workflow_definition/index.ts +3 -0
  801. package/generated/entities/workflow_definition_draft/index.ts +11 -0
  802. package/generated/entities/workflow_definition_metric_rollup/index.ts +10 -0
  803. package/generated/entities/workflow_instance/index.ts +2 -0
  804. package/generated/entities.ids.generated.ts +4 -1
  805. package/generated/entity-fields-registry.ts +39 -0
  806. package/jest.setup.ts +13 -0
  807. package/package.json +8 -7
  808. package/src/bootstrap.ts +13 -3
  809. package/src/helpers/integration/workflowsFixtures.ts +765 -5
  810. package/src/helpers/integration/workflowsUi.ts +318 -0
  811. package/src/modules/attachments/lib/createFromBuffer.ts +120 -0
  812. package/src/modules/audit_logs/data/entities.ts +12 -0
  813. package/src/modules/audit_logs/data/validators.ts +1 -0
  814. package/src/modules/audit_logs/lib/projections.ts +5 -1
  815. package/src/modules/audit_logs/migrations/.snapshot-open-mercato.json +26 -0
  816. package/src/modules/audit_logs/migrations/Migration20260625120000.ts +15 -0
  817. package/src/modules/audit_logs/services/actionLogService.ts +3 -0
  818. package/src/modules/auth/data/entities.ts +21 -0
  819. package/src/modules/auth/i18n/de.json +31 -0
  820. package/src/modules/auth/i18n/en.json +31 -0
  821. package/src/modules/auth/i18n/es.json +31 -0
  822. package/src/modules/auth/i18n/ko.json +31 -0
  823. package/src/modules/auth/i18n/pl.json +31 -0
  824. package/src/modules/auth/lib/executionPrincipal.ts +235 -0
  825. package/src/modules/auth/migrations/.snapshot-open-mercato.json +16 -0
  826. package/src/modules/auth/migrations/Migration20260625050000.ts +21 -0
  827. package/src/modules/business_rules/components/InlineRuleEditor.tsx +231 -0
  828. package/src/modules/business_rules/data/validators.ts +6 -0
  829. package/src/modules/business_rules/i18n/de.json +4 -0
  830. package/src/modules/business_rules/i18n/en.json +4 -0
  831. package/src/modules/business_rules/i18n/es.json +4 -0
  832. package/src/modules/business_rules/i18n/ko.json +4 -0
  833. package/src/modules/business_rules/i18n/pl.json +4 -0
  834. package/src/modules/business_rules/lib/rule-engine.ts +15 -1
  835. package/src/modules/catalog/commands/products.ts +12 -0
  836. package/src/modules/catalog/i18n/de.json +1 -0
  837. package/src/modules/catalog/i18n/en.json +1 -0
  838. package/src/modules/catalog/i18n/es.json +1 -0
  839. package/src/modules/catalog/i18n/ko.json +1 -0
  840. package/src/modules/catalog/i18n/pl.json +1 -0
  841. package/src/modules/catalog/workflows.ts +35 -0
  842. package/src/modules/customer_accounts/AGENTS.md +11 -0
  843. package/src/modules/customer_accounts/backend/customer_accounts/roles/[id]/page.tsx +2 -0
  844. package/src/modules/customer_accounts/cli.ts +96 -0
  845. package/src/modules/customer_accounts/i18n/de.json +5 -0
  846. package/src/modules/customer_accounts/i18n/en.json +5 -0
  847. package/src/modules/customer_accounts/i18n/es.json +5 -0
  848. package/src/modules/customer_accounts/i18n/ko.json +5 -0
  849. package/src/modules/customer_accounts/i18n/pl.json +5 -0
  850. package/src/modules/customer_accounts/lib/customerRoleAclSync.ts +92 -0
  851. package/src/modules/customer_accounts/setup.ts +15 -3
  852. package/src/modules/customers/commands/deals.ts +5 -0
  853. package/src/modules/customers/components/detail/ChangelogEntryRow.tsx +4 -2
  854. package/src/modules/customers/components/detail/utils.ts +2 -0
  855. package/src/modules/customers/events.ts +21 -3
  856. package/src/modules/customers/i18n/de.json +4 -0
  857. package/src/modules/customers/i18n/en.json +4 -0
  858. package/src/modules/customers/i18n/es.json +4 -0
  859. package/src/modules/customers/i18n/ko.json +4 -0
  860. package/src/modules/customers/i18n/pl.json +4 -0
  861. package/src/modules/customers/lib/link-workflow-task-handler.ts +136 -0
  862. package/src/modules/customers/lib/todoCompatibility.ts +4 -1
  863. package/src/modules/customers/lib/workflowTaskLink.ts +23 -0
  864. package/src/modules/customers/subscribers/link-workflow-task.ts +19 -0
  865. package/src/modules/customers/workflows.ts +57 -0
  866. package/src/modules/entities/api/records.ts +18 -62
  867. package/src/modules/entities/lib/entityClassification.ts +102 -0
  868. package/src/modules/sales/commands/documents.ts +46 -0
  869. package/src/modules/sales/events.ts +20 -2
  870. package/src/modules/sales/i18n/de.json +2 -1
  871. package/src/modules/sales/i18n/en.json +2 -1
  872. package/src/modules/sales/i18n/es.json +2 -1
  873. package/src/modules/sales/i18n/ko.json +2 -1
  874. package/src/modules/sales/i18n/pl.json +2 -1
  875. package/src/modules/sales/workflows.ts +12 -1
  876. package/src/modules/seeds/README.md +86 -0
  877. package/src/modules/seeds/cli.ts +220 -0
  878. package/src/modules/seeds/examples/sample-seed.json +26 -0
  879. package/src/modules/seeds/index.ts +11 -0
  880. package/src/modules/workflows/AGENTS.md +851 -10
  881. package/src/modules/workflows/acl.ts +96 -0
  882. package/src/modules/workflows/ai-agents.ts +80 -0
  883. package/src/modules/workflows/ai-tools/authoring-pack.ts +798 -0
  884. package/src/modules/workflows/ai-tools/types.ts +117 -0
  885. package/src/modules/workflows/ai-tools.ts +20 -0
  886. package/src/modules/workflows/api/command-settings/route.ts +171 -0
  887. package/src/modules/workflows/api/commands/route.ts +109 -0
  888. package/src/modules/workflows/api/definitions/[id]/callers/route.ts +95 -0
  889. package/src/modules/workflows/api/definitions/[id]/context-schema/route.ts +187 -0
  890. package/src/modules/workflows/api/definitions/[id]/customize/route.ts +6 -1
  891. package/src/modules/workflows/api/definitions/[id]/draft/route.ts +395 -0
  892. package/src/modules/workflows/api/definitions/[id]/publish/route.ts +250 -0
  893. package/src/modules/workflows/api/definitions/[id]/reset-to-code/route.ts +5 -0
  894. package/src/modules/workflows/api/definitions/[id]/route.ts +107 -7
  895. package/src/modules/workflows/api/definitions/[id]/test-step/route.ts +288 -0
  896. package/src/modules/workflows/api/definitions/generate/route.ts +335 -0
  897. package/src/modules/workflows/api/definitions/route.ts +88 -113
  898. package/src/modules/workflows/api/definitions/serialize.ts +9 -0
  899. package/src/modules/workflows/api/endpoints/route.ts +88 -0
  900. package/src/modules/workflows/api/functions/route.ts +91 -0
  901. package/src/modules/workflows/api/grantable-features/route.ts +102 -0
  902. package/src/modules/workflows/api/instances/[id]/context/route.ts +214 -0
  903. package/src/modules/workflows/api/instances/[id]/rerun-step/route.ts +226 -0
  904. package/src/modules/workflows/api/instances/[id]/retry/route.ts +18 -2
  905. package/src/modules/workflows/api/instances/[id]/step-through/route.ts +195 -0
  906. package/src/modules/workflows/api/instances/[id]/steps/route.ts +160 -0
  907. package/src/modules/workflows/api/instances/[id]/would-do/route.ts +216 -0
  908. package/src/modules/workflows/api/instances/bulk-replay/route.ts +141 -0
  909. package/src/modules/workflows/api/instances/failure-queue/route.ts +197 -0
  910. package/src/modules/workflows/api/instances/route.ts +135 -3
  911. package/src/modules/workflows/api/metrics/definitions/route.ts +262 -0
  912. package/src/modules/workflows/api/openapi.ts +549 -2
  913. package/src/modules/workflows/api/portal/tasks/[id]/complete/route.ts +160 -0
  914. package/src/modules/workflows/api/portal/tasks/[id]/route.ts +124 -0
  915. package/src/modules/workflows/api/portal/tasks/route.ts +157 -0
  916. package/src/modules/workflows/api/rule-usage/route.ts +94 -0
  917. package/src/modules/workflows/api/task-settings/route.ts +138 -0
  918. package/src/modules/workflows/api/tasks/[id]/claim/route.ts +42 -17
  919. package/src/modules/workflows/api/tasks/[id]/complete/route.ts +47 -15
  920. package/src/modules/workflows/api/tasks/[id]/reassign/route.ts +197 -0
  921. package/src/modules/workflows/api/tasks/[id]/route.ts +57 -6
  922. package/src/modules/workflows/api/tasks/[id]/unclaim/route.ts +132 -0
  923. package/src/modules/workflows/api/tasks/route.ts +60 -5
  924. package/src/modules/workflows/api/tasks/serialize.ts +119 -0
  925. package/src/modules/workflows/api/templates/route.ts +87 -0
  926. package/src/modules/workflows/api/work-inbox/next/route.ts +170 -0
  927. package/src/modules/workflows/api/work-inbox/route.ts +153 -0
  928. package/src/modules/workflows/backend/config/workflows/page.meta.ts +33 -0
  929. package/src/modules/workflows/backend/config/workflows/page.tsx +12 -0
  930. package/src/modules/workflows/backend/definitions/[id]/page.tsx +19 -343
  931. package/src/modules/workflows/backend/definitions/create/page.meta.ts +1 -1
  932. package/src/modules/workflows/backend/definitions/create/page.tsx +19 -59
  933. package/src/modules/workflows/backend/definitions/page.tsx +156 -76
  934. package/src/modules/workflows/backend/definitions/visual-editor/page.tsx +3129 -584
  935. package/src/modules/workflows/backend/instances/[id]/page.meta.ts +1 -1
  936. package/src/modules/workflows/backend/instances/[id]/page.tsx +711 -539
  937. package/src/modules/workflows/backend/instances/failure-queue/page.meta.ts +16 -0
  938. package/src/modules/workflows/backend/instances/failure-queue/page.tsx +311 -0
  939. package/src/modules/workflows/backend/instances/page.meta.ts +1 -1
  940. package/src/modules/workflows/backend/instances/page.tsx +218 -38
  941. package/src/modules/workflows/backend/tasks/[id]/page.tsx +639 -400
  942. package/src/modules/workflows/backend/tasks/page.meta.ts +7 -0
  943. package/src/modules/workflows/backend/tasks/page.tsx +20 -333
  944. package/src/modules/workflows/backend/work-inbox/page.meta.ts +15 -0
  945. package/src/modules/workflows/backend/work-inbox/page.tsx +506 -0
  946. package/src/modules/workflows/cli.ts +1 -1
  947. package/src/modules/workflows/commands/tasks.ts +146 -0
  948. package/src/modules/workflows/components/ActivitiesEditor.tsx +91 -114
  949. package/src/modules/workflows/components/ActivityEditDialog.tsx +244 -0
  950. package/src/modules/workflows/components/AgentSelector.tsx +200 -0
  951. package/src/modules/workflows/components/AnnotationEditDialog.tsx +157 -0
  952. package/src/modules/workflows/components/BusinessRuleUsagePanel.tsx +106 -0
  953. package/src/modules/workflows/components/BusinessRulesSelector.tsx +71 -17
  954. package/src/modules/workflows/components/ConfigJsonTextarea.tsx +111 -0
  955. package/src/modules/workflows/components/ContextSchemaEditor.tsx +165 -0
  956. package/src/modules/workflows/components/DefinitionErrorHandlerField.tsx +133 -0
  957. package/src/modules/workflows/components/DefinitionMetadataDrawer.tsx +456 -0
  958. package/src/modules/workflows/components/DefinitionMetricsCells.tsx +212 -0
  959. package/src/modules/workflows/components/DefinitionTriggersEditor.tsx +196 -95
  960. package/src/modules/workflows/components/EdgeEditDialog.tsx +15 -16
  961. package/src/modules/workflows/components/EdgeEditDialogCrudForm.tsx +130 -80
  962. package/src/modules/workflows/components/InputDataPanel.tsx +177 -0
  963. package/src/modules/workflows/components/InspectorPanel.tsx +250 -0
  964. package/src/modules/workflows/components/NodeEditDialog.tsx +444 -38
  965. package/src/modules/workflows/components/NodeEditDialogCrudForm.tsx +848 -178
  966. package/src/modules/workflows/components/StepsEditor.tsx +9 -2
  967. package/src/modules/workflows/components/TemplateGalleryDialog.tsx +205 -0
  968. package/src/modules/workflows/components/TransitionsEditor.tsx +98 -89
  969. package/src/modules/workflows/components/TriggersDialog.tsx +83 -0
  970. package/src/modules/workflows/components/TriggersEditor.tsx +363 -0
  971. package/src/modules/workflows/components/WorkflowAiDraftDialog.tsx +383 -0
  972. package/src/modules/workflows/components/WorkflowCodeView.tsx +257 -0
  973. package/src/modules/workflows/components/WorkflowCommandPalette.tsx +82 -0
  974. package/src/modules/workflows/components/WorkflowCompensationGhostEdge.tsx +78 -0
  975. package/src/modules/workflows/components/WorkflowDataMappingEdge.tsx +46 -0
  976. package/src/modules/workflows/components/WorkflowGraph.tsx +54 -1
  977. package/src/modules/workflows/components/WorkflowGraphImpl.tsx +463 -37
  978. package/src/modules/workflows/components/WorkflowIconPicker.tsx +144 -0
  979. package/src/modules/workflows/components/WorkflowLegend.tsx +59 -33
  980. package/src/modules/workflows/components/WorkflowNodeCard.tsx +355 -28
  981. package/src/modules/workflows/components/WorkflowRouteChips.tsx +145 -0
  982. package/src/modules/workflows/components/WorkflowSelector.tsx +33 -14
  983. package/src/modules/workflows/components/WorkflowTransitionEdge.tsx +83 -10
  984. package/src/modules/workflows/components/WorkflowTransitionLabel.tsx +13 -2
  985. package/src/modules/workflows/components/fields/ActivityArrayEditor.tsx +120 -30
  986. package/src/modules/workflows/components/fields/ActivityConfigFields.tsx +675 -0
  987. package/src/modules/workflows/components/fields/ActivityTestPanel.tsx +250 -0
  988. package/src/modules/workflows/components/fields/AgentInvokeConfigField.tsx +580 -0
  989. package/src/modules/workflows/components/fields/AgentReviewOnBreachField.tsx +88 -0
  990. package/src/modules/workflows/components/fields/BranchingRoutesEditor.tsx +323 -0
  991. package/src/modules/workflows/components/fields/BusinessRuleConditionsEditor.tsx +36 -4
  992. package/src/modules/workflows/components/fields/CommandPicker.tsx +158 -0
  993. package/src/modules/workflows/components/fields/EndpointPicker.tsx +330 -0
  994. package/src/modules/workflows/components/fields/FormFieldArrayEditor.tsx +2 -2
  995. package/src/modules/workflows/components/fields/FunctionPicker.tsx +68 -0
  996. package/src/modules/workflows/components/fields/MappingArrayEditor.tsx +46 -12
  997. package/src/modules/workflows/components/fields/PortFieldArrayEditor.tsx +255 -0
  998. package/src/modules/workflows/components/fields/RolesMultiSelect.tsx +68 -0
  999. package/src/modules/workflows/components/fields/RouteOrderEditor.tsx +169 -0
  1000. package/src/modules/workflows/components/fields/SchemaBuilderDialog.tsx +139 -0
  1001. package/src/modules/workflows/components/fields/StartPreConditionsEditor.tsx +2 -2
  1002. package/src/modules/workflows/components/fields/TaskAssignmentField.tsx +355 -0
  1003. package/src/modules/workflows/components/fields/TaskDeadlineFields.tsx +172 -0
  1004. package/src/modules/workflows/components/fields/TaskDecisionsField.tsx +294 -0
  1005. package/src/modules/workflows/components/fields/TaskEntityBindingsField.tsx +156 -0
  1006. package/src/modules/workflows/components/fields/TemplateTextControl.tsx +85 -0
  1007. package/src/modules/workflows/components/fields/VariablePickerButton.tsx +177 -0
  1008. package/src/modules/workflows/components/fields/ledgerDropTarget.ts +55 -0
  1009. package/src/modules/workflows/components/fields/useActivityTypeOptions.ts +19 -0
  1010. package/src/modules/workflows/components/formConfig.tsx +84 -10
  1011. package/src/modules/workflows/components/mobile/MobileDefinitionDetail.tsx +5 -0
  1012. package/src/modules/workflows/components/mobile/MobileMetadataSheet.tsx +10 -0
  1013. package/src/modules/workflows/components/mobile/MobileTaskForm.tsx +55 -19
  1014. package/src/modules/workflows/components/mobile/MobileVisualEditor.tsx +49 -40
  1015. package/src/modules/workflows/components/nodes/AnnotationGroupNode.tsx +81 -0
  1016. package/src/modules/workflows/components/nodes/AutomatedNode.tsx +34 -17
  1017. package/src/modules/workflows/components/nodes/EndNode.tsx +15 -14
  1018. package/src/modules/workflows/components/nodes/ErrorOutputHandle.tsx +32 -0
  1019. package/src/modules/workflows/components/nodes/IfElseNode.tsx +64 -0
  1020. package/src/modules/workflows/components/nodes/InvokeAgentNode.tsx +180 -0
  1021. package/src/modules/workflows/components/nodes/NodeOutcomeRows.tsx +143 -0
  1022. package/src/modules/workflows/components/nodes/ParallelForkNode.tsx +28 -15
  1023. package/src/modules/workflows/components/nodes/ParallelJoinNode.tsx +28 -15
  1024. package/src/modules/workflows/components/nodes/StartNode.tsx +78 -18
  1025. package/src/modules/workflows/components/nodes/StickyNoteNode.tsx +65 -0
  1026. package/src/modules/workflows/components/nodes/SubWorkflowNode.tsx +124 -23
  1027. package/src/modules/workflows/components/nodes/SwitchNode.tsx +64 -0
  1028. package/src/modules/workflows/components/nodes/TriggerCap.tsx +103 -0
  1029. package/src/modules/workflows/components/nodes/TriggerNode.tsx +151 -0
  1030. package/src/modules/workflows/components/nodes/UserTaskNode.tsx +67 -22
  1031. package/src/modules/workflows/components/nodes/WaitForConditionNode.tsx +74 -0
  1032. package/src/modules/workflows/components/nodes/WaitForSignalNode.tsx +31 -17
  1033. package/src/modules/workflows/components/nodes/WaitForTimerNode.tsx +31 -16
  1034. package/src/modules/workflows/components/nodes/index.ts +17 -0
  1035. package/src/modules/workflows/components/portal/PortalTaskForm.tsx +280 -0
  1036. package/src/modules/workflows/components/run/AgentActivityPanel.tsx +48 -0
  1037. package/src/modules/workflows/components/run/RerunStepDialog.tsx +133 -0
  1038. package/src/modules/workflows/components/run/RunEventBadge.tsx +37 -0
  1039. package/src/modules/workflows/components/run/RunGantt.tsx +180 -0
  1040. package/src/modules/workflows/components/run/RunStatusBadge.tsx +112 -0
  1041. package/src/modules/workflows/components/run/RunStepInspector.tsx +176 -0
  1042. package/src/modules/workflows/components/run/WouldDoReport.tsx +93 -0
  1043. package/src/modules/workflows/components/run/useAgentLabels.ts +42 -0
  1044. package/src/modules/workflows/components/run/useLastRunOverlay.ts +150 -0
  1045. package/src/modules/workflows/components/run/useLiveAgentActions.ts +70 -0
  1046. package/src/modules/workflows/components/run/useLiveRunUpdates.ts +89 -0
  1047. package/src/modules/workflows/components/settings/CommandSettingsPageClient.tsx +228 -0
  1048. package/src/modules/workflows/components/work-inbox/EntityContextPanel.tsx +179 -0
  1049. package/src/modules/workflows/components/work-inbox/ReassignTaskDialog.tsx +203 -0
  1050. package/src/modules/workflows/components/work-inbox/StatusChip.tsx +61 -0
  1051. package/src/modules/workflows/components/work-inbox/TaskActionNotice.tsx +77 -0
  1052. package/src/modules/workflows/components/work-inbox/TaskFormFields.tsx +322 -0
  1053. package/src/modules/workflows/components/work-inbox/WorkInboxEmptyState.tsx +43 -0
  1054. package/src/modules/workflows/data/activity-config-schemas.ts +249 -0
  1055. package/src/modules/workflows/data/activity-config-warnings.ts +71 -0
  1056. package/src/modules/workflows/data/branching-route-warnings.ts +129 -0
  1057. package/src/modules/workflows/data/entities.ts +397 -4
  1058. package/src/modules/workflows/data/metrics-validators.ts +51 -0
  1059. package/src/modules/workflows/data/task-primitives.ts +36 -0
  1060. package/src/modules/workflows/data/types.ts +81 -2
  1061. package/src/modules/workflows/data/validators.ts +729 -60
  1062. package/src/modules/workflows/di.ts +22 -0
  1063. package/src/modules/workflows/events.ts +68 -8
  1064. package/src/modules/workflows/examples/oze-residential-install-60-nodes.json +1545 -0
  1065. package/src/modules/workflows/examples/sales-pipeline-definition.json +35 -35
  1066. package/src/modules/workflows/examples/templates/lead-to-install.json +160 -0
  1067. package/src/modules/workflows/examples/templates/order-approval.json +230 -0
  1068. package/src/modules/workflows/examples/templates/task-escalation.json +110 -0
  1069. package/src/modules/workflows/examples/templates/webhook-integration.json +113 -0
  1070. package/src/modules/workflows/extension-points.ts +1 -1
  1071. package/src/modules/workflows/frontend/[orgSlug]/portal/tasks/[id]/page.meta.ts +21 -0
  1072. package/src/modules/workflows/frontend/[orgSlug]/portal/tasks/[id]/page.tsx +212 -0
  1073. package/src/modules/workflows/frontend/[orgSlug]/portal/tasks/page.meta.ts +25 -0
  1074. package/src/modules/workflows/frontend/[orgSlug]/portal/tasks/page.tsx +185 -0
  1075. package/src/modules/workflows/i18n/de.json +1083 -24
  1076. package/src/modules/workflows/i18n/en.json +1081 -22
  1077. package/src/modules/workflows/i18n/es.json +1083 -24
  1078. package/src/modules/workflows/i18n/ko.json +1072 -13
  1079. package/src/modules/workflows/i18n/pl.json +1082 -23
  1080. package/src/modules/workflows/lib/activity-executor.ts +855 -186
  1081. package/src/modules/workflows/lib/activity-queue-types.ts +120 -1
  1082. package/src/modules/workflows/lib/activity-registry-bootstrap.ts +7 -0
  1083. package/src/modules/workflows/lib/activity-registry.ts +89 -0
  1084. package/src/modules/workflows/lib/activity-types.ts +415 -0
  1085. package/src/modules/workflows/lib/activity-worker-handler.ts +744 -35
  1086. package/src/modules/workflows/lib/advanced-config.ts +41 -0
  1087. package/src/modules/workflows/lib/agent-disposition-task.ts +343 -0
  1088. package/src/modules/workflows/lib/agent-outcome-paths.ts +153 -0
  1089. package/src/modules/workflows/lib/agent-result-mapping.ts +88 -0
  1090. package/src/modules/workflows/lib/agent-review-inspector.ts +125 -0
  1091. package/src/modules/workflows/lib/agent-review.ts +171 -0
  1092. package/src/modules/workflows/lib/ai-authoring.ts +402 -0
  1093. package/src/modules/workflows/lib/ai-draft-runner.ts +105 -0
  1094. package/src/modules/workflows/lib/annotation-events.ts +15 -0
  1095. package/src/modules/workflows/lib/branching-routes.ts +280 -0
  1096. package/src/modules/workflows/lib/breach-routing.ts +142 -0
  1097. package/src/modules/workflows/lib/bulk-replay.ts +185 -0
  1098. package/src/modules/workflows/lib/business-rule-usage.ts +154 -0
  1099. package/src/modules/workflows/lib/caller-graph.ts +104 -0
  1100. package/src/modules/workflows/lib/code-view-apply.ts +178 -0
  1101. package/src/modules/workflows/lib/collect-validation-issues.ts +239 -0
  1102. package/src/modules/workflows/lib/compensation-ghosts.ts +134 -0
  1103. package/src/modules/workflows/lib/component-guard.ts +13 -0
  1104. package/src/modules/workflows/lib/condition-handler.ts +658 -0
  1105. package/src/modules/workflows/lib/context-ledger.ts +814 -0
  1106. package/src/modules/workflows/lib/crud-form-dialogs-flag.ts +5 -0
  1107. package/src/modules/workflows/lib/data-edge-mapping.ts +112 -0
  1108. package/src/modules/workflows/lib/definition-context-schema.ts +103 -0
  1109. package/src/modules/workflows/lib/definition-edit-safety.ts +228 -0
  1110. package/src/modules/workflows/lib/definition-error-body.ts +43 -0
  1111. package/src/modules/workflows/lib/definition-evaluation.ts +156 -0
  1112. package/src/modules/workflows/lib/definition-grant.ts +211 -0
  1113. package/src/modules/workflows/lib/definition-json-locations.ts +226 -0
  1114. package/src/modules/workflows/lib/definition-payload.ts +81 -0
  1115. package/src/modules/workflows/lib/draft-restore.ts +72 -0
  1116. package/src/modules/workflows/lib/dry-run.ts +203 -0
  1117. package/src/modules/workflows/lib/duration.ts +138 -24
  1118. package/src/modules/workflows/lib/edge-reattachment.ts +186 -0
  1119. package/src/modules/workflows/lib/edgeFormTransforms.ts +15 -14
  1120. package/src/modules/workflows/lib/editor-annotations.ts +301 -0
  1121. package/src/modules/workflows/lib/editor-commands.ts +205 -0
  1122. package/src/modules/workflows/lib/editor-history.ts +151 -0
  1123. package/src/modules/workflows/lib/endpoint-catalog.ts +283 -0
  1124. package/src/modules/workflows/lib/endpoint-path.ts +134 -0
  1125. package/src/modules/workflows/lib/engine-version.ts +67 -0
  1126. package/src/modules/workflows/lib/error-handler.ts +192 -0
  1127. package/src/modules/workflows/lib/error-routing.ts +236 -0
  1128. package/src/modules/workflows/lib/event-logger.ts +27 -0
  1129. package/src/modules/workflows/lib/expression-refs.ts +291 -0
  1130. package/src/modules/workflows/lib/failure-grouping.ts +134 -0
  1131. package/src/modules/workflows/lib/find-definition.ts +4 -0
  1132. package/src/modules/workflows/lib/flow-logic-warnings.ts +591 -0
  1133. package/src/modules/workflows/lib/graph-utils.ts +620 -110
  1134. package/src/modules/workflows/lib/icon-search.ts +65 -0
  1135. package/src/modules/workflows/lib/insert-at-cursor.ts +53 -0
  1136. package/src/modules/workflows/lib/instance-date-filter.ts +56 -0
  1137. package/src/modules/workflows/lib/interpolation-pipeline.ts +351 -0
  1138. package/src/modules/workflows/lib/ledger-drag.ts +61 -0
  1139. package/src/modules/workflows/lib/ledger-entry-display.ts +89 -0
  1140. package/src/modules/workflows/lib/ledger-schema-flatten.ts +137 -0
  1141. package/src/modules/workflows/lib/metrics/definition-metrics.ts +268 -0
  1142. package/src/modules/workflows/lib/metrics/metrics-display.ts +57 -0
  1143. package/src/modules/workflows/lib/metrics/rollup-schedule.ts +26 -0
  1144. package/src/modules/workflows/lib/metrics/rollup-service.ts +292 -0
  1145. package/src/modules/workflows/lib/node-config-summary.ts +235 -0
  1146. package/src/modules/workflows/lib/node-geometry.ts +168 -0
  1147. package/src/modules/workflows/lib/node-nudge.ts +63 -0
  1148. package/src/modules/workflows/lib/node-outcome-rows.ts +195 -0
  1149. package/src/modules/workflows/lib/node-placement.ts +104 -0
  1150. package/src/modules/workflows/lib/node-type-icons.ts +71 -10
  1151. package/src/modules/workflows/lib/nodeFormTransforms.ts +519 -28
  1152. package/src/modules/workflows/lib/outcome-routing.ts +346 -0
  1153. package/src/modules/workflows/lib/owned-definition.ts +188 -0
  1154. package/src/modules/workflows/lib/owned-node-changes.ts +29 -0
  1155. package/src/modules/workflows/lib/palette-drag.ts +68 -0
  1156. package/src/modules/workflows/lib/palette-drop.ts +114 -0
  1157. package/src/modules/workflows/lib/parallel-handler.ts +79 -2
  1158. package/src/modules/workflows/lib/port-contract.ts +107 -0
  1159. package/src/modules/workflows/lib/portal-task-access.ts +220 -0
  1160. package/src/modules/workflows/lib/rerun-from-step.ts +140 -0
  1161. package/src/modules/workflows/lib/route-chip-events.ts +45 -0
  1162. package/src/modules/workflows/lib/route-chips.ts +177 -0
  1163. package/src/modules/workflows/lib/route-edge.ts +57 -0
  1164. package/src/modules/workflows/lib/route-kinds.ts +166 -0
  1165. package/src/modules/workflows/lib/route-priority.ts +197 -0
  1166. package/src/modules/workflows/lib/run-duration.ts +70 -0
  1167. package/src/modules/workflows/lib/run-event-tone.ts +46 -0
  1168. package/src/modules/workflows/lib/run-execution.ts +598 -0
  1169. package/src/modules/workflows/lib/run-gantt.ts +311 -0
  1170. package/src/modules/workflows/lib/run-outcome-evidence.ts +60 -0
  1171. package/src/modules/workflows/lib/run-outcome.ts +142 -0
  1172. package/src/modules/workflows/lib/sample-resolver.ts +113 -0
  1173. package/src/modules/workflows/lib/seeds.ts +3 -1
  1174. package/src/modules/workflows/lib/server-output-contract.ts +120 -0
  1175. package/src/modules/workflows/lib/set-variable.ts +94 -0
  1176. package/src/modules/workflows/lib/signal-handler.ts +134 -4
  1177. package/src/modules/workflows/lib/start-fixtures.ts +93 -0
  1178. package/src/modules/workflows/lib/status-colors.ts +220 -21
  1179. package/src/modules/workflows/lib/step-handler.ts +744 -68
  1180. package/src/modules/workflows/lib/step-presentation.ts +295 -0
  1181. package/src/modules/workflows/lib/step-through.ts +71 -0
  1182. package/src/modules/workflows/lib/step-type-conversion.ts +263 -0
  1183. package/src/modules/workflows/lib/subgraph-clipboard.ts +214 -0
  1184. package/src/modules/workflows/lib/task-decision-context.ts +76 -0
  1185. package/src/modules/workflows/lib/task-decisions.ts +117 -0
  1186. package/src/modules/workflows/lib/task-entity-access.ts +181 -0
  1187. package/src/modules/workflows/lib/task-entity-aliases.ts +77 -0
  1188. package/src/modules/workflows/lib/task-entity-types.ts +66 -0
  1189. package/src/modules/workflows/lib/task-form-registry.ts +123 -0
  1190. package/src/modules/workflows/lib/task-form-schema.ts +129 -0
  1191. package/src/modules/workflows/lib/task-handler.ts +492 -51
  1192. package/src/modules/workflows/lib/task-inspector-config.ts +348 -0
  1193. package/src/modules/workflows/lib/task-list-query.ts +46 -0
  1194. package/src/modules/workflows/lib/task-notifications.ts +218 -0
  1195. package/src/modules/workflows/lib/task-permission-settings.ts +117 -0
  1196. package/src/modules/workflows/lib/task-presets.ts +95 -0
  1197. package/src/modules/workflows/lib/task-quick-action.ts +111 -0
  1198. package/src/modules/workflows/lib/task-resolution.ts +270 -0
  1199. package/src/modules/workflows/lib/task-sla.ts +628 -0
  1200. package/src/modules/workflows/lib/task-visibility-request.ts +668 -0
  1201. package/src/modules/workflows/lib/task-visibility.ts +627 -0
  1202. package/src/modules/workflows/lib/timer-handler.ts +8 -1
  1203. package/src/modules/workflows/lib/transition-handler.ts +228 -20
  1204. package/src/modules/workflows/lib/trigger-node.ts +272 -0
  1205. package/src/modules/workflows/lib/visual-editor-navigation.ts +29 -0
  1206. package/src/modules/workflows/lib/work-inbox/empty-state.ts +150 -0
  1207. package/src/modules/workflows/lib/work-inbox/entity-links.ts +99 -0
  1208. package/src/modules/workflows/lib/work-inbox/navigation.ts +28 -0
  1209. package/src/modules/workflows/lib/work-inbox/presentation.ts +163 -0
  1210. package/src/modules/workflows/lib/work-inbox/provider.ts +390 -0
  1211. package/src/modules/workflows/lib/work-inbox/query.ts +103 -0
  1212. package/src/modules/workflows/lib/work-inbox/service.ts +122 -0
  1213. package/src/modules/workflows/lib/work-inbox/user-task-source.ts +240 -0
  1214. package/src/modules/workflows/lib/workflow-command-enablement.ts +188 -0
  1215. package/src/modules/workflows/lib/workflow-command-settings.ts +124 -0
  1216. package/src/modules/workflows/lib/workflow-executor.ts +1036 -10
  1217. package/src/modules/workflows/lib/workflow-function-registry.ts +44 -0
  1218. package/src/modules/workflows/lib/workflow-safe-commands.ts +42 -1
  1219. package/src/modules/workflows/lib/workflow-templates.ts +91 -0
  1220. package/src/modules/workflows/migrations/.snapshot-open-mercato.json +2321 -698
  1221. package/src/modules/workflows/migrations/Migration20260626214855_workflows.ts +15 -0
  1222. package/src/modules/workflows/migrations/Migration20260626222424_workflows.ts +17 -0
  1223. package/src/modules/workflows/migrations/Migration20260727074335_workflows.ts +15 -0
  1224. package/src/modules/workflows/migrations/Migration20260728104038_workflows.ts +13 -0
  1225. package/src/modules/workflows/migrations/Migration20260728163001_workflows.ts +36 -0
  1226. package/src/modules/workflows/migrations/Migration20260729061736_workflows.ts +13 -0
  1227. package/src/modules/workflows/migrations/Migration20260729192652_workflows.ts +11 -0
  1228. package/src/modules/workflows/migrations/Migration20260730085000_workflows.ts +13 -0
  1229. package/src/modules/workflows/migrations/Migration20260730092706_workflows.ts +13 -0
  1230. package/src/modules/workflows/notifications.ts +40 -10
  1231. package/src/modules/workflows/setup.ts +128 -0
  1232. package/src/modules/workflows/subscribers/task-assigned-notification.ts +36 -45
  1233. package/src/modules/workflows/subscribers/task-deadline-breached-notification.ts +31 -0
  1234. package/src/modules/workflows/subscribers/task-reminder-notification.ts +31 -0
  1235. package/src/modules/workflows/widgets/injection/pending-work/widget.client.tsx +119 -0
  1236. package/src/modules/workflows/widgets/injection/pending-work/widget.ts +27 -0
  1237. package/src/modules/workflows/widgets/injection-table.ts +52 -2
  1238. package/src/modules/workflows/workers/workflow-activities.worker.ts +95 -42
  1239. package/src/modules/workflows/workers/workflow-definition-metric-rollup.worker.ts +71 -0
  1240. package/src/modules/workflows/workers/workflow-instance-bulk-replay.ts +51 -0
  1241. package/src/modules/workflows/workers/workflow-invoke-agent.worker.ts +72 -0
@@ -1,16 +1,93 @@
1
1
  "use client";
2
2
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
3
  import { WorkflowGraph } from "../../../components/WorkflowGraph.js";
4
+ import { useLastRunOverlay } from "../../../components/run/useLastRunOverlay.js";
4
5
  import { NodeEditDialog } from "../../../components/NodeEditDialog.js";
5
6
  import { EdgeEditDialog } from "../../../components/EdgeEditDialog.js";
6
7
  import { NodeEditDialogCrudForm } from "../../../components/NodeEditDialogCrudForm.js";
7
8
  import { EdgeEditDialogCrudForm } from "../../../components/EdgeEditDialogCrudForm.js";
8
- import { useState, useCallback, useEffect } from "react";
9
+ import { useState, useCallback, useEffect, useMemo, useRef } from "react";
9
10
  import { useRouter, useSearchParams, usePathname } from "next/navigation";
10
- import { graphToDefinition, definitionToGraph, validateWorkflowGraph, generateStepId, generateTransitionId, appendWorkflowEdge } from "../../../lib/graph-utils.js";
11
+ import { graphToDefinition, definitionToGraph, applyAutoLayout, validateWorkflowGraph, generateStepId, generateTransitionId, appendWorkflowEdge, getBadgeForNodeType } from "../../../lib/graph-utils.js";
12
+ import { convertStepType } from "../../../lib/step-type-conversion.js";
13
+ import { collectValidationIssues, countIssuesBySeverity } from "../../../lib/collect-validation-issues.js";
14
+ import { formatWorkflowValidationError } from "../../../lib/format-validation-error.js";
15
+ import { resolveNewNodePlacement } from "../../../lib/node-placement.js";
16
+ import {
17
+ commitEditorHistory,
18
+ createEditorHistory,
19
+ redoEditorHistory,
20
+ undoEditorHistory
21
+ } from "../../../lib/editor-history.js";
22
+ import {
23
+ parseWorkflowSubgraph,
24
+ pasteWorkflowSubgraph,
25
+ serializeWorkflowSubgraph,
26
+ stringifyWorkflowSubgraph
27
+ } from "../../../lib/subgraph-clipboard.js";
28
+ import {
29
+ annotationsFromNodes,
30
+ annotationsToNodes,
31
+ createGroupNode,
32
+ createNoteNode,
33
+ isAnnotationNode,
34
+ readEditorAnnotations,
35
+ removeAnnotationNode,
36
+ updateAnnotationNode
37
+ } from "../../../lib/editor-annotations.js";
38
+ import { WORKFLOW_GROUP_TOGGLE_EVENT } from "../../../lib/annotation-events.js";
39
+ import { AnnotationEditDialog } from "../../../components/AnnotationEditDialog.js";
40
+ import { WorkflowCodeView } from "../../../components/WorkflowCodeView.js";
41
+ import { DefinitionMetadataDrawer } from "../../../components/DefinitionMetadataDrawer.js";
42
+ import { TriggersDialog } from "../../../components/TriggersDialog.js";
43
+ import { describeCodeViewDraft, evaluateCodeViewDraft } from "../../../lib/code-view-apply.js";
44
+ import {
45
+ WorkflowAiDraftDialog
46
+ } from "../../../components/WorkflowAiDraftDialog.js";
47
+ import { buildAiCheckpointLabel } from "../../../lib/ai-authoring.js";
48
+ import {
49
+ locateDefinitionJsonEntities,
50
+ locateIssues,
51
+ severityByLine
52
+ } from "../../../lib/definition-json-locations.js";
53
+ import { WorkflowCommandPalette } from "../../../components/WorkflowCommandPalette.js";
54
+ import { buildWorkflowEditorCommands } from "../../../lib/editor-commands.js";
55
+ import { NUDGE_COMMIT_DELAY_MS, nudgeOffset, nudgeSelectedNodes, resolveNudgeDirection, selectedNodeIds } from "../../../lib/node-nudge.js";
56
+ import { canRedoEditorHistory, canUndoEditorHistory } from "../../../lib/editor-history.js";
57
+ import { readPaletteDragItem, writePaletteDragPayload } from "../../../lib/palette-drag.js";
58
+ import { appendActivityToRoute, insertStepOnRoute } from "../../../lib/palette-drop.js";
59
+ import { useActivityTypeOptions } from "../../../components/fields/useActivityTypeOptions.js";
60
+ import { resolveActivityIcon } from "../../../components/WorkflowRouteChips.js";
11
61
  import { performDeleteEdgeFlow, performDeleteNodeFlow } from "../../../lib/visual-editor-delete-flow.js";
12
- import { humanizeDefinitionIssuePath } from "../../../lib/format-validation-error.js";
62
+ import { resolveCrudFormDialogsEnabled } from "../../../lib/crud-form-dialogs-flag.js";
63
+ import { decideDraftRestore, isServerDraftEligible, stableSerializeDefinition } from "../../../lib/draft-restore.js";
64
+ import { buildDefinitionPayload, buildMetadataPayload } from "../../../lib/definition-payload.js";
65
+ import { resolveDefinitionInterpolationMode } from "../../../lib/interpolation-pipeline.js";
66
+ import { findRouteKindDescriptorForHandle } from "../../../lib/route-kinds.js";
67
+ import { buildWorkflowRouteEdge } from "../../../lib/route-edge.js";
68
+ import { isDecisionSourceHandle } from "../../../lib/node-outcome-rows.js";
69
+ import { STRUCTURAL_EDIT_CONFLICT_CODE } from "../../../lib/definition-edit-safety.js";
70
+ import { WORKFLOW_NODE_DELETE_EVENT } from "../../../components/WorkflowNodeCard.js";
71
+ import { WORKFLOW_ROUTE_CHIP_EVENT, WORKFLOW_ROUTE_ACTIVITY_EVENT } from "../../../lib/route-chip-events.js";
72
+ import { ActivityEditDialog } from "../../../components/ActivityEditDialog.js";
73
+ import { applyRouteOrder, canNormalizeRoutePriorities, normalizeRoutePriorities, readRouteOrder } from "../../../lib/route-priority.js";
74
+ import { classifyConnection, applyInputMappingToNodes, buildDataMappingEdge } from "../../../lib/data-edge-mapping.js";
75
+ import { reattachWorkflowEdge } from "../../../lib/edge-reattachment.js";
13
76
  import { workflowDefinitionDataSchema } from "../../../data/validators.js";
77
+ import { collectActivityConfigWarnings } from "../../../data/activity-config-warnings.js";
78
+ import {
79
+ applyIfElseRoutes,
80
+ applySwitchRoutes,
81
+ isBranchingNodeType,
82
+ readBranchingRoutes,
83
+ readSwitchField
84
+ } from "../../../lib/branching-routes.js";
85
+ import {
86
+ buildTriggerPayloadContracts,
87
+ computeContextLedger
88
+ } from "../../../lib/context-ledger.js";
89
+ import { useAvailableEvents } from "@open-mercato/ui/backend/inputs/EventSelect";
90
+ import { toUnresolvedContextRefIssues } from "../../../lib/definition-evaluation.js";
14
91
  import { Page } from "@open-mercato/ui/backend/Page";
15
92
  import { Button } from "@open-mercato/ui/primitives/button";
16
93
  import { Input } from "@open-mercato/ui/primitives/input";
@@ -18,37 +95,92 @@ import { Textarea } from "@open-mercato/ui/primitives/textarea";
18
95
  import { Label } from "@open-mercato/ui/primitives/label";
19
96
  import { Switch } from "@open-mercato/ui/primitives/switch";
20
97
  import {
21
- Dialog,
22
- DialogContent,
23
- DialogDescription,
24
- DialogFooter,
25
- DialogHeader,
26
- DialogTitle
27
- } from "@open-mercato/ui/primitives/dialog";
28
- import { TagsInput } from "@open-mercato/ui/backend/inputs/TagsInput";
98
+ Drawer,
99
+ DrawerBody,
100
+ DrawerContent,
101
+ DrawerDescription,
102
+ DrawerFooter,
103
+ DrawerHeader,
104
+ DrawerTitle
105
+ } from "@open-mercato/ui/primitives/drawer";
29
106
  import { LoadingMessage } from "@open-mercato/ui/backend/detail";
30
- import { Alert, AlertTitle } from "@open-mercato/ui/primitives/alert";
31
- import { useConfirmDialog } from "@open-mercato/ui/backend/confirm-dialog";
107
+ import { Alert, AlertDescription, AlertTitle } from "@open-mercato/ui/primitives/alert";
108
+ import { ConfirmDialog, useConfirmDialog } from "@open-mercato/ui/backend/confirm-dialog";
32
109
  import { useT } from "@open-mercato/shared/lib/i18n/context";
33
- import { FormHeader } from "@open-mercato/ui/backend/forms";
110
+ import { formatRelativeTime } from "@open-mercato/shared/lib/time";
111
+ import { FormHeader, ActionsDropdown } from "@open-mercato/ui/backend/forms";
34
112
  import { apiCall, withScopedApiRequestHeaders } from "@open-mercato/ui/backend/utils/apiCall";
113
+ import { apiFetch } from "@open-mercato/ui/backend/utils/api";
35
114
  import { buildOptimisticLockHeader } from "@open-mercato/ui/backend/utils/optimisticLock";
36
115
  import { surfaceRecordConflict } from "@open-mercato/ui/backend/conflicts";
37
116
  import { buildRecordInjectionContext, useSetCurrentRecordInjectionContext } from "@open-mercato/ui/backend/injection/recordContext";
117
+ import { readJsonSafe } from "@open-mercato/ui/backend/utils/serverErrors";
118
+ import { useGuardedMutation } from "@open-mercato/ui/backend/injection/useGuardedMutation";
119
+ import { Spinner } from "@open-mercato/ui/primitives/spinner";
120
+ import {
121
+ listStartFixtures,
122
+ removeStartFixture,
123
+ upsertStartFixture
124
+ } from "../../../lib/start-fixtures.js";
38
125
  import { flash } from "@open-mercato/ui/backend/FlashMessages";
39
- import { CircleQuestionMark, PanelTopClose, PanelTopOpen, Play, Save, Trash2 } from "lucide-react";
126
+ import { ChevronDown, ChevronRight, CircleAlert, CircleQuestionMark, Code, Command, Group, History, Maximize2, Minimize2, Network, PanelLeftClose, PanelLeftOpen, PanelRightOpen, Play, Save, ShieldMinus, Sparkles, StickyNote, Trash2, TriangleAlert, X } from "lucide-react";
127
+ import { IconButton } from "@open-mercato/ui/primitives/icon-button";
128
+ import { usePersistedBooleanFlag } from "@open-mercato/ui/backend/crud/usePersistedBooleanFlag";
129
+ import { useSidebarCollapse } from "@open-mercato/ui/backend/AppShell";
40
130
  import { NODE_TYPE_ICONS, NODE_TYPE_COLORS, NODE_TYPE_LABELS } from "../../../lib/node-type-icons.js";
41
- import { DefinitionTriggersEditor } from "../../../components/DefinitionTriggersEditor.js";
131
+ import { TemplateGalleryDialog } from "../../../components/TemplateGalleryDialog.js";
42
132
  import { MobileVisualEditor } from "../../../components/mobile/MobileVisualEditor.js";
43
133
  import { useIsMobile } from "@open-mercato/ui/hooks/useIsMobile";
134
+ import * as React from "react";
44
135
  import { createLogger } from "@open-mercato/shared/lib/logger";
45
136
  const logger = createLogger("workflows");
137
+ const DRAFT_AUTOSAVE_DEBOUNCE_MS = 2e3;
138
+ const DRAFT_SAVED_LABEL_REFRESH_MS = 3e4;
139
+ function computeClientContextLedger(definitionData, triggerPayloadContracts) {
140
+ return computeContextLedger(definitionData, {
141
+ resolveOutputContract: () => "unknown",
142
+ triggerPayloadContracts
143
+ });
144
+ }
145
+ function collectContextRefWarnings(definitionData, translate, triggerPayloadContracts) {
146
+ const ledger = computeClientContextLedger(definitionData, triggerPayloadContracts);
147
+ return toUnresolvedContextRefIssues(definitionData, ledger, translate);
148
+ }
149
+ async function loadSubWorkflowContracts(definition) {
150
+ const contracts = /* @__PURE__ */ new Map();
151
+ const subWorkflowIds = Array.from(
152
+ new Set(
153
+ (definition?.steps || []).filter((step) => step?.stepType === "SUB_WORKFLOW" && step?.config?.subWorkflowId).map((step) => step.config.subWorkflowId)
154
+ )
155
+ );
156
+ await Promise.all(
157
+ subWorkflowIds.map(async (workflowId) => {
158
+ try {
159
+ const res = await apiCall(
160
+ `/api/workflows/definitions?workflowId=${encodeURIComponent(workflowId)}&limit=1`
161
+ );
162
+ const io = res.ok ? res.result?.data?.[0]?.definition?.io : void 0;
163
+ if (io) contracts.set(workflowId, io);
164
+ } catch {
165
+ }
166
+ })
167
+ );
168
+ return contracts;
169
+ }
170
+ const PALETTE_NODE_TYPES = ["start", "userTask", "automated", "invokeAgent", "ifElse", "switch", "waitForSignal", "waitForTimer", "waitForCondition", "subWorkflow", "end"];
171
+ function readStructuralEditConflictCount(status, body) {
172
+ if (status !== 409) return null;
173
+ const record = body && typeof body === "object" ? body : null;
174
+ if (!record || record.code !== STRUCTURAL_EDIT_CONFLICT_CODE) return null;
175
+ return typeof record.activeInstanceCount === "number" ? record.activeInstanceCount : 0;
176
+ }
46
177
  function VisualEditorPage() {
47
178
  const t = useT();
48
179
  const router = useRouter();
49
180
  const searchParams = useSearchParams();
50
181
  const pathname = usePathname();
51
182
  const definitionId = searchParams.get("id");
183
+ const templateId = searchParams.get("template");
52
184
  const isMobile = useIsMobile();
53
185
  const { confirm, ConfirmDialogElement } = useConfirmDialog();
54
186
  const [isLoading, setIsLoading] = useState(!!definitionId);
@@ -57,15 +189,56 @@ function VisualEditorPage() {
57
189
  const [edges, setEdges] = useState([]);
58
190
  const [selectedNode, setSelectedNode] = useState(null);
59
191
  const [selectedEdge, setSelectedEdge] = useState(null);
60
- const [showMetadata, setShowMetadata] = useState(true);
192
+ const [edgeDialogFocusFieldId, setEdgeDialogFocusFieldId] = useState(null);
193
+ const edgesRef = React.useRef([]);
194
+ edgesRef.current = edges;
195
+ const nodesRef = React.useRef([]);
196
+ nodesRef.current = nodes;
197
+ const [metadataOpen, setMetadataOpen] = useState(false);
198
+ const [metadataFocusSection, setMetadataFocusSection] = useState(null);
199
+ const [triggersDialogOpen, setTriggersDialogOpen] = useState(false);
200
+ const [activityEdit, setActivityEdit] = useState(null);
201
+ const handleOpenTriggers = useCallback(() => {
202
+ setTriggersDialogOpen(true);
203
+ }, []);
204
+ const handleMetadataOpenChange = useCallback((open) => {
205
+ if (!open) setMetadataFocusSection(null);
206
+ setMetadataOpen(open);
207
+ }, []);
61
208
  const [isCompactViewport, setIsCompactViewport] = useState(false);
209
+ const [autosaveState, setAutosaveState] = useState("idle");
210
+ const [structuralConflict, setStructuralConflict] = useState(null);
211
+ const [isCreatingVersion, setIsCreatingVersion] = useState(false);
212
+ const autosaveTimerRef = React.useRef(null);
213
+ const performAutosaveRef = React.useRef(async () => {
214
+ });
215
+ const lastAutosavedBodyRef = React.useRef(null);
216
+ const { value: paletteCollapsed, toggle: togglePaletteCollapsed, setValue: setPaletteCollapsed } = usePersistedBooleanFlag("om:wf-editor-palette", false);
217
+ const [showPaletteHowTo, setShowPaletteHowTo] = useState(false);
218
+ const { value: focusMode, setValue: setFocusMode, toggle: toggleFocus } = usePersistedBooleanFlag("om:wf-editor-focus", false);
219
+ const { value: showCompensation, toggle: toggleCompensation } = usePersistedBooleanFlag("om:wf-editor-compensation", false);
220
+ const { value: showLastRun, toggle: toggleLastRun } = usePersistedBooleanFlag("om:wf-editor-last-run", false);
221
+ const { requestCollapse, releaseRequest } = useSidebarCollapse();
222
+ const priorPaletteCollapsedRef = React.useRef(null);
223
+ useEffect(() => {
224
+ if (focusMode) {
225
+ if (priorPaletteCollapsedRef.current === null) priorPaletteCollapsedRef.current = paletteCollapsed;
226
+ requestCollapse(true);
227
+ setPaletteCollapsed(true);
228
+ } else {
229
+ releaseRequest();
230
+ if (priorPaletteCollapsedRef.current !== null) {
231
+ setPaletteCollapsed(priorPaletteCollapsedRef.current);
232
+ priorPaletteCollapsedRef.current = null;
233
+ }
234
+ }
235
+ }, [focusMode]);
236
+ useEffect(() => () => releaseRequest(), [releaseRequest]);
62
237
  useEffect(() => {
63
238
  if (typeof window === "undefined") return;
64
239
  const mediaQuery = window.matchMedia("(max-width: 1279px)");
65
240
  const applyViewportMode = () => {
66
- const compact = mediaQuery.matches;
67
- setIsCompactViewport(compact);
68
- setShowMetadata(!compact);
241
+ setIsCompactViewport(mediaQuery.matches);
69
242
  };
70
243
  applyViewportMode();
71
244
  mediaQuery.addEventListener("change", applyViewportMode);
@@ -75,8 +248,29 @@ function VisualEditorPage() {
75
248
  }, []);
76
249
  const [showNodeDialog, setShowNodeDialog] = useState(false);
77
250
  const [showEdgeDialog, setShowEdgeDialog] = useState(false);
251
+ const crudFormDialogsEnabled = resolveCrudFormDialogsEnabled(process.env.NEXT_PUBLIC_WORKFLOW_CRUDFORM_ENABLED);
252
+ const inspectorsDocked = crudFormDialogsEnabled && !isMobile && !isCompactViewport;
253
+ const [selectedAnnotation, setSelectedAnnotation] = useState(null);
254
+ const [showAnnotationDialog, setShowAnnotationDialog] = useState(false);
255
+ const [showCommandPalette, setShowCommandPalette] = useState(false);
256
+ const [showCodeView, setShowCodeView] = useState(false);
78
257
  const [showClearConfirm, setShowClearConfirm] = useState(false);
258
+ const [showTemplateGallery, setShowTemplateGallery] = useState(false);
259
+ const [problems, setProblems] = useState([]);
260
+ const [showProblems, setShowProblems] = useState(false);
261
+ const [problemsCollapsed, setProblemsCollapsed] = useState(false);
262
+ const [focusTarget, setFocusTarget] = useState(null);
263
+ const focusRequestRef = React.useRef(0);
264
+ const nodeErrorCounts = React.useMemo(() => {
265
+ const counts = {};
266
+ for (const issue of problems) {
267
+ if (issue.severity !== "error" || !issue.nodeId) continue;
268
+ counts[issue.nodeId] = (counts[issue.nodeId] ?? 0) + 1;
269
+ }
270
+ return counts;
271
+ }, [problems]);
79
272
  const [workflowId, setWorkflowId] = useState("");
273
+ const lastRunOverlay = useLastRunOverlay({ workflowId, enabled: showLastRun && !!definitionId });
80
274
  const [workflowName, setWorkflowName] = useState("");
81
275
  const [description, setDescription] = useState("");
82
276
  const [version, setVersion] = useState(1);
@@ -87,13 +281,101 @@ function VisualEditorPage() {
87
281
  const [effectiveFrom, setEffectiveFrom] = useState("");
88
282
  const [effectiveTo, setEffectiveTo] = useState("");
89
283
  const [triggers, setTriggers] = useState([]);
284
+ const [contextSchema, setContextSchema] = useState(void 0);
285
+ const [definitionIo, setDefinitionIo] = useState(void 0);
286
+ const [interpolation, setInterpolation] = useState(void 0);
287
+ const [errorHandler, setErrorHandler] = useState(void 0);
288
+ const [loadedMetadata, setLoadedMetadata] = useState(null);
90
289
  const [source, setSource] = useState(null);
91
290
  const [updatedAt, setUpdatedAt] = useState(null);
291
+ const [startOpen, setStartOpen] = useState(false);
292
+ const [startContext, setStartContext] = useState("{}");
293
+ const [starting, setStarting] = useState(false);
294
+ const [startDryRun, setStartDryRun] = useState(false);
295
+ const [startStepThrough, setStartStepThrough] = useState(false);
296
+ const [fixtureName, setFixtureName] = useState("");
297
+ const [history, setHistory] = useState(createEditorHistory);
298
+ const historyRef = React.useRef(history);
299
+ historyRef.current = history;
300
+ const loadedMetadataRef = React.useRef(null);
301
+ loadedMetadataRef.current = loadedMetadata;
302
+ const historyLabels = useMemo(() => ({
303
+ addStep: t("workflows.visualEditor.history.addStep", "Add step"),
304
+ editStep: t("workflows.visualEditor.history.editStep", "Edit step"),
305
+ editRoute: t("workflows.visualEditor.history.editRoute", "Edit route"),
306
+ deleteStep: t("workflows.visualEditor.history.deleteStep", "Delete step"),
307
+ deleteRoute: t("workflows.visualEditor.history.deleteRoute", "Delete route"),
308
+ connect: t("workflows.visualEditor.history.connect", "Connect steps"),
309
+ reattach: t("workflows.visualEditor.history.reattach", "Re-target route"),
310
+ move: t("workflows.visualEditor.history.move", "Move steps"),
311
+ convert: t("workflows.visualEditor.history.convert", "Change step type"),
312
+ branchRoutes: t("workflows.visualEditor.history.branchRoutes", "Edit branch routes"),
313
+ routeOrder: t("workflows.visualEditor.history.routeOrder", "Reorder routes"),
314
+ pinSample: t("workflows.visualEditor.history.pinSample", "Pin sample"),
315
+ unpinSample: t("workflows.visualEditor.history.unpinSample", "Unpin sample"),
316
+ tidy: t("workflows.visualEditor.history.tidy", "Tidy layout"),
317
+ paste: t("workflows.visualEditor.history.paste", "Paste"),
318
+ duplicate: t("workflows.visualEditor.history.duplicate", "Duplicate"),
319
+ insertOnRoute: t("workflows.visualEditor.history.insertOnRoute", "Insert step on route"),
320
+ addActivity: t("workflows.visualEditor.history.addActivity", "Add action to route"),
321
+ addNote: t("workflows.visualEditor.history.addNote", "Add note"),
322
+ addGroup: t("workflows.visualEditor.history.addGroup", "Add group"),
323
+ editAnnotation: t("workflows.visualEditor.history.editAnnotation", "Edit annotation"),
324
+ deleteAnnotation: t("workflows.visualEditor.history.deleteAnnotation", "Delete annotation"),
325
+ toggleGroup: t("workflows.visualEditor.history.toggleGroup", "Collapse group"),
326
+ saveFixture: t("workflows.visualEditor.history.saveFixture", "Save start fixture"),
327
+ deleteFixture: t("workflows.visualEditor.history.deleteFixture", "Delete start fixture"),
328
+ applyCode: t("workflows.visualEditor.history.applyCode", "Apply JSON from Code view")
329
+ }), [t]);
330
+ const panelStateRef = React.useRef({
331
+ triggers: [],
332
+ contextSchema: void 0,
333
+ io: void 0,
334
+ interpolation: void 0,
335
+ errorHandler: void 0
336
+ });
337
+ useEffect(() => {
338
+ panelStateRef.current = {
339
+ triggers,
340
+ contextSchema,
341
+ io: definitionIo,
342
+ interpolation,
343
+ errorHandler
344
+ };
345
+ }, [triggers, contextSchema, definitionIo, interpolation, errorHandler]);
346
+ const captureDocument = useCallback(() => ({
347
+ nodes: nodesRef.current,
348
+ edges: edgesRef.current,
349
+ metadata: loadedMetadataRef.current,
350
+ panel: panelStateRef.current
351
+ }), []);
352
+ const commitCapturedDocument = useCallback((document2, label) => {
353
+ setHistory((current) => commitEditorHistory(current, document2, label));
354
+ }, []);
355
+ const commitHistory = useCallback((label) => {
356
+ commitCapturedDocument(captureDocument(), label);
357
+ }, [captureDocument, commitCapturedDocument]);
358
+ const resetHistory = useCallback(() => {
359
+ setHistory(createEditorHistory());
360
+ }, []);
361
+ const mutationContextId = `workflows.definitions.visual-editor:${definitionId ?? "unknown"}`;
362
+ const { runMutation, retryLastMutation } = useGuardedMutation({
363
+ contextId: mutationContextId
364
+ });
92
365
  const isCodeOnly = source === "code";
93
366
  const isCodeOverride = source === "code_override";
367
+ const draftEligible = isServerDraftEligible(definitionId) && !isCodeOnly;
368
+ const [pendingDraft, setPendingDraft] = useState(null);
369
+ const [draftAutosaveReady, setDraftAutosaveReady] = useState(false);
370
+ const [draftSavedAt, setDraftSavedAt] = useState(null);
371
+ const [draftSaveFailed, setDraftSaveFailed] = useState(false);
372
+ const [draftClock, setDraftClock] = useState(0);
373
+ const lastPersistedDraftRef = useRef(null);
374
+ const draftSuspendedRef = useRef(false);
94
375
  useEffect(() => {
95
376
  const loadDefinition = async () => {
96
377
  if (!definitionId) {
378
+ setInterpolation("strict");
97
379
  setIsLoading(false);
98
380
  return;
99
381
  }
@@ -115,12 +397,64 @@ function VisualEditorPage() {
115
397
  setIcon(definition.metadata?.icon || "");
116
398
  setEffectiveFrom(definition.effectiveFrom || "");
117
399
  setEffectiveTo(definition.effectiveTo || "");
118
- const graph = definitionToGraph(definition.definition);
119
- setNodes(graph.nodes);
400
+ const childContracts = await loadSubWorkflowContracts(definition.definition);
401
+ const graph = definitionToGraph(definition.definition, { childContracts });
402
+ const loadedMetadataBag = definition.metadata && typeof definition.metadata === "object" ? definition.metadata : null;
403
+ const annotationNodes = annotationsToNodes(readEditorAnnotations(loadedMetadataBag));
404
+ setNodes([...graph.nodes, ...annotationNodes]);
120
405
  setEdges(graph.edges);
121
- setTriggers(definition.definition?.triggers || []);
122
- setSource(definition.source ?? null);
123
- setUpdatedAt(typeof definition.updatedAt === "string" ? definition.updatedAt : null);
406
+ const loadedTriggers = definition.definition?.triggers || [];
407
+ setTriggers(loadedTriggers);
408
+ const loadedContextSchema = definition.definition?.contextSchema ?? void 0;
409
+ setContextSchema(loadedContextSchema);
410
+ const loadedIo = definition.definition?.io ?? void 0;
411
+ setDefinitionIo(loadedIo);
412
+ const loadedInterpolation = resolveDefinitionInterpolationMode(definition.definition);
413
+ setInterpolation(loadedInterpolation);
414
+ const loadedErrorHandler = definition.definition?.errorHandler ?? void 0;
415
+ setErrorHandler(loadedErrorHandler);
416
+ const loadedMetadataObject = loadedMetadataBag ? { ...loadedMetadataBag } : null;
417
+ setLoadedMetadata(loadedMetadataObject);
418
+ const loadedSource = definition.source ?? null;
419
+ setSource(loadedSource);
420
+ const loadedUpdatedAt = typeof definition.updatedAt === "string" ? definition.updatedAt : null;
421
+ setUpdatedAt(loadedUpdatedAt);
422
+ const comparableDefinition = buildDefinitionPayload({
423
+ graphDefinition: graphToDefinition(graph.nodes, graph.edges, { includePositions: true }),
424
+ triggers: loadedTriggers,
425
+ contextSchema: loadedContextSchema,
426
+ io: loadedIo,
427
+ interpolation: loadedInterpolation,
428
+ errorHandler: loadedErrorHandler
429
+ });
430
+ const loadedDraftMetadata = buildMetadataPayload({
431
+ loadedMetadata: loadedMetadataObject,
432
+ category: definition.metadata?.category || "",
433
+ tags: definition.metadata?.tags || [],
434
+ icon: definition.metadata?.icon || "",
435
+ annotations: annotationsFromNodes(annotationNodes)
436
+ });
437
+ lastPersistedDraftRef.current = stableSerializeDefinition({
438
+ definition: comparableDefinition,
439
+ metadata: loadedDraftMetadata
440
+ });
441
+ if (loadedSource !== "code" && isServerDraftEligible(definitionId)) {
442
+ const draftResult = await apiCall(
443
+ `/api/workflows/definitions/${definitionId}/draft`
444
+ );
445
+ const draft = draftResult.ok ? draftResult.result?.data : void 0;
446
+ const decision = draft ? decideDraftRestore({
447
+ draftDefinition: draft.definition,
448
+ draftBaseUpdatedAt: draft.baseUpdatedAt,
449
+ loadedDefinition: comparableDefinition,
450
+ definitionUpdatedAt: loadedUpdatedAt
451
+ }) : { offerRestore: false };
452
+ if (draft && decision.offerRestore) {
453
+ setPendingDraft({ draft, baseMismatch: decision.baseMismatch });
454
+ } else {
455
+ setDraftAutosaveReady(true);
456
+ }
457
+ }
124
458
  } catch (error) {
125
459
  logger.error("Error loading workflow definition", { err: error });
126
460
  flash("Failed to load workflow definition", "error");
@@ -130,62 +464,475 @@ function VisualEditorPage() {
130
464
  };
131
465
  loadDefinition();
132
466
  }, [definitionId]);
133
- const handleNodesChange = useCallback((nextNodes) => {
467
+ const annotations = useMemo(() => annotationsFromNodes(nodes), [nodes]);
468
+ const draftMetadata = useMemo(
469
+ () => buildMetadataPayload({ loadedMetadata, tags, category, icon, annotations }),
470
+ [loadedMetadata, tags, category, icon, annotations]
471
+ );
472
+ useEffect(() => {
473
+ if (!draftAutosaveReady || !draftEligible || !definitionId) return;
474
+ if (draftSuspendedRef.current) return;
475
+ let payload;
476
+ try {
477
+ payload = {
478
+ definition: buildDefinitionPayload({
479
+ graphDefinition: graphToDefinition(nodes, edges, { includePositions: true }),
480
+ triggers,
481
+ contextSchema,
482
+ io: definitionIo,
483
+ interpolation,
484
+ errorHandler
485
+ }),
486
+ metadata: draftMetadata,
487
+ baseUpdatedAt: updatedAt
488
+ };
489
+ } catch {
490
+ return;
491
+ }
492
+ const serialized = stableSerializeDefinition({ definition: payload.definition, metadata: payload.metadata });
493
+ if (serialized === lastPersistedDraftRef.current) return;
494
+ const timer = window.setTimeout(async () => {
495
+ if (draftSuspendedRef.current) return;
496
+ try {
497
+ const result = await apiCall(
498
+ `/api/workflows/definitions/${definitionId}/draft`,
499
+ {
500
+ method: "PUT",
501
+ headers: { "Content-Type": "application/json" },
502
+ body: JSON.stringify(payload)
503
+ }
504
+ );
505
+ if (result.ok) {
506
+ lastPersistedDraftRef.current = serialized;
507
+ setDraftSavedAt((/* @__PURE__ */ new Date()).toISOString());
508
+ setDraftSaveFailed(false);
509
+ } else {
510
+ setDraftSaveFailed(true);
511
+ }
512
+ } catch {
513
+ setDraftSaveFailed(true);
514
+ }
515
+ }, DRAFT_AUTOSAVE_DEBOUNCE_MS);
516
+ return () => window.clearTimeout(timer);
517
+ }, [draftAutosaveReady, draftEligible, definitionId, nodes, edges, triggers, contextSchema, definitionIo, interpolation, errorHandler, draftMetadata, updatedAt, workflowName, description, version, enabled, effectiveFrom, effectiveTo]);
518
+ useEffect(() => {
519
+ if (!draftSavedAt) return;
520
+ const interval = window.setInterval(() => setDraftClock((tick) => tick + 1), DRAFT_SAVED_LABEL_REFRESH_MS);
521
+ return () => window.clearInterval(interval);
522
+ }, [draftSavedAt]);
523
+ const draftSavedLabel = useMemo(() => {
524
+ if (!draftSavedAt) return null;
525
+ return formatRelativeTime(draftSavedAt, { translate: t });
526
+ }, [draftSavedAt, draftClock, t]);
527
+ const handleRestoreDraft = useCallback(() => {
528
+ if (!pendingDraft) return;
529
+ try {
530
+ const draftDefinition = pendingDraft.draft.definition;
531
+ const graph = definitionToGraph(draftDefinition);
532
+ setNodes([...graph.nodes, ...annotationsToNodes(readEditorAnnotations(pendingDraft.draft.metadata ?? null))]);
533
+ setEdges(graph.edges);
534
+ const draftTriggers = pendingDraft.draft.definition.triggers;
535
+ setTriggers(Array.isArray(draftTriggers) ? draftTriggers : []);
536
+ const draftContextSchema = pendingDraft.draft.definition.contextSchema;
537
+ setContextSchema(draftContextSchema ? draftContextSchema : void 0);
538
+ const draftIo = pendingDraft.draft.definition.io;
539
+ setDefinitionIo(draftIo ?? void 0);
540
+ setInterpolation(resolveDefinitionInterpolationMode(pendingDraft.draft.definition));
541
+ const draftErrorHandler = pendingDraft.draft.definition.errorHandler;
542
+ setErrorHandler(draftErrorHandler ?? void 0);
543
+ const restoredMetadata = pendingDraft.draft.metadata ?? null;
544
+ setLoadedMetadata(restoredMetadata);
545
+ if (restoredMetadata) {
546
+ setCategory(typeof restoredMetadata.category === "string" ? restoredMetadata.category : "");
547
+ setTags(Array.isArray(restoredMetadata.tags) ? restoredMetadata.tags.filter((tag) => typeof tag === "string") : []);
548
+ setIcon(typeof restoredMetadata.icon === "string" ? restoredMetadata.icon : "");
549
+ }
550
+ lastPersistedDraftRef.current = stableSerializeDefinition({
551
+ definition: pendingDraft.draft.definition,
552
+ metadata: pendingDraft.draft.metadata ?? null
553
+ });
554
+ resetHistory();
555
+ flash(t("workflows.visualEditor.draft.restored", "Draft restored"), "success");
556
+ } catch (error) {
557
+ logger.error("Error restoring workflow definition draft", { err: error });
558
+ flash(t("workflows.visualEditor.draft.restoreFailed", "Failed to restore draft"), "error");
559
+ } finally {
560
+ setPendingDraft(null);
561
+ setDraftAutosaveReady(true);
562
+ }
563
+ }, [pendingDraft, resetHistory, t]);
564
+ const handleDiscardDraft = useCallback(async () => {
565
+ setPendingDraft(null);
566
+ setDraftAutosaveReady(true);
567
+ if (!definitionId) return;
568
+ try {
569
+ await apiCall(`/api/workflows/definitions/${definitionId}/draft`, { method: "DELETE" });
570
+ } catch (error) {
571
+ logger.error("Error discarding workflow definition draft", { err: error });
572
+ }
573
+ }, [definitionId]);
574
+ const scheduleAutosave = useCallback(() => {
575
+ if (!definitionId || isCodeOnly) return;
576
+ if (autosaveTimerRef.current) clearTimeout(autosaveTimerRef.current);
577
+ autosaveTimerRef.current = setTimeout(() => {
578
+ autosaveTimerRef.current = null;
579
+ void performAutosaveRef.current();
580
+ }, 900);
581
+ }, [definitionId, isCodeOnly]);
582
+ useEffect(() => () => {
583
+ if (autosaveTimerRef.current) clearTimeout(autosaveTimerRef.current);
584
+ }, []);
585
+ const applyHistoryDocument = useCallback((document2) => {
586
+ setNodes(document2.nodes);
587
+ setEdges(document2.edges);
588
+ setLoadedMetadata(document2.metadata);
589
+ if (document2.panel) {
590
+ setTriggers(document2.panel.triggers);
591
+ setContextSchema(document2.panel.contextSchema);
592
+ setDefinitionIo(document2.panel.io);
593
+ setInterpolation(document2.panel.interpolation);
594
+ setErrorHandler(document2.panel.errorHandler);
595
+ }
596
+ scheduleAutosave();
597
+ }, [scheduleAutosave]);
598
+ const handleUndo = useCallback(() => {
134
599
  if (isCodeOnly) return;
600
+ const step = undoEditorHistory(historyRef.current, captureDocument());
601
+ if (!step) {
602
+ flash(t("workflows.visualEditor.history.nothingToUndo", "Nothing to undo"), "info");
603
+ return;
604
+ }
605
+ setHistory(step.state);
606
+ applyHistoryDocument(step.document);
607
+ flash(t("workflows.visualEditor.history.undone", "Undone: {label}", { label: step.label }), "success");
608
+ }, [isCodeOnly, captureDocument, applyHistoryDocument, t]);
609
+ const handleRedo = useCallback(() => {
610
+ if (isCodeOnly) return;
611
+ const step = redoEditorHistory(historyRef.current, captureDocument());
612
+ if (!step) {
613
+ flash(t("workflows.visualEditor.history.nothingToRedo", "Nothing to redo"), "info");
614
+ return;
615
+ }
616
+ setHistory(step.state);
617
+ applyHistoryDocument(step.document);
618
+ flash(t("workflows.visualEditor.history.redone", "Redone: {label}", { label: step.label }), "success");
619
+ }, [isCodeOnly, captureDocument, applyHistoryDocument, t]);
620
+ const localClipboardRef = React.useRef(null);
621
+ const selectedSubgraph = useCallback(() => {
622
+ const selectedIds = nodesRef.current.filter((node) => node.selected).map((node) => node.id);
623
+ return serializeWorkflowSubgraph(nodesRef.current, edgesRef.current, selectedIds);
624
+ }, []);
625
+ const handleCopySelection = useCallback(async () => {
626
+ const payload = selectedSubgraph();
627
+ if (!payload) {
628
+ flash(t("workflows.visualEditor.clipboard.nothingSelected", "Select one or more steps first"), "info");
629
+ return;
630
+ }
631
+ const serialized = stringifyWorkflowSubgraph(payload);
632
+ localClipboardRef.current = serialized;
633
+ try {
634
+ await navigator.clipboard.writeText(serialized);
635
+ flash(
636
+ t("workflows.visualEditor.clipboard.copied", "Copied {count} step(s)", { count: String(payload.steps.length) }),
637
+ "success"
638
+ );
639
+ } catch (error) {
640
+ logger.warn("Workflow subgraph copy fell back to the in-page buffer", { err: error });
641
+ flash(
642
+ t(
643
+ "workflows.visualEditor.clipboard.copyUnavailable",
644
+ "The browser blocked clipboard access, so the selection was kept in this editor only \u2014 pasting into another tab will not work."
645
+ ),
646
+ "warning"
647
+ );
648
+ }
649
+ }, [selectedSubgraph, t]);
650
+ const describeClipboardFailure = useCallback((code) => {
651
+ switch (code) {
652
+ case "invalidJson":
653
+ return t("workflows.visualEditor.clipboard.invalidJson", "The clipboard does not contain valid JSON.");
654
+ case "empty":
655
+ return t("workflows.visualEditor.clipboard.empty", "The copied selection contains no steps.");
656
+ default:
657
+ return t("workflows.visualEditor.clipboard.unsupportedFormat", "The clipboard does not contain a copied workflow selection.");
658
+ }
659
+ }, [t]);
660
+ const spliceSubgraph = useCallback((payload, label) => {
661
+ const result = pasteWorkflowSubgraph(nodesRef.current, edgesRef.current, payload);
662
+ commitHistory(label);
663
+ setNodes(result.nodes);
664
+ setEdges(result.edges);
665
+ scheduleAutosave();
666
+ flash(
667
+ t("workflows.visualEditor.clipboard.pasted", "Pasted {count} step(s)", { count: String(result.pastedNodeIds.length) }),
668
+ "success"
669
+ );
670
+ }, [commitHistory, scheduleAutosave, t]);
671
+ const handlePaste = useCallback(async () => {
672
+ if (isCodeOnly) return;
673
+ let text = null;
674
+ try {
675
+ text = await navigator.clipboard.readText();
676
+ } catch (error) {
677
+ logger.warn("Workflow subgraph paste fell back to the in-page buffer", { err: error });
678
+ text = localClipboardRef.current;
679
+ if (!text) {
680
+ flash(
681
+ t(
682
+ "workflows.visualEditor.clipboard.pasteUnavailable",
683
+ "The browser blocked clipboard access, so there is nothing to paste. Copy a selection in this editor first."
684
+ ),
685
+ "error"
686
+ );
687
+ return;
688
+ }
689
+ }
690
+ const parsed = parseWorkflowSubgraph(text ?? "");
691
+ if (!parsed.ok) {
692
+ flash(describeClipboardFailure(parsed.code), "error");
693
+ return;
694
+ }
695
+ spliceSubgraph(parsed.payload, historyLabels.paste);
696
+ }, [isCodeOnly, describeClipboardFailure, spliceSubgraph, historyLabels.paste, t]);
697
+ const handleDuplicateSelection = useCallback(() => {
698
+ if (isCodeOnly) return;
699
+ const payload = selectedSubgraph();
700
+ if (!payload) {
701
+ flash(t("workflows.visualEditor.clipboard.nothingSelected", "Select one or more steps first"), "info");
702
+ return;
703
+ }
704
+ spliceSubgraph(payload, historyLabels.duplicate);
705
+ }, [isCodeOnly, selectedSubgraph, spliceSubgraph, historyLabels.duplicate, t]);
706
+ const dragBaselineRef = React.useRef(null);
707
+ const handleNodesChange = useCallback((nextNodes, meta) => {
708
+ if (isCodeOnly) return;
709
+ if ((meta?.dragging || meta?.resizing) && !dragBaselineRef.current) dragBaselineRef.current = captureDocument();
710
+ const baseline = dragBaselineRef.current ?? captureDocument();
135
711
  setNodes(nextNodes);
136
- }, [isCodeOnly]);
712
+ if (meta && !meta.persistable) return;
713
+ dragBaselineRef.current = null;
714
+ commitCapturedDocument(baseline, historyLabels.move);
715
+ scheduleAutosave();
716
+ }, [isCodeOnly, captureDocument, commitCapturedDocument, historyLabels.move, scheduleAutosave]);
717
+ const handleAutoArrange = useCallback(() => {
718
+ if (isCodeOnly) return;
719
+ commitHistory(historyLabels.tidy);
720
+ setNodes((nds) => applyAutoLayout(nds, edges));
721
+ scheduleAutosave();
722
+ }, [isCodeOnly, edges, commitHistory, historyLabels.tidy, scheduleAutosave]);
137
723
  const handleEdgesChange = useCallback((nextEdges) => {
138
724
  if (isCodeOnly) return;
139
725
  setEdges(nextEdges);
140
726
  }, [isCodeOnly]);
141
- const handleAddNode = useCallback((nodeType) => {
727
+ const buildPaletteNode = useCallback((nodeType, dropPosition) => ({
728
+ id: generateStepId(nodeType),
729
+ type: nodeType,
730
+ position: resolveNewNodePlacement(nodesRef.current, { dropPosition }),
731
+ data: {
732
+ label: getDefaultLabel(nodeType),
733
+ description: "",
734
+ badge: getDefaultBadge(nodeType),
735
+ status: "pending"
736
+ }
737
+ }), []);
738
+ const handleAddNode = useCallback((nodeType, dropPosition) => {
142
739
  if (isCodeOnly) return;
143
- const newNode = {
144
- id: generateStepId(nodeType),
145
- type: nodeType,
146
- position: {
147
- x: 250 + nodes.length * 50,
148
- y: 100 + nodes.length * 150
149
- },
150
- data: {
151
- label: getDefaultLabel(nodeType),
152
- description: "",
153
- badge: getDefaultBadge(nodeType),
154
- status: "pending"
155
- }
156
- };
740
+ const newNode = buildPaletteNode(nodeType, dropPosition);
741
+ commitHistory(historyLabels.addStep);
157
742
  setNodes((nds) => [...nds, newNode]);
158
- }, [nodes.length, isCodeOnly]);
743
+ scheduleAutosave();
744
+ }, [isCodeOnly, buildPaletteNode, commitHistory, historyLabels.addStep, scheduleAutosave]);
745
+ const handleAddAnnotation = useCallback((kind) => {
746
+ if (isCodeOnly) return;
747
+ const position = resolveNewNodePlacement(nodesRef.current, {});
748
+ const node = kind === "note" ? createNoteNode(position, "") : createGroupNode(position, t("workflows.annotations.group.untitled", "Untitled group"));
749
+ commitHistory(kind === "note" ? historyLabels.addNote : historyLabels.addGroup);
750
+ setNodes((nds) => [...nds, node]);
751
+ setSelectedAnnotation(node);
752
+ setShowAnnotationDialog(true);
753
+ scheduleAutosave();
754
+ }, [isCodeOnly, commitHistory, historyLabels.addNote, historyLabels.addGroup, scheduleAutosave, t]);
755
+ const activityTypeOptions = useActivityTypeOptions();
756
+ const describePaletteDropRejection = useCallback((code) => {
757
+ switch (code) {
758
+ case "dataMappingRoute":
759
+ return t("workflows.visualEditor.palette.rejected.dataMappingRoute", "Data mapping links are edited in the step configuration, not on the canvas.");
760
+ default:
761
+ return t("workflows.visualEditor.palette.rejected.unknownRoute", "That route no longer exists.");
762
+ }
763
+ }, [t]);
764
+ const handleCanvasDrop = useCallback((event) => {
765
+ if (isCodeOnly) return;
766
+ const item = readPaletteDragItem(event.dataTransfer);
767
+ if (!item) return;
768
+ if (item.kind === "step") {
769
+ if (!event.edgeId) {
770
+ handleAddNode(item.nodeType, event.position);
771
+ return;
772
+ }
773
+ const result2 = insertStepOnRoute(
774
+ nodesRef.current,
775
+ edgesRef.current,
776
+ event.edgeId,
777
+ buildPaletteNode(item.nodeType, event.position)
778
+ );
779
+ if (!result2.ok) {
780
+ flash(describePaletteDropRejection(result2.code), "error");
781
+ return;
782
+ }
783
+ commitHistory(historyLabels.insertOnRoute);
784
+ setNodes(result2.nodes);
785
+ setEdges(result2.edges);
786
+ scheduleAutosave();
787
+ flash(t("workflows.visualEditor.palette.insertedOnRoute", "Step inserted on the route"), "success");
788
+ return;
789
+ }
790
+ if (!event.edgeId) {
791
+ flash(t("workflows.visualEditor.palette.dragActionHint", "Drop an action onto a route to add it there"), "info");
792
+ return;
793
+ }
794
+ const activityLabel = activityTypeOptions.find((option) => option.value === item.activityType)?.label ?? item.activityType;
795
+ const result = appendActivityToRoute(edgesRef.current, event.edgeId, {
796
+ activityId: `activity_${Date.now()}`,
797
+ activityName: activityLabel,
798
+ activityType: item.activityType,
799
+ config: {}
800
+ });
801
+ if (!result.ok) {
802
+ flash(describePaletteDropRejection(result.code), "error");
803
+ return;
804
+ }
805
+ commitHistory(historyLabels.addActivity);
806
+ setEdges(result.edges);
807
+ scheduleAutosave();
808
+ flash(t("workflows.visualEditor.palette.activityAdded", "{activity} added to the route", { activity: activityLabel }), "success");
809
+ }, [
810
+ isCodeOnly,
811
+ handleAddNode,
812
+ buildPaletteNode,
813
+ activityTypeOptions,
814
+ describePaletteDropRejection,
815
+ commitHistory,
816
+ historyLabels.insertOnRoute,
817
+ historyLabels.addActivity,
818
+ scheduleAutosave,
819
+ t
820
+ ]);
821
+ const handlePaletteStepDragStart = useCallback((event, nodeType) => {
822
+ writePaletteDragPayload(event.dataTransfer, { kind: "step", nodeType }, NODE_TYPE_LABELS[nodeType]?.title ?? nodeType);
823
+ }, []);
824
+ const handlePaletteActivityDragStart = useCallback((event, activityType, label) => {
825
+ writePaletteDragPayload(event.dataTransfer, { kind: "activity", activityType }, label);
826
+ }, []);
159
827
  const handleNodeClick = useCallback((_event, node) => {
160
828
  if (isCodeOnly) return;
829
+ if (isAnnotationNode(node)) {
830
+ setSelectedAnnotation(node);
831
+ setSelectedNode(null);
832
+ setSelectedEdge(null);
833
+ setShowAnnotationDialog(true);
834
+ return;
835
+ }
161
836
  setSelectedNode(node);
162
837
  setSelectedEdge(null);
838
+ setShowEdgeDialog(false);
163
839
  setShowNodeDialog(true);
164
840
  }, [isCodeOnly]);
165
841
  const handleEdgeClick = useCallback((_event, edge) => {
166
842
  if (isCodeOnly) return;
167
843
  setSelectedEdge(edge);
168
844
  setSelectedNode(null);
845
+ setEdgeDialogFocusFieldId(null);
846
+ setShowNodeDialog(false);
847
+ setShowEdgeDialog(true);
848
+ }, [isCodeOnly]);
849
+ const handleRouteChipOpen = useCallback((edgeId, section) => {
850
+ if (isCodeOnly) return;
851
+ const edge = edgesRef.current.find((candidate) => candidate.id === edgeId);
852
+ if (!edge) return;
853
+ setSelectedEdge(edge);
854
+ setSelectedNode(null);
855
+ setEdgeDialogFocusFieldId(section === "condition" ? "condition" : "activities");
169
856
  setShowEdgeDialog(true);
170
857
  }, [isCodeOnly]);
171
858
  const handleSaveNode = useCallback((nodeId, updates) => {
859
+ commitHistory(historyLabels.editStep);
860
+ if (Object.prototype.hasOwnProperty.call(updates, "decisions")) {
861
+ const previousNode = nodesRef.current.find((node) => node.id === nodeId);
862
+ const previousDecisions = previousNode?.data.decisions;
863
+ const nextDecisions = updates.decisions;
864
+ const previousIds = new Set(
865
+ (previousDecisions ?? []).flatMap(
866
+ (decision) => typeof decision.transitionId === "string" ? [decision.transitionId] : []
867
+ )
868
+ );
869
+ const nextIds = new Set(
870
+ (nextDecisions ?? []).flatMap(
871
+ (decision) => typeof decision.transitionId === "string" ? [decision.transitionId] : []
872
+ )
873
+ );
874
+ setEdges(
875
+ (currentEdges) => currentEdges.map((edge) => {
876
+ if (edge.source !== nodeId) return edge;
877
+ if (nextIds.has(edge.id)) return { ...edge, sourceHandle: edge.id };
878
+ if (previousIds.has(edge.id) && edge.sourceHandle === edge.id) {
879
+ const { sourceHandle: _removed, ...remainingEdge } = edge;
880
+ return remainingEdge;
881
+ }
882
+ return edge;
883
+ })
884
+ );
885
+ }
172
886
  setNodes(
173
887
  (nds) => nds.map(
174
888
  (node) => node.id === nodeId ? { ...node, data: { ...node.data, ...updates } } : node
175
889
  )
176
890
  );
177
891
  flash("Node updated successfully", "success");
178
- }, []);
892
+ }, [commitHistory, historyLabels.editStep]);
893
+ const branchingRoutesValue = useMemo(() => {
894
+ if (!selectedNode || !isBranchingNodeType(selectedNode.type)) return void 0;
895
+ return {
896
+ field: readSwitchField(edges, selectedNode.id),
897
+ routes: readBranchingRoutes(edges, selectedNode.id)
898
+ };
899
+ }, [selectedNode, edges]);
900
+ const handleSaveBranchingRoutes = useCallback((nodeId, value) => {
901
+ const nodeType = nodes.find((node) => node.id === nodeId)?.type;
902
+ commitHistory(historyLabels.branchRoutes);
903
+ setEdges((eds) => nodeType === "switch" ? applySwitchRoutes(eds, nodeId, value) : applyIfElseRoutes(eds, nodeId, value.routes));
904
+ scheduleAutosave();
905
+ }, [nodes, commitHistory, historyLabels.branchRoutes, scheduleAutosave]);
906
+ const routeOrderValue = useMemo(() => {
907
+ if (!selectedNode || isBranchingNodeType(selectedNode.type)) return void 0;
908
+ return readRouteOrder(edges, selectedNode.id);
909
+ }, [selectedNode, edges]);
910
+ const normalizedRouteNodesRef = React.useRef(/* @__PURE__ */ new Set());
911
+ const normalizeRoutesOnFirstEdit = useCallback((nodeId) => {
912
+ if (!canNormalizeRoutePriorities(source)) return;
913
+ if (normalizedRouteNodesRef.current.has(nodeId)) return;
914
+ normalizedRouteNodesRef.current.add(nodeId);
915
+ setEdges((eds) => normalizeRoutePriorities(eds, nodeId));
916
+ }, [source]);
917
+ const handleSaveRouteOrder = useCallback((nodeId, entries) => {
918
+ if (isCodeOnly) return;
919
+ commitHistory(historyLabels.routeOrder);
920
+ normalizeRoutesOnFirstEdit(nodeId);
921
+ setEdges((eds) => applyRouteOrder(eds, nodeId, entries));
922
+ scheduleAutosave();
923
+ }, [isCodeOnly, normalizeRoutesOnFirstEdit, commitHistory, historyLabels.routeOrder, scheduleAutosave]);
179
924
  const handleSaveEdge = useCallback((edgeId, updates) => {
925
+ commitHistory(historyLabels.editRoute);
180
926
  setEdges(
181
927
  (eds) => eds.map(
182
928
  (edge) => edge.id === edgeId ? { ...edge, data: { ...edge.data, ...updates } } : edge
183
929
  )
184
930
  );
185
931
  flash("Transition updated successfully", "success");
186
- }, []);
932
+ }, [commitHistory, historyLabels.editRoute]);
187
933
  const handleDeleteEdge = useCallback(async (edgeId) => {
188
- await performDeleteEdgeFlow(edgeId, {
934
+ const before = captureDocument();
935
+ const deleted = await performDeleteEdgeFlow(edgeId, {
189
936
  confirm,
190
937
  t,
191
938
  setShowEdgeDialog,
@@ -193,9 +940,54 @@ function VisualEditorPage() {
193
940
  setEdges,
194
941
  notifyDeleted: () => flash("Transition deleted successfully", "success")
195
942
  });
196
- }, [confirm, t]);
943
+ if (deleted) commitCapturedDocument(before, historyLabels.deleteRoute);
944
+ }, [confirm, t, captureDocument, commitCapturedDocument, historyLabels.deleteRoute]);
945
+ const handleSaveAnnotation = useCallback((nodeId, updates) => {
946
+ if (isCodeOnly) return;
947
+ commitHistory(historyLabels.editAnnotation);
948
+ setNodes((nds) => updateAnnotationNode(nds, nodeId, updates));
949
+ scheduleAutosave();
950
+ }, [isCodeOnly, commitHistory, historyLabels.editAnnotation, scheduleAutosave]);
951
+ const handleDeleteAnnotation = useCallback(async (nodeId) => {
952
+ if (isCodeOnly) return;
953
+ const before = captureDocument();
954
+ setShowAnnotationDialog(false);
955
+ setSelectedAnnotation(null);
956
+ const confirmed = await confirm({
957
+ title: t("workflows.annotations.confirmDeleteTitle", "Delete annotation?"),
958
+ text: t("workflows.annotations.confirmDeleteText", "The note or group is removed from the canvas. Nothing the workflow executes changes."),
959
+ variant: "destructive"
960
+ });
961
+ if (!confirmed) return;
962
+ commitCapturedDocument(before, historyLabels.deleteAnnotation);
963
+ setNodes((nds) => removeAnnotationNode(nds, nodeId));
964
+ scheduleAutosave();
965
+ }, [isCodeOnly, captureDocument, commitCapturedDocument, historyLabels.deleteAnnotation, confirm, scheduleAutosave, t]);
966
+ const handleToggleGroupCollapsed = useCallback((nodeId) => {
967
+ if (isCodeOnly) return;
968
+ const node = nodesRef.current.find((candidate) => candidate.id === nodeId);
969
+ if (!node) return;
970
+ const collapsed = node.data?.collapsed === true;
971
+ commitHistory(historyLabels.toggleGroup);
972
+ setNodes((nds) => updateAnnotationNode(nds, nodeId, { collapsed: !collapsed }));
973
+ scheduleAutosave();
974
+ }, [isCodeOnly, commitHistory, historyLabels.toggleGroup, scheduleAutosave]);
975
+ useEffect(() => {
976
+ const onToggle = (event) => {
977
+ const nodeId = event.detail?.nodeId;
978
+ if (typeof nodeId === "string") handleToggleGroupCollapsed(nodeId);
979
+ };
980
+ window.addEventListener(WORKFLOW_GROUP_TOGGLE_EVENT, onToggle);
981
+ return () => window.removeEventListener(WORKFLOW_GROUP_TOGGLE_EVENT, onToggle);
982
+ }, [handleToggleGroupCollapsed]);
197
983
  const handleDeleteNode = useCallback(async (nodeId) => {
198
- await performDeleteNodeFlow(nodeId, {
984
+ const annotation = nodesRef.current.find((candidate) => candidate.id === nodeId && isAnnotationNode(candidate));
985
+ if (annotation) {
986
+ await handleDeleteAnnotation(nodeId);
987
+ return;
988
+ }
989
+ const before = captureDocument();
990
+ const deleted = await performDeleteNodeFlow(nodeId, {
199
991
  nodes,
200
992
  confirm,
201
993
  t,
@@ -205,100 +997,569 @@ function VisualEditorPage() {
205
997
  setEdges,
206
998
  notifyDeleted: () => flash("Step deleted successfully", "success")
207
999
  });
208
- }, [confirm, nodes, t]);
1000
+ if (deleted) commitCapturedDocument(before, historyLabels.deleteStep);
1001
+ }, [confirm, nodes, t, captureDocument, commitCapturedDocument, historyLabels.deleteStep, handleDeleteAnnotation]);
1002
+ const handleConvertNodeType = useCallback(async (nodeId, targetType) => {
1003
+ if (isCodeOnly) return;
1004
+ const node = nodesRef.current.find((candidate) => candidate.id === nodeId);
1005
+ if (!node) return;
1006
+ const before = captureDocument();
1007
+ const result = convertStepType({ type: node.type, data: node.data }, targetType);
1008
+ if (!result.ok) {
1009
+ flash(t("workflows.stepConversion.unavailable", "This step type cannot be changed."), "error");
1010
+ return;
1011
+ }
1012
+ setShowNodeDialog(false);
1013
+ setSelectedNode(null);
1014
+ const confirmed = await confirm({
1015
+ title: t("workflows.stepConversion.confirmTitle", "Change step type?"),
1016
+ text: result.quarantined.length > 0 ? t(
1017
+ "workflows.stepConversion.confirmWithQuarantine",
1018
+ "The step keeps its name, position and routes. {count} setting(s) the new type cannot execute move to Unmapped configuration: {keys}",
1019
+ { count: String(result.quarantined.length), keys: result.quarantined.join(", ") }
1020
+ ) : t("workflows.stepConversion.confirm", "The step keeps its name, position and routes.")
1021
+ });
1022
+ if (!confirmed) return;
1023
+ commitCapturedDocument(before, historyLabels.convert);
1024
+ setNodes(
1025
+ (nds) => nds.map(
1026
+ (candidate) => candidate.id === nodeId ? { ...candidate, type: result.type, data: { ...result.data, badge: getBadgeForNodeType(result.type) } } : candidate
1027
+ )
1028
+ );
1029
+ scheduleAutosave();
1030
+ flash(
1031
+ result.quarantined.length > 0 ? t("workflows.stepConversion.convertedWithQuarantine", "Step type changed; {count} setting(s) parked as unmapped configuration", { count: String(result.quarantined.length) }) : t("workflows.stepConversion.converted", "Step type changed"),
1032
+ result.quarantined.length > 0 ? "warning" : "success"
1033
+ );
1034
+ }, [isCodeOnly, confirm, captureDocument, commitCapturedDocument, historyLabels.convert, scheduleAutosave, t]);
1035
+ useEffect(() => {
1036
+ if (isCodeOnly) return;
1037
+ const onNodeDelete = (event) => {
1038
+ const nodeId = event.detail?.nodeId;
1039
+ if (typeof nodeId === "string") void handleDeleteNode(nodeId);
1040
+ };
1041
+ window.addEventListener(WORKFLOW_NODE_DELETE_EVENT, onNodeDelete);
1042
+ return () => window.removeEventListener(WORKFLOW_NODE_DELETE_EVENT, onNodeDelete);
1043
+ }, [isCodeOnly, handleDeleteNode]);
1044
+ useEffect(() => {
1045
+ const onChipOpen = (event) => {
1046
+ const detail = event.detail;
1047
+ if (detail?.edgeId) handleRouteChipOpen(detail.edgeId, detail.section);
1048
+ };
1049
+ window.addEventListener(WORKFLOW_ROUTE_CHIP_EVENT, onChipOpen);
1050
+ return () => window.removeEventListener(WORKFLOW_ROUTE_CHIP_EVENT, onChipOpen);
1051
+ }, [handleRouteChipOpen]);
1052
+ useEffect(() => {
1053
+ const onActivityOpen = (event) => {
1054
+ if (isCodeOnly) return;
1055
+ const detail = event.detail;
1056
+ if (detail?.edgeId && detail?.activityId) {
1057
+ setActivityEdit({ edgeId: detail.edgeId, activityId: detail.activityId });
1058
+ }
1059
+ };
1060
+ window.addEventListener(WORKFLOW_ROUTE_ACTIVITY_EVENT, onActivityOpen);
1061
+ return () => window.removeEventListener(WORKFLOW_ROUTE_ACTIVITY_EVENT, onActivityOpen);
1062
+ }, [isCodeOnly]);
209
1063
  const handleConnect = useCallback((connection) => {
210
- const newEdge = {
211
- id: generateTransitionId(connection.source, connection.target),
1064
+ const classification = classifyConnection(connection);
1065
+ if (classification.kind === "data-ignored") {
1066
+ return;
1067
+ }
1068
+ if (classification.kind === "data-mapping") {
1069
+ const { targetNodeId, childPortKey, parentPath } = classification;
1070
+ commitHistory(historyLabels.connect);
1071
+ setNodes((nds) => applyInputMappingToNodes(nds, targetNodeId, childPortKey, parentPath));
1072
+ const dataEdge = buildDataMappingEdge(connection, childPortKey);
1073
+ setEdges((eds) => appendWorkflowEdge(eds.filter((e) => e.id !== dataEdge.id), dataEdge));
1074
+ return;
1075
+ }
1076
+ const routeKind = findRouteKindDescriptorForHandle(connection.sourceHandle);
1077
+ const sourceNode = nodesRef.current.find((node) => node.id === connection.source);
1078
+ const decisions = sourceNode?.data.decisions;
1079
+ const decisionTransitionId = isDecisionSourceHandle(decisions, connection.sourceHandle) ? connection.sourceHandle : null;
1080
+ if (decisionTransitionId && edgesRef.current.some(
1081
+ (edge) => edge.source === connection.source && (edge.id === decisionTransitionId || edge.sourceHandle === decisionTransitionId)
1082
+ )) {
1083
+ return;
1084
+ }
1085
+ const routeData = routeKind ? {
1086
+ kind: routeKind.kind,
1087
+ ...routeKind.discriminatorFields({
1088
+ sourceHandle: connection.sourceHandle
1089
+ })
1090
+ } : {};
1091
+ const newEdge = buildWorkflowRouteEdge({
1092
+ id: decisionTransitionId ?? generateTransitionId(),
212
1093
  source: connection.source,
213
1094
  target: connection.target,
214
- type: "smoothstep",
1095
+ sourceHandle: routeKind ? connection.sourceHandle : decisionTransitionId,
215
1096
  data: {
216
1097
  trigger: "auto",
217
1098
  preConditions: [],
218
1099
  postConditions: [],
219
1100
  activities: [],
220
- label: ""
1101
+ label: "",
1102
+ ...routeData
221
1103
  }
222
- };
1104
+ });
1105
+ commitHistory(historyLabels.connect);
223
1106
  setEdges((eds) => appendWorkflowEdge(eds, newEdge));
224
- }, []);
225
- const handleValidate = useCallback(() => {
1107
+ }, [commitHistory, historyLabels.connect]);
1108
+ const describeReattachRejection = useCallback((rejection) => {
1109
+ switch (rejection.code) {
1110
+ case "selfLoop":
1111
+ return t("workflows.reattach.rejected.selfLoop", "A route cannot start and end on the same step.");
1112
+ case "duplicateRoute":
1113
+ return t("workflows.reattach.rejected.duplicateRoute", "A route already connects those two steps.");
1114
+ case "dataMappingRoute":
1115
+ return t("workflows.reattach.rejected.dataMappingRoute", "Data mapping links are edited in the step configuration, not on the canvas.");
1116
+ case "errorHandleUnsupported":
1117
+ return t("workflows.reattach.rejected.errorHandleUnsupported", "An error route can only start at a step that can fail.");
1118
+ case "kindHandleUnsupported":
1119
+ return t("workflows.reattach.rejected.kindHandleUnsupported", "This route cannot start at that type of step.");
1120
+ case "graphInvalid":
1121
+ return t("workflows.reattach.rejected.graphInvalid", "That target would break the workflow: {reason}", { reason: rejection.detail ?? "" });
1122
+ case "forkJoinInvalid":
1123
+ return t("workflows.reattach.rejected.forkJoinInvalid", "That target would break the parallel branch structure: {reason}", { reason: rejection.detail ?? "" });
1124
+ default:
1125
+ return t("workflows.reattach.rejected.unsupported", "That route cannot be re-targeted.");
1126
+ }
1127
+ }, [t]);
1128
+ const handleReconnect = useCallback((oldEdge, connection) => {
1129
+ if (isCodeOnly) return;
1130
+ const result = reattachWorkflowEdge(nodesRef.current, edgesRef.current, oldEdge.id, connection);
1131
+ if (!result.ok) {
1132
+ flash(describeReattachRejection(result), "error");
1133
+ return;
1134
+ }
1135
+ commitHistory(historyLabels.reattach);
1136
+ setEdges(result.edges);
1137
+ scheduleAutosave();
1138
+ flash(t("workflows.reattach.retargeted", "Route re-targeted"), "success");
1139
+ }, [isCodeOnly, describeReattachRejection, commitHistory, historyLabels.reattach, scheduleAutosave, t]);
1140
+ const errorHandlerStepOptions = useMemo(
1141
+ () => nodes.filter((node) => node.type !== "start" && node.type !== "end").map((node) => ({
1142
+ stepId: node.id,
1143
+ label: typeof node.data?.label === "string" && node.data.label ? node.data.label : node.id
1144
+ })),
1145
+ [nodes]
1146
+ );
1147
+ const { events: availableEvents } = useAvailableEvents();
1148
+ const triggerPayloadContracts = useMemo(
1149
+ () => buildTriggerPayloadContracts(triggers, availableEvents),
1150
+ [triggers, availableEvents]
1151
+ );
1152
+ const dialogLedger = useMemo(() => {
1153
+ if (!showNodeDialog && !showEdgeDialog) return null;
1154
+ try {
1155
+ const definitionData = buildDefinitionPayload({
1156
+ graphDefinition: graphToDefinition(nodes, edges),
1157
+ triggers,
1158
+ contextSchema,
1159
+ io: definitionIo
1160
+ });
1161
+ return computeClientContextLedger(definitionData, triggerPayloadContracts);
1162
+ } catch {
1163
+ return null;
1164
+ }
1165
+ }, [showNodeDialog, showEdgeDialog, nodes, edges, triggers, contextSchema, definitionIo, triggerPayloadContracts]);
1166
+ const nodeDialogLedgerEntries = useMemo(() => {
1167
+ if (!dialogLedger || !selectedNode) return void 0;
1168
+ const stepEntries = dialogLedger.steps[selectedNode.id]?.entries;
1169
+ if (stepEntries && stepEntries.length > 0) return stepEntries;
1170
+ const startNode = nodes.find((candidate) => candidate.type === "start");
1171
+ const startEntries = startNode ? dialogLedger.steps[startNode.id]?.entries : void 0;
1172
+ return startEntries && startEntries.length > 0 ? startEntries : stepEntries;
1173
+ }, [dialogLedger, nodes, selectedNode]);
1174
+ const editorSamples = useMemo(() => {
1175
+ const editorValue = loadedMetadata?.editor;
1176
+ if (!editorValue || typeof editorValue !== "object" || Array.isArray(editorValue)) return void 0;
1177
+ const samplesValue = editorValue.samples;
1178
+ if (!samplesValue || typeof samplesValue !== "object" || Array.isArray(samplesValue)) return void 0;
1179
+ return samplesValue;
1180
+ }, [loadedMetadata]);
1181
+ const handlePinSample = useCallback((stepId, data) => {
1182
+ commitHistory(historyLabels.pinSample);
1183
+ setLoadedMetadata((previous) => {
1184
+ const base = { ...previous ?? {} };
1185
+ const editorValue = base.editor;
1186
+ const editor = editorValue && typeof editorValue === "object" && !Array.isArray(editorValue) ? { ...editorValue } : {};
1187
+ const samplesValue = editor.samples;
1188
+ const samples = samplesValue && typeof samplesValue === "object" && !Array.isArray(samplesValue) ? { ...samplesValue } : {};
1189
+ samples[stepId] = { pinnedAt: (/* @__PURE__ */ new Date()).toISOString(), source: "test", data };
1190
+ editor.samples = samples;
1191
+ base.editor = editor;
1192
+ return base;
1193
+ });
1194
+ flash(t("workflows.testStep.pinnedFlash", "Sample pinned \u2014 it is stored with the definition on save"), "success");
1195
+ }, [commitHistory, historyLabels.pinSample, t]);
1196
+ const handleUnpinSample = useCallback((stepId) => {
1197
+ commitHistory(historyLabels.unpinSample);
1198
+ setLoadedMetadata((previous) => {
1199
+ if (!previous) return previous;
1200
+ const editorValue = previous.editor;
1201
+ if (!editorValue || typeof editorValue !== "object" || Array.isArray(editorValue)) return previous;
1202
+ const editor = { ...editorValue };
1203
+ const samplesValue = editor.samples;
1204
+ if (!samplesValue || typeof samplesValue !== "object" || Array.isArray(samplesValue)) return previous;
1205
+ const samples = { ...samplesValue };
1206
+ if (!(stepId in samples)) return previous;
1207
+ delete samples[stepId];
1208
+ if (Object.keys(samples).length > 0) editor.samples = samples;
1209
+ else delete editor.samples;
1210
+ const base = { ...previous };
1211
+ if (Object.keys(editor).length > 0) base.editor = editor;
1212
+ else delete base.editor;
1213
+ return Object.keys(base).length > 0 ? base : null;
1214
+ });
1215
+ }, [commitHistory, historyLabels.unpinSample]);
1216
+ const startFixtures = useMemo(() => {
1217
+ const editorValue = loadedMetadata?.editor;
1218
+ if (!editorValue || typeof editorValue !== "object" || Array.isArray(editorValue)) return void 0;
1219
+ const fixturesValue = editorValue.fixtures;
1220
+ if (!fixturesValue || typeof fixturesValue !== "object" || Array.isArray(fixturesValue)) return void 0;
1221
+ return fixturesValue;
1222
+ }, [loadedMetadata]);
1223
+ const startFixtureList = useMemo(() => listStartFixtures(startFixtures), [startFixtures]);
1224
+ const handleSaveFixture = useCallback(() => {
1225
+ let context;
1226
+ try {
1227
+ const parsed = startContext.trim() ? JSON.parse(startContext) : {};
1228
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
1229
+ throw new Error("[internal] context must be a JSON object");
1230
+ }
1231
+ context = parsed;
1232
+ } catch {
1233
+ flash(t("workflows.startInstance.invalidJson"), "error");
1234
+ return;
1235
+ }
1236
+ const result = upsertStartFixture(startFixtures, fixtureName, context, (/* @__PURE__ */ new Date()).toISOString());
1237
+ if (!result.ok) {
1238
+ flash(t(`workflows.fixtures.errors.${result.reason}`), "error");
1239
+ return;
1240
+ }
1241
+ commitHistory(historyLabels.saveFixture);
1242
+ setLoadedMetadata((previous) => {
1243
+ const base = { ...previous ?? {} };
1244
+ const editorValue = base.editor;
1245
+ const editor = editorValue && typeof editorValue === "object" && !Array.isArray(editorValue) ? { ...editorValue } : {};
1246
+ editor.fixtures = result.fixtures;
1247
+ base.editor = editor;
1248
+ return base;
1249
+ });
1250
+ setFixtureName("");
1251
+ flash(t("workflows.fixtures.saved"), "success");
1252
+ }, [startContext, startFixtures, fixtureName, commitHistory, historyLabels.saveFixture, t]);
1253
+ const handleApplyFixture = useCallback((name) => {
1254
+ const fixture = startFixtures?.[name];
1255
+ if (!fixture) return;
1256
+ setStartContext(JSON.stringify(fixture.context, null, 2));
1257
+ setFixtureName(fixture.name);
1258
+ }, [startFixtures]);
1259
+ const handleDeleteFixture = useCallback((name) => {
1260
+ commitHistory(historyLabels.deleteFixture);
1261
+ setLoadedMetadata((previous) => {
1262
+ if (!previous) return previous;
1263
+ const editorValue = previous.editor;
1264
+ if (!editorValue || typeof editorValue !== "object" || Array.isArray(editorValue)) return previous;
1265
+ const editor = { ...editorValue };
1266
+ const next = removeStartFixture(editor.fixtures, name);
1267
+ if (Object.keys(next).length > 0) editor.fixtures = next;
1268
+ else delete editor.fixtures;
1269
+ const base = { ...previous };
1270
+ if (Object.keys(editor).length > 0) base.editor = editor;
1271
+ else delete base.editor;
1272
+ return Object.keys(base).length > 0 ? base : null;
1273
+ });
1274
+ }, [commitHistory, historyLabels.deleteFixture]);
1275
+ const edgeDialogLedgerEntries = useMemo(
1276
+ () => dialogLedger && selectedEdge ? dialogLedger.steps[selectedEdge.target]?.entries : void 0,
1277
+ [dialogLedger, selectedEdge]
1278
+ );
1279
+ const translateIssue = useCallback(
1280
+ (key, fallback, params) => t(key, fallback, params),
1281
+ [t]
1282
+ );
1283
+ const evaluateWorkflowIssues = useCallback(() => {
226
1284
  const graphErrors = validateWorkflowGraph(nodes, edges);
227
- const allErrors = [...graphErrors];
1285
+ let zodIssues = [];
1286
+ let configWarnings = [];
1287
+ let schemaFailureMessage = null;
1288
+ let flowLogicDefinition = null;
1289
+ let flowLogicLedger;
228
1290
  try {
229
1291
  const definitionData = graphToDefinition(nodes, edges, { includePositions: true });
230
1292
  const result = workflowDefinitionDataSchema.safeParse(definitionData);
231
1293
  if (!result.success) {
232
- result.error.issues.forEach((issue) => {
233
- allErrors.push({
234
- type: "error",
235
- message: `Schema validation: ${issue.path.join(".")} - ${issue.message}`
236
- });
237
- });
1294
+ zodIssues = result.error.issues;
238
1295
  }
1296
+ const ledgerDefinition = buildDefinitionPayload({ graphDefinition: definitionData, triggers, contextSchema, io: definitionIo, interpolation, errorHandler });
1297
+ flowLogicDefinition = ledgerDefinition;
1298
+ flowLogicLedger = computeClientContextLedger(ledgerDefinition, triggerPayloadContracts);
1299
+ configWarnings = [
1300
+ ...collectActivityConfigWarnings(definitionData),
1301
+ ...collectContextRefWarnings(ledgerDefinition, t, triggerPayloadContracts)
1302
+ ];
239
1303
  } catch (error) {
240
- allErrors.push({
241
- type: "error",
242
- message: `Schema validation failed: ${error instanceof Error ? error.message : "Unknown error"}`
1304
+ schemaFailureMessage = error instanceof Error ? error.message : String(error);
1305
+ }
1306
+ const issues = collectValidationIssues({
1307
+ graphErrors,
1308
+ zodIssues,
1309
+ configWarnings,
1310
+ nodes,
1311
+ edges,
1312
+ definition: flowLogicDefinition,
1313
+ ledger: flowLogicLedger,
1314
+ translate: translateIssue
1315
+ });
1316
+ if (schemaFailureMessage) {
1317
+ issues.unshift({
1318
+ id: "schema-exception",
1319
+ severity: "error",
1320
+ message: t("workflows.visualEditor.problems.schemaValidationFailed", "Schema validation failed: {message}", { message: schemaFailureMessage })
243
1321
  });
244
1322
  }
245
- if (allErrors.length === 0) {
246
- flash("Validation passed! Your workflow is valid and ready to save.", "success");
1323
+ return issues;
1324
+ }, [nodes, edges, triggers, contextSchema, definitionIo, interpolation, errorHandler, triggerPayloadContracts, translateIssue, t]);
1325
+ const runProblemsPass = useCallback(() => {
1326
+ const issues = evaluateWorkflowIssues();
1327
+ setProblems(issues);
1328
+ if (issues.length === 0) {
1329
+ setShowProblems(false);
1330
+ flash(t("workflows.visualEditor.problems.validationPassed", "Validation passed! Your workflow is valid and ready to save."), "success");
247
1331
  } else {
248
- const firstError = allErrors[0];
249
- const errorCount = allErrors.length;
250
- const message = errorCount > 1 ? `${firstError.message} (and ${errorCount - 1} more ${errorCount === 2 ? "issue" : "issues"})` : firstError.message;
251
- flash(message, firstError.type === "error" ? "error" : "warning");
1332
+ setShowProblems(true);
1333
+ setProblemsCollapsed(false);
1334
+ const { errors, warnings } = countIssuesBySeverity(issues);
1335
+ flash(
1336
+ t("workflows.visualEditor.problems.summary", "Validation found {errors} error(s) and {warnings} warning(s).", { errors, warnings }),
1337
+ errors > 0 ? "error" : "warning"
1338
+ );
252
1339
  }
253
- }, [nodes, edges]);
1340
+ }, [evaluateWorkflowIssues, t]);
1341
+ const handleValidate = runProblemsPass;
1342
+ const codeViewJson = useMemo(() => {
1343
+ if (!showCodeView) return "";
1344
+ try {
1345
+ const definitionData = buildDefinitionPayload({
1346
+ graphDefinition: graphToDefinition(nodes, edges, { includePositions: true }),
1347
+ triggers,
1348
+ contextSchema,
1349
+ io: definitionIo,
1350
+ interpolation,
1351
+ errorHandler
1352
+ });
1353
+ return JSON.stringify(definitionData, null, 2);
1354
+ } catch (error) {
1355
+ return t(
1356
+ "workflows.visualEditor.codeView.assemblyFailed",
1357
+ "The definition could not be assembled: {message}",
1358
+ { message: error instanceof Error ? error.message : String(error) }
1359
+ );
1360
+ }
1361
+ }, [showCodeView, nodes, edges, triggers, contextSchema, definitionIo, interpolation, errorHandler, t]);
1362
+ const codeViewIssues = useMemo(
1363
+ () => showCodeView ? evaluateWorkflowIssues() : [],
1364
+ [showCodeView, evaluateWorkflowIssues]
1365
+ );
1366
+ const [codeDraft, setCodeDraft] = useState(null);
1367
+ const codeDraftText = codeDraft ?? codeViewJson;
1368
+ const definitionApplyGate = useMemo(() => ({
1369
+ parseDefinition: (parsed) => {
1370
+ const result = workflowDefinitionDataSchema.safeParse(parsed);
1371
+ if (result.success) return { ok: true, definition: result.data };
1372
+ return {
1373
+ ok: false,
1374
+ messages: result.error.issues.map((issue) => `${issue.path.join(".") || "(root)"}: ${issue.message}`)
1375
+ };
1376
+ },
1377
+ // Warnings never block, exactly as they never block a Save; only graph
1378
+ // ERRORS do, because a canvas holding a graph the engine rejects is worse
1379
+ // than no apply — the author has lost the text they typed.
1380
+ // `autoLayout: false` on purpose: whether a definition is VALID cannot
1381
+ // depend on running a layout engine over it, and skipping dagre keeps this
1382
+ // memo cheap enough to run on every keystroke.
1383
+ validateGraph: (definition) => {
1384
+ const graph = definitionToGraph(definition, { autoLayout: false });
1385
+ return validateWorkflowGraph(graph.nodes, graph.edges).filter((issue) => issue.type === "error").map((issue) => issue.message);
1386
+ }
1387
+ }), []);
1388
+ const codeApplyDecision = useMemo(
1389
+ () => evaluateCodeViewDraft({
1390
+ draftText: codeDraftText,
1391
+ canvasText: codeViewJson,
1392
+ ...definitionApplyGate
1393
+ }),
1394
+ [codeDraftText, codeViewJson, definitionApplyGate]
1395
+ );
1396
+ const codeDraftStatus = useMemo(() => describeCodeViewDraft(codeApplyDecision), [codeApplyDecision]);
1397
+ const codeIssueLocations = useMemo(() => {
1398
+ if (!showCodeView) return { severityByLine: /* @__PURE__ */ new Map(), lineByIssueId: /* @__PURE__ */ new Map() };
1399
+ let parsed;
1400
+ try {
1401
+ parsed = JSON.parse(codeDraftText);
1402
+ } catch {
1403
+ return { severityByLine: /* @__PURE__ */ new Map(), lineByIssueId: /* @__PURE__ */ new Map() };
1404
+ }
1405
+ const locations = locateDefinitionJsonEntities(codeDraftText, parsed);
1406
+ const located = locateIssues(codeViewIssues, locations);
1407
+ return {
1408
+ severityByLine: severityByLine(located),
1409
+ lineByIssueId: new Map(located.map((issue) => [issue.issueId, issue.line]))
1410
+ };
1411
+ }, [showCodeView, codeDraftText, codeViewIssues]);
1412
+ const applyDefinitionDocument = useCallback((definition, label) => {
1413
+ commitHistory(label);
1414
+ const graph = definitionToGraph(definition, { autoLayout: true });
1415
+ setNodes(graph.nodes);
1416
+ setEdges(graph.edges);
1417
+ setTriggers(definition.triggers ?? []);
1418
+ setContextSchema(definition.contextSchema);
1419
+ setDefinitionIo(definition.io);
1420
+ setInterpolation(definition.interpolation);
1421
+ setErrorHandler(definition.errorHandler);
1422
+ scheduleAutosave();
1423
+ }, [commitHistory, scheduleAutosave]);
1424
+ const handleApplyCodeDraft = useCallback(() => {
1425
+ if (!codeApplyDecision.ok) return;
1426
+ applyDefinitionDocument(codeApplyDecision.definition, historyLabels.applyCode);
1427
+ setCodeDraft(null);
1428
+ flash(t("workflows.visualEditor.codeView.applied"), "success");
1429
+ }, [codeApplyDecision, applyDefinitionDocument, historyLabels.applyCode, t]);
1430
+ const [showAiDraft, setShowAiDraft] = useState(false);
1431
+ const [problemsPassToken, setProblemsPassToken] = useState(0);
1432
+ const runProblemsPassRef = React.useRef(runProblemsPass);
1433
+ runProblemsPassRef.current = runProblemsPass;
1434
+ useEffect(() => {
1435
+ if (problemsPassToken === 0) return;
1436
+ runProblemsPassRef.current();
1437
+ }, [problemsPassToken]);
1438
+ const handleApplyAiDraft = useCallback((input) => {
1439
+ const decision = evaluateCodeViewDraft({
1440
+ // Serialized so the AI path and the Code view path are literally the same
1441
+ // call. It also proves the draft survives the JSON round trip the Code
1442
+ // view and every save will put it through.
1443
+ draftText: JSON.stringify(input.definition, null, 2),
1444
+ ...definitionApplyGate
1445
+ });
1446
+ if (!decision.ok) {
1447
+ switch (decision.reason) {
1448
+ case "parseError":
1449
+ return { reason: "parseError", messages: [decision.message] };
1450
+ case "schemaError":
1451
+ case "graphError":
1452
+ return { reason: decision.reason, messages: decision.messages };
1453
+ default:
1454
+ return {
1455
+ reason: "schemaError",
1456
+ messages: [t("workflows.aiDraft.refusedUnknown", "The generated definition was refused.")]
1457
+ };
1458
+ }
1459
+ }
1460
+ applyDefinitionDocument(
1461
+ decision.definition,
1462
+ buildAiCheckpointLabel(input.prompt, {
1463
+ prefix: t("workflows.visualEditor.history.aiDraft", "AI draft")
1464
+ })
1465
+ );
1466
+ setProblemsPassToken((token) => token + 1);
1467
+ flash(
1468
+ t("workflows.aiDraft.applied", "AI draft applied. Review the problems, then save when you are happy with it."),
1469
+ "success"
1470
+ );
1471
+ return null;
1472
+ }, [definitionApplyGate, applyDefinitionDocument, t]);
1473
+ const handleRevertCodeDraft = useCallback(() => {
1474
+ setCodeDraft(null);
1475
+ }, []);
1476
+ const handleCloseCodeView = useCallback(() => {
1477
+ setCodeDraft(null);
1478
+ setShowCodeView(false);
1479
+ }, []);
1480
+ const handleCopyDefinitionJson = useCallback(async () => {
1481
+ try {
1482
+ await navigator.clipboard.writeText(codeViewJson);
1483
+ flash(t("workflows.visualEditor.codeView.copied", "Definition JSON copied"), "success");
1484
+ } catch (error) {
1485
+ logger.warn("Workflow definition JSON copy was blocked by the browser", { err: error });
1486
+ flash(
1487
+ t(
1488
+ "workflows.visualEditor.codeView.copyUnavailable",
1489
+ "The browser blocked clipboard access, so nothing was copied \u2014 select the JSON and copy it manually."
1490
+ ),
1491
+ "warning"
1492
+ );
1493
+ }
1494
+ }, [codeViewJson, t]);
1495
+ const handleProblemClick = useCallback((issue) => {
1496
+ if (!issue.nodeId && !issue.edgeId) return;
1497
+ focusRequestRef.current += 1;
1498
+ setFocusTarget({
1499
+ ...issue.nodeId ? { nodeId: issue.nodeId } : { edgeId: issue.edgeId },
1500
+ requestId: focusRequestRef.current
1501
+ });
1502
+ }, []);
254
1503
  const handleSave = useCallback(async () => {
255
1504
  if (!workflowId || !workflowName) {
256
- flash("Workflow ID and Name are required fields", "error");
257
- return;
258
- }
259
- const errors = validateWorkflowGraph(nodes, edges);
260
- const criticalErrors = errors.filter((e) => e.type === "error");
261
- if (criticalErrors.length > 0) {
262
- flash(`Cannot save: ${criticalErrors.length} validation error(s) found. Please fix them first.`, "error");
1505
+ setMetadataOpen(true);
1506
+ flash(t("workflows.visualEditor.metadata.requiredFields", "Workflow ID and name are required."), "error");
263
1507
  return;
264
1508
  }
265
- const graphDefinition = graphToDefinition(nodes, edges, { includePositions: true });
266
- const definitionData = {
267
- ...graphDefinition,
268
- triggers: triggers.length > 0 ? triggers : void 0
269
- };
1509
+ const graphErrors = validateWorkflowGraph(nodes, edges);
1510
+ const definitionData = buildDefinitionPayload({
1511
+ graphDefinition: graphToDefinition(nodes, edges, { includePositions: true }),
1512
+ triggers,
1513
+ contextSchema,
1514
+ io: definitionIo,
1515
+ interpolation,
1516
+ errorHandler
1517
+ });
270
1518
  const schemaResult = workflowDefinitionDataSchema.safeParse(definitionData);
271
- if (!schemaResult.success) {
272
- const issues = schemaResult.error.issues;
273
- const described = issues.slice(0, 3).map((issue) => `${humanizeDefinitionIssuePath(issue.path)}: ${issue.message}`);
274
- const suffix = issues.length > described.length ? ` (+${issues.length - described.length} more)` : "";
275
- flash(`Cannot save \u2014 ${described.join("; ")}${suffix}`, "error");
1519
+ const issues = collectValidationIssues({
1520
+ graphErrors,
1521
+ zodIssues: schemaResult.success ? [] : schemaResult.error.issues,
1522
+ configWarnings: [
1523
+ ...collectActivityConfigWarnings(definitionData),
1524
+ ...collectContextRefWarnings(definitionData, t, triggerPayloadContracts)
1525
+ ],
1526
+ nodes,
1527
+ edges,
1528
+ definition: definitionData,
1529
+ ledger: computeClientContextLedger(definitionData, triggerPayloadContracts),
1530
+ translate: translateIssue
1531
+ });
1532
+ setProblems(issues);
1533
+ const { errors } = countIssuesBySeverity(issues);
1534
+ if (errors > 0) {
1535
+ setShowProblems(true);
1536
+ setProblemsCollapsed(false);
1537
+ flash(t("workflows.visualEditor.problems.saveBlocked", "Cannot save: {count} validation error(s) found. Please fix them first.", { count: errors }), "error");
276
1538
  return;
277
1539
  }
278
1540
  setIsSaving(true);
279
1541
  try {
280
- const metadata2 = {};
281
- if (category) metadata2.category = category;
282
- if (tags.length > 0) metadata2.tags = tags;
283
- if (icon) metadata2.icon = icon;
1542
+ const metadataPayload = buildMetadataPayload({ loadedMetadata, tags, category, icon, definition: definitionData, annotations });
284
1543
  const isUpdate = !!definitionId;
285
1544
  let result;
1545
+ let updateBody = null;
286
1546
  if (isUpdate) {
1547
+ updateBody = {
1548
+ workflowName,
1549
+ description: description || null,
1550
+ version,
1551
+ definition: definitionData,
1552
+ metadata: metadataPayload,
1553
+ enabled,
1554
+ effectiveFrom: effectiveFrom || null,
1555
+ effectiveTo: effectiveTo || null
1556
+ };
287
1557
  result = await withScopedApiRequestHeaders(
288
1558
  buildOptimisticLockHeader(updatedAt),
289
1559
  () => apiCall(`/api/workflows/definitions/${definitionId}`, {
290
1560
  method: "PUT",
291
1561
  headers: { "Content-Type": "application/json" },
292
- body: JSON.stringify({
293
- workflowName,
294
- description: description || null,
295
- version,
296
- definition: definitionData,
297
- metadata: Object.keys(metadata2).length > 0 ? metadata2 : null,
298
- enabled,
299
- effectiveFrom: effectiveFrom || null,
300
- effectiveTo: effectiveTo || null
301
- })
1562
+ body: JSON.stringify(updateBody)
302
1563
  })
303
1564
  );
304
1565
  } else {
@@ -311,7 +1572,7 @@ function VisualEditorPage() {
311
1572
  description: description || null,
312
1573
  version,
313
1574
  definition: definitionData,
314
- metadata: Object.keys(metadata2).length > 0 ? metadata2 : null,
1575
+ metadata: metadataPayload,
315
1576
  enabled,
316
1577
  effectiveFrom: effectiveFrom || null,
317
1578
  effectiveTo: effectiveTo || null
@@ -319,27 +1580,424 @@ function VisualEditorPage() {
319
1580
  });
320
1581
  }
321
1582
  if (!result.ok) {
1583
+ const activeInstanceCount = readStructuralEditConflictCount(result.status, result.result);
1584
+ if (activeInstanceCount !== null && updateBody) {
1585
+ setStructuralConflict({ activeInstanceCount, payload: updateBody });
1586
+ return;
1587
+ }
322
1588
  const conflictError = Object.assign(new Error(t("workflows.messages.saveFailed", "Failed to save")), {
323
1589
  status: result.status,
324
1590
  ...result.result && typeof result.result === "object" ? result.result : {}
325
1591
  });
326
1592
  if (!surfaceRecordConflict(conflictError, t)) {
327
- flash(`Failed to save: ${result.result?.error || "Unknown error"}`, "error");
1593
+ flash(formatWorkflowValidationError(result.result, t("workflows.messages.saveFailed", "Failed to save")), "error");
328
1594
  }
329
1595
  return;
330
1596
  }
1597
+ setStructuralConflict(null);
1598
+ if (updateBody) lastAutosavedBodyRef.current = stableSerializeDefinition(updateBody);
331
1599
  const savedDefinition = result.result?.data;
332
- flash(`Workflow ${isUpdate ? "updated" : "created"} successfully!`, "success");
333
- setTimeout(() => {
334
- router.push(`/backend/definitions/${savedDefinition.id}`);
335
- }, 1500);
1600
+ draftSuspendedRef.current = true;
1601
+ setPendingDraft(null);
1602
+ setDraftSavedAt(null);
1603
+ setDraftSaveFailed(false);
1604
+ if (isUpdate && isServerDraftEligible(definitionId)) {
1605
+ void apiCall(`/api/workflows/definitions/${definitionId}/draft`, { method: "DELETE" }).catch(() => void 0);
1606
+ }
1607
+ flash(
1608
+ isUpdate ? t("workflows.messages.workflowUpdated", "Workflow updated successfully") : t("workflows.messages.workflowCreated", "Workflow created successfully"),
1609
+ "success"
1610
+ );
1611
+ if (isUpdate) {
1612
+ if (typeof savedDefinition?.updatedAt === "string") {
1613
+ setUpdatedAt(savedDefinition.updatedAt);
1614
+ }
1615
+ } else if (savedDefinition?.id) {
1616
+ router.replace(`/backend/definitions/visual-editor?id=${encodeURIComponent(savedDefinition.id)}`);
1617
+ }
336
1618
  } catch (error) {
337
1619
  logger.error("Error saving workflow definition", { err: error });
338
- flash("Failed to save workflow definition. Please try again.", "error");
1620
+ flash(t("workflows.messages.saveFailed", "Failed to save"), "error");
339
1621
  } finally {
340
1622
  setIsSaving(false);
341
1623
  }
342
- }, [nodes, edges, workflowId, workflowName, description, version, enabled, category, tags, icon, effectiveFrom, effectiveTo, triggers, definitionId, updatedAt, router]);
1624
+ }, [nodes, edges, workflowId, workflowName, description, version, enabled, category, tags, icon, effectiveFrom, effectiveTo, triggers, contextSchema, definitionIo, interpolation, errorHandler, loadedMetadata, annotations, definitionId, updatedAt, router, t]);
1625
+ const focusNode = useCallback((nodeId) => {
1626
+ focusRequestRef.current += 1;
1627
+ setFocusTarget({ nodeId, requestId: focusRequestRef.current });
1628
+ }, []);
1629
+ const openSelectedInspector = useCallback(() => {
1630
+ const selected = nodesRef.current.find((node) => node.selected);
1631
+ if (!selected) {
1632
+ const selectedEdgeNode = edgesRef.current.find((edge) => edge.selected);
1633
+ if (!selectedEdgeNode || isCodeOnly) return;
1634
+ setSelectedEdge(selectedEdgeNode);
1635
+ setSelectedNode(null);
1636
+ setEdgeDialogFocusFieldId(null);
1637
+ setShowEdgeDialog(true);
1638
+ return;
1639
+ }
1640
+ handleNodeClick({}, selected);
1641
+ }, [isCodeOnly, handleNodeClick]);
1642
+ const handleDeleteSelection = useCallback(async () => {
1643
+ if (isCodeOnly) return;
1644
+ const nodeIds = selectedNodeIds(nodesRef.current);
1645
+ const edgeIds = edgesRef.current.filter((edge) => edge.selected).map((edge) => edge.id);
1646
+ if (nodeIds.length === 0 && edgeIds.length === 0) {
1647
+ flash(t("workflows.visualEditor.keyboard.nothingSelected", "Select a step or a route first"), "info");
1648
+ return;
1649
+ }
1650
+ for (const edgeId of edgeIds) await handleDeleteEdge(edgeId);
1651
+ for (const nodeId of nodeIds) await handleDeleteNode(nodeId);
1652
+ }, [isCodeOnly, handleDeleteEdge, handleDeleteNode, t]);
1653
+ const nudgeBaselineRef = React.useRef(null);
1654
+ const nudgeCommitTimerRef = React.useRef(null);
1655
+ const handleNudge = useCallback((key, coarse) => {
1656
+ if (isCodeOnly) return false;
1657
+ const direction = resolveNudgeDirection(key);
1658
+ if (!direction) return false;
1659
+ if (selectedNodeIds(nodesRef.current).length === 0) return false;
1660
+ if (!nudgeBaselineRef.current) nudgeBaselineRef.current = captureDocument();
1661
+ setNodes((nds) => nudgeSelectedNodes(nds, nudgeOffset(direction, coarse)));
1662
+ if (nudgeCommitTimerRef.current) clearTimeout(nudgeCommitTimerRef.current);
1663
+ nudgeCommitTimerRef.current = setTimeout(() => {
1664
+ nudgeCommitTimerRef.current = null;
1665
+ const baseline = nudgeBaselineRef.current;
1666
+ nudgeBaselineRef.current = null;
1667
+ if (baseline) commitCapturedDocument(baseline, historyLabels.move);
1668
+ scheduleAutosave();
1669
+ }, NUDGE_COMMIT_DELAY_MS);
1670
+ return true;
1671
+ }, [isCodeOnly, captureDocument, commitCapturedDocument, historyLabels.move, scheduleAutosave]);
1672
+ useEffect(() => () => {
1673
+ if (nudgeCommitTimerRef.current) clearTimeout(nudgeCommitTimerRef.current);
1674
+ }, []);
1675
+ const commandPaletteCommands = useMemo(() => buildWorkflowEditorCommands({
1676
+ readOnly: isCodeOnly,
1677
+ canUndo: canUndoEditorHistory(history),
1678
+ canRedo: canRedoEditorHistory(history),
1679
+ hasNodes: nodes.length > 0,
1680
+ hasSelection: nodes.some((node) => node.selected) || edges.some((edge) => edge.selected),
1681
+ canRunTest: !!definitionId,
1682
+ stepTypes: PALETTE_NODE_TYPES.map((nodeType) => ({
1683
+ nodeType,
1684
+ typeLabel: NODE_TYPE_LABELS[nodeType]?.title ?? nodeType
1685
+ })),
1686
+ steps: nodes.filter((node) => !isAnnotationNode(node)).map((node) => ({
1687
+ id: node.id,
1688
+ label: typeof node.data?.label === "string" && node.data.label ? node.data.label : node.id,
1689
+ typeLabel: NODE_TYPE_LABELS[node.type ?? ""]?.title ?? node.type ?? ""
1690
+ })),
1691
+ labels: {
1692
+ undo: t("workflows.commandPalette.undo", "Undo"),
1693
+ redo: t("workflows.commandPalette.redo", "Redo"),
1694
+ deleteSelection: t("workflows.commandPalette.delete", "Delete selection"),
1695
+ copy: t("workflows.commandPalette.copy", "Copy selection"),
1696
+ paste: t("workflows.commandPalette.paste", "Paste"),
1697
+ duplicate: t("workflows.commandPalette.duplicate", "Duplicate selection"),
1698
+ addStep: (typeLabel) => t("workflows.commandPalette.addStep", "Add step: {type}", { type: typeLabel }),
1699
+ addNote: t("workflows.commandPalette.addNote", "Add note"),
1700
+ addGroup: t("workflows.commandPalette.addGroup", "Add group"),
1701
+ goToStep: t("workflows.commandPalette.goToStep", "Go to step"),
1702
+ tidy: t("workflows.visualEditor.autoArrange"),
1703
+ togglePalette: t("workflows.commandPalette.togglePalette", "Toggle the step palette"),
1704
+ toggleMetadata: t("workflows.commandPalette.toggleMetadata", "Toggle the workflow details panel"),
1705
+ toggleFocus: t("workflows.commandPalette.toggleFocus", "Toggle focus mode"),
1706
+ toggleProblems: t("workflows.commandPalette.toggleProblems", "Toggle the Problems panel"),
1707
+ toggleCodeView: t("workflows.commandPalette.toggleCodeView", "Toggle the Code view"),
1708
+ toggleCompensation: t("workflows.commandPalette.toggleCompensation", "Toggle compensation paths"),
1709
+ validate: t("workflows.visualEditor.validate"),
1710
+ runTest: t("workflows.actions.startInstance"),
1711
+ save: t("workflows.common.save"),
1712
+ aiDraft: t("workflows.aiDraft.open", "Draft this workflow with AI")
1713
+ },
1714
+ actions: {
1715
+ undo: handleUndo,
1716
+ redo: handleRedo,
1717
+ deleteSelection: () => {
1718
+ void handleDeleteSelection();
1719
+ },
1720
+ copy: () => {
1721
+ void handleCopySelection();
1722
+ },
1723
+ paste: () => {
1724
+ void handlePaste();
1725
+ },
1726
+ duplicate: handleDuplicateSelection,
1727
+ addStep: (nodeType) => handleAddNode(nodeType),
1728
+ addNote: () => handleAddAnnotation("note"),
1729
+ addGroup: () => handleAddAnnotation("group"),
1730
+ goToStep: focusNode,
1731
+ tidy: handleAutoArrange,
1732
+ togglePalette: togglePaletteCollapsed,
1733
+ toggleMetadata: () => setMetadataOpen((open) => !open),
1734
+ toggleFocus,
1735
+ toggleProblems: () => setShowProblems((visible) => !visible),
1736
+ toggleCodeView: () => setShowCodeView((visible) => !visible),
1737
+ toggleCompensation,
1738
+ validate: handleValidate,
1739
+ runTest: () => setStartOpen(true),
1740
+ save: () => {
1741
+ void handleSave();
1742
+ },
1743
+ aiDraft: () => setShowAiDraft(true)
1744
+ }
1745
+ }), [
1746
+ isCodeOnly,
1747
+ history,
1748
+ nodes,
1749
+ edges,
1750
+ definitionId,
1751
+ t,
1752
+ handleUndo,
1753
+ handleRedo,
1754
+ handleDeleteSelection,
1755
+ handleCopySelection,
1756
+ handlePaste,
1757
+ handleDuplicateSelection,
1758
+ handleAddNode,
1759
+ handleAddAnnotation,
1760
+ focusNode,
1761
+ handleAutoArrange,
1762
+ togglePaletteCollapsed,
1763
+ toggleFocus,
1764
+ toggleCompensation,
1765
+ handleValidate,
1766
+ handleSave
1767
+ ]);
1768
+ useEffect(() => {
1769
+ if (isMobile) return;
1770
+ const onKeyDown = (event) => {
1771
+ const active = document.activeElement;
1772
+ const tag = (active?.tagName || "").toLowerCase();
1773
+ const isEditing = tag === "input" || tag === "textarea" || tag === "select" || !!active?.isContentEditable;
1774
+ const isInspectorOpen = showNodeDialog || showEdgeDialog;
1775
+ const isDialogOpen = isInspectorOpen || showAnnotationDialog || showClearConfirm || startOpen || showCodeView;
1776
+ const isOverlayOpen = isDialogOpen || metadataOpen || showAiDraft;
1777
+ const isCommandKey = (event.metaKey || event.ctrlKey) && !event.altKey;
1778
+ if (isCommandKey && (event.key === "k" || event.key === "K")) {
1779
+ if (isOverlayOpen) return;
1780
+ event.preventDefault();
1781
+ setShowCommandPalette((open) => !open);
1782
+ return;
1783
+ }
1784
+ if (isCommandKey && !event.shiftKey && (event.key === "s" || event.key === "S")) {
1785
+ if (isDialogOpen || isCodeOnly) return;
1786
+ event.preventDefault();
1787
+ void handleSave();
1788
+ return;
1789
+ }
1790
+ if (isEditing || showCommandPalette) return;
1791
+ if (isCommandKey && (event.key === "z" || event.key === "Z")) {
1792
+ if (isOverlayOpen) return;
1793
+ event.preventDefault();
1794
+ if (event.shiftKey) handleRedo();
1795
+ else handleUndo();
1796
+ return;
1797
+ }
1798
+ if (isCommandKey && !event.shiftKey && !isOverlayOpen) {
1799
+ if (event.key === "c" || event.key === "C") {
1800
+ event.preventDefault();
1801
+ void handleCopySelection();
1802
+ return;
1803
+ }
1804
+ if (event.key === "v" || event.key === "V") {
1805
+ event.preventDefault();
1806
+ void handlePaste();
1807
+ return;
1808
+ }
1809
+ if (event.key === "d" || event.key === "D") {
1810
+ event.preventDefault();
1811
+ handleDuplicateSelection();
1812
+ return;
1813
+ }
1814
+ }
1815
+ if (event.metaKey || event.ctrlKey || event.altKey) return;
1816
+ if (event.key === "Escape") {
1817
+ if (inspectorsDocked && isInspectorOpen) {
1818
+ event.preventDefault();
1819
+ setShowNodeDialog(false);
1820
+ setShowEdgeDialog(false);
1821
+ return;
1822
+ }
1823
+ if (focusMode && !isOverlayOpen) {
1824
+ event.preventDefault();
1825
+ setFocusMode(false);
1826
+ }
1827
+ return;
1828
+ }
1829
+ if (isOverlayOpen) return;
1830
+ if (event.key === "Enter") {
1831
+ event.preventDefault();
1832
+ openSelectedInspector();
1833
+ return;
1834
+ }
1835
+ if (event.key === "Delete" || event.key === "Backspace") {
1836
+ event.preventDefault();
1837
+ void handleDeleteSelection();
1838
+ return;
1839
+ }
1840
+ if (resolveNudgeDirection(event.key)) {
1841
+ if (handleNudge(event.key, event.shiftKey)) event.preventDefault();
1842
+ return;
1843
+ }
1844
+ if (event.key === "f" || event.key === "F") {
1845
+ event.preventDefault();
1846
+ toggleFocus();
1847
+ }
1848
+ };
1849
+ window.addEventListener("keydown", onKeyDown);
1850
+ return () => window.removeEventListener("keydown", onKeyDown);
1851
+ }, [
1852
+ isMobile,
1853
+ focusMode,
1854
+ inspectorsDocked,
1855
+ showNodeDialog,
1856
+ showEdgeDialog,
1857
+ showAnnotationDialog,
1858
+ showClearConfirm,
1859
+ startOpen,
1860
+ showCodeView,
1861
+ metadataOpen,
1862
+ showAiDraft,
1863
+ showCommandPalette,
1864
+ toggleFocus,
1865
+ setFocusMode,
1866
+ handleUndo,
1867
+ handleRedo,
1868
+ handleCopySelection,
1869
+ handlePaste,
1870
+ handleDuplicateSelection,
1871
+ openSelectedInspector,
1872
+ handleDeleteSelection,
1873
+ handleNudge,
1874
+ handleSave,
1875
+ isCodeOnly
1876
+ ]);
1877
+ performAutosaveRef.current = async () => {
1878
+ if (!definitionId || isCodeOnly) return;
1879
+ if (!workflowId || !workflowName) return;
1880
+ const criticalErrors = validateWorkflowGraph(nodes, edges).filter((e) => e.type === "error");
1881
+ if (criticalErrors.length > 0) return;
1882
+ const definitionData = buildDefinitionPayload({
1883
+ graphDefinition: graphToDefinition(nodes, edges, { includePositions: true }),
1884
+ triggers,
1885
+ contextSchema,
1886
+ io: definitionIo,
1887
+ interpolation,
1888
+ errorHandler
1889
+ });
1890
+ if (!workflowDefinitionDataSchema.safeParse(definitionData).success) return;
1891
+ const metadataPayload = buildMetadataPayload({ loadedMetadata, tags, category, icon, definition: definitionData, annotations });
1892
+ const updateBody = {
1893
+ workflowName,
1894
+ description: description || null,
1895
+ version,
1896
+ definition: definitionData,
1897
+ metadata: metadataPayload,
1898
+ enabled,
1899
+ effectiveFrom: effectiveFrom || null,
1900
+ effectiveTo: effectiveTo || null
1901
+ };
1902
+ const serializedBody = stableSerializeDefinition(updateBody);
1903
+ if (serializedBody === lastAutosavedBodyRef.current) return;
1904
+ lastAutosavedBodyRef.current = serializedBody;
1905
+ setAutosaveState("saving");
1906
+ try {
1907
+ const result = await withScopedApiRequestHeaders(
1908
+ buildOptimisticLockHeader(updatedAt),
1909
+ () => apiCall(`/api/workflows/definitions/${definitionId}`, {
1910
+ method: "PUT",
1911
+ headers: { "Content-Type": "application/json" },
1912
+ body: JSON.stringify(updateBody)
1913
+ })
1914
+ );
1915
+ if (!result.ok) {
1916
+ setAutosaveState("idle");
1917
+ lastAutosavedBodyRef.current = null;
1918
+ const activeInstanceCount = readStructuralEditConflictCount(result.status, result.result);
1919
+ if (activeInstanceCount !== null) {
1920
+ setStructuralConflict({ activeInstanceCount, payload: updateBody });
1921
+ return;
1922
+ }
1923
+ const conflictError = Object.assign(new Error("[internal] workflow autosave failed"), {
1924
+ status: result.status,
1925
+ ...result.result && typeof result.result === "object" ? result.result : {}
1926
+ });
1927
+ surfaceRecordConflict(conflictError, t);
1928
+ return;
1929
+ }
1930
+ setStructuralConflict(null);
1931
+ const savedDefinition = result.result?.data;
1932
+ if (typeof savedDefinition?.updatedAt === "string") {
1933
+ setUpdatedAt(savedDefinition.updatedAt);
1934
+ }
1935
+ setAutosaveState("saved");
1936
+ } catch (error) {
1937
+ logger.error("workflow autosave failed", {
1938
+ error: error instanceof Error ? error.message : String(error)
1939
+ });
1940
+ lastAutosavedBodyRef.current = null;
1941
+ setAutosaveState("idle");
1942
+ }
1943
+ };
1944
+ useEffect(() => {
1945
+ if (autosaveState !== "saved") return;
1946
+ const timer = setTimeout(() => setAutosaveState("idle"), 2e3);
1947
+ return () => clearTimeout(timer);
1948
+ }, [autosaveState]);
1949
+ const handleCreateVersion = useCallback(async () => {
1950
+ if (!definitionId || !structuralConflict) return;
1951
+ setIsCreatingVersion(true);
1952
+ try {
1953
+ const publishResult = await apiCall(
1954
+ `/api/workflows/definitions/${definitionId}/publish`,
1955
+ { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({}) }
1956
+ );
1957
+ const mintedVersion = publishResult.result?.data;
1958
+ if (!publishResult.ok || !mintedVersion?.id) {
1959
+ flash(
1960
+ publishResult.result?.error || t("workflows.visualEditor.editSafety.createVersionFailed", "Could not create a new version."),
1961
+ "error"
1962
+ );
1963
+ return;
1964
+ }
1965
+ const nextVersion = mintedVersion.version ?? version;
1966
+ const saveResult = await withScopedApiRequestHeaders(
1967
+ buildOptimisticLockHeader(mintedVersion.updatedAt ?? null),
1968
+ () => apiCall(
1969
+ `/api/workflows/definitions/${mintedVersion.id}`,
1970
+ {
1971
+ method: "PUT",
1972
+ headers: { "Content-Type": "application/json" },
1973
+ body: JSON.stringify({ ...structuralConflict.payload, version: nextVersion })
1974
+ }
1975
+ )
1976
+ );
1977
+ if (!saveResult.ok) {
1978
+ flash(
1979
+ formatWorkflowValidationError(
1980
+ saveResult.result,
1981
+ t("workflows.visualEditor.editSafety.createVersionFailed", "Could not create a new version.")
1982
+ ),
1983
+ "error"
1984
+ );
1985
+ return;
1986
+ }
1987
+ setStructuralConflict(null);
1988
+ setVersion(nextVersion);
1989
+ setUpdatedAt(saveResult.result?.data?.updatedAt ?? null);
1990
+ flash(
1991
+ t("workflows.visualEditor.editSafety.createVersionSucceeded", "Version {version} created. Your changes were saved there.", {
1992
+ version: String(nextVersion)
1993
+ }),
1994
+ "success"
1995
+ );
1996
+ router.replace(`/backend/definitions/visual-editor?id=${encodeURIComponent(mintedVersion.id)}`);
1997
+ } finally {
1998
+ setIsCreatingVersion(false);
1999
+ }
2000
+ }, [definitionId, structuralConflict, version, router, t]);
343
2001
  const handleCustomize = useCallback(async () => {
344
2002
  if (!definitionId) return;
345
2003
  setIsSaving(true);
@@ -387,90 +2045,106 @@ function VisualEditorPage() {
387
2045
  setIsSaving(false);
388
2046
  }
389
2047
  }, [definitionId, workflowId, router, confirm, t]);
390
- const handleTest = useCallback(() => {
391
- const errors = validateWorkflowGraph(nodes, edges);
392
- const criticalErrors = errors.filter((e) => e.type === "error");
393
- if (criticalErrors.length > 0) {
394
- flash(`Cannot test: ${criticalErrors.length} validation error(s) found. Please fix them first.`, "error");
2048
+ const handleStartInstance = useCallback(async () => {
2049
+ let initialContext;
2050
+ try {
2051
+ const parsed = startContext.trim() ? JSON.parse(startContext) : {};
2052
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
2053
+ throw new Error("[internal] context must be a JSON object");
2054
+ }
2055
+ initialContext = parsed;
2056
+ } catch {
2057
+ flash(t("workflows.startInstance.invalidJson"), "error");
395
2058
  return;
396
2059
  }
397
- flash("Test functionality will be implemented next", "info");
398
- }, [nodes, edges]);
399
- const handleLoadExample = useCallback(() => {
400
- setWorkflowId("approval_workflow");
401
- setWorkflowName("Simple Approval Workflow");
402
- setDescription("A basic approval workflow for reviewing and approving requests");
403
- setVersion(1);
404
- setEnabled(true);
405
- setCategory("Approvals");
406
- setTags(["approval", "review"]);
407
- const exampleNodes = [
408
- {
409
- id: "start",
410
- type: "start",
411
- position: { x: 250, y: 50 },
412
- data: {
413
- label: "Start",
414
- description: "Workflow begins",
415
- status: "pending",
416
- badge: "Start"
417
- }
418
- },
419
- {
420
- id: "step1",
421
- type: "userTask",
422
- position: { x: 250, y: 250 },
423
- data: {
424
- label: "Review Request",
425
- description: "User reviews the incoming request",
426
- status: "pending",
427
- stepNumber: 1,
428
- badge: "User Task",
429
- assignedToRoles: ["Reviewer"]
430
- }
431
- },
432
- {
433
- id: "end",
434
- type: "end",
435
- position: { x: 250, y: 450 },
436
- data: {
437
- label: "Complete",
438
- description: "Workflow ends",
439
- status: "pending",
440
- badge: "End"
441
- }
442
- }
443
- ];
444
- const exampleEdges = [
445
- {
446
- id: "e-start-step1",
447
- source: "start",
448
- target: "step1",
449
- type: "smoothstep",
450
- data: {
451
- trigger: "auto",
452
- preConditions: [],
453
- postConditions: [],
454
- activities: []
455
- }
456
- },
457
- {
458
- id: "e-step1-end",
459
- source: "step1",
460
- target: "end",
461
- type: "smoothstep",
462
- data: {
463
- trigger: "auto",
464
- preConditions: [],
465
- postConditions: [],
466
- activities: []
2060
+ setStarting(true);
2061
+ try {
2062
+ const result = await runMutation({
2063
+ operation: async () => {
2064
+ const response = await apiFetch("/api/workflows/instances", {
2065
+ method: "POST",
2066
+ headers: { "Content-Type": "application/json" },
2067
+ body: JSON.stringify({
2068
+ workflowId,
2069
+ version,
2070
+ initialContext,
2071
+ ...startDryRun ? { dryRun: true } : {},
2072
+ ...startStepThrough ? { stepThrough: true } : {}
2073
+ })
2074
+ });
2075
+ if (!response.ok) {
2076
+ const errorBody = await readJsonSafe(response, null);
2077
+ throw new Error(errorBody?.error || t("workflows.startInstance.failed"));
2078
+ }
2079
+ return readJsonSafe(response, null);
2080
+ },
2081
+ mutationPayload: { resourceId: definitionId, operation: "start-instance" },
2082
+ context: {
2083
+ formId: mutationContextId,
2084
+ resourceKind: "workflows.instance",
2085
+ resourceId: definitionId,
2086
+ operation: "start-instance",
2087
+ retryLastMutation
467
2088
  }
2089
+ });
2090
+ flash(t("workflows.startInstance.started"), "success");
2091
+ setStartOpen(false);
2092
+ const instanceId = result?.data?.instance?.id;
2093
+ if (instanceId) {
2094
+ router.push(`/backend/instances/${instanceId}`);
2095
+ router.refresh();
468
2096
  }
469
- ];
470
- setNodes(exampleNodes);
471
- setEdges(exampleEdges);
472
- flash("Example workflow loaded", "success");
2097
+ } catch (error) {
2098
+ flash(error instanceof Error ? error.message : t("workflows.startInstance.failed"), "error");
2099
+ } finally {
2100
+ setStarting(false);
2101
+ }
2102
+ }, [startContext, startDryRun, startStepThrough, workflowId, version, definitionId, mutationContextId, retryLastMutation, router, t]);
2103
+ const applyTemplate = useCallback((template) => {
2104
+ setWorkflowId(template.id.replace(/-/g, "_"));
2105
+ setWorkflowName(t(template.nameKey));
2106
+ setDescription(t(template.descriptionKey));
2107
+ setVersion(1);
2108
+ setEnabled(true);
2109
+ setCategory(template.category);
2110
+ setTags([]);
2111
+ setIcon(template.icon);
2112
+ const graph = definitionToGraph(template.definition);
2113
+ setNodes(graph.nodes);
2114
+ setEdges(graph.edges);
2115
+ setTriggers(template.definition.triggers || []);
2116
+ setContextSchema(template.definition.contextSchema ?? void 0);
2117
+ setDefinitionIo(template.definition.io ?? void 0);
2118
+ setInterpolation(resolveDefinitionInterpolationMode(template.definition) ?? "strict");
2119
+ setErrorHandler(template.definition.errorHandler ?? void 0);
2120
+ setLoadedMetadata(null);
2121
+ resetHistory();
2122
+ flash(t("workflows.visualEditor.templateLoaded", "Template loaded"), "success");
2123
+ }, [resetHistory, t]);
2124
+ const handleOpenTemplateGallery = useCallback(() => {
2125
+ setShowTemplateGallery(true);
473
2126
  }, []);
2127
+ const handleTemplateSelect = useCallback((template) => {
2128
+ if (template) applyTemplate(template);
2129
+ }, [applyTemplate]);
2130
+ useEffect(() => {
2131
+ if (definitionId || !templateId) return;
2132
+ let cancelled = false;
2133
+ const loadTemplate = async () => {
2134
+ const result = await apiCall("/api/workflows/templates");
2135
+ if (cancelled) return;
2136
+ const template = result.ok ? (result.result?.items || []).find((item) => item.id === templateId) : void 0;
2137
+ if (template) {
2138
+ applyTemplate(template);
2139
+ } else {
2140
+ flash(t("workflows.templates.gallery.notFound", "Template not found"), "error");
2141
+ }
2142
+ };
2143
+ void loadTemplate();
2144
+ return () => {
2145
+ cancelled = true;
2146
+ };
2147
+ }, [definitionId, templateId, applyTemplate, t]);
474
2148
  const handleClear = useCallback(() => {
475
2149
  if (nodes.length > 0 || edges.length > 0 || workflowId || workflowName) {
476
2150
  setShowClearConfirm(true);
@@ -490,9 +2164,13 @@ function VisualEditorPage() {
490
2164
  setEffectiveFrom("");
491
2165
  setEffectiveTo("");
492
2166
  setTriggers([]);
2167
+ setContextSchema(void 0);
2168
+ setDefinitionIo(void 0);
2169
+ setLoadedMetadata(null);
493
2170
  setShowClearConfirm(false);
2171
+ resetHistory();
494
2172
  flash("Canvas cleared", "success");
495
- }, []);
2173
+ }, [resetHistory]);
496
2174
  useSetCurrentRecordInjectionContext(
497
2175
  buildRecordInjectionContext({
498
2176
  resourceKind: "workflows.definition",
@@ -515,7 +2193,10 @@ function VisualEditorPage() {
515
2193
  icon,
516
2194
  effectiveFrom,
517
2195
  effectiveTo,
518
- triggers
2196
+ triggers,
2197
+ contextSchema,
2198
+ interpolation,
2199
+ errorHandler
519
2200
  };
520
2201
  const metadataHandlers = {
521
2202
  setWorkflowId,
@@ -528,24 +2209,345 @@ function VisualEditorPage() {
528
2209
  setIcon,
529
2210
  setEffectiveFrom,
530
2211
  setEffectiveTo,
531
- setTriggers
2212
+ setTriggers,
2213
+ setContextSchema,
2214
+ setInterpolation,
2215
+ setErrorHandler: (next) => setErrorHandler(next ?? void 0)
532
2216
  };
2217
+ const metadataIncomplete = !isCodeOnly && (!workflowId || !workflowName);
2218
+ const inspectorPanels = /* @__PURE__ */ jsxs(Fragment, { children: [
2219
+ crudFormDialogsEnabled ? (
2220
+ // Both forms are dense (Invoke-Agent, timeout, sub-editors on the step;
2221
+ // condition, mapping and activities on the route) and the 384px docked
2222
+ // rail made them unusable, so each opens as the wide overlay Drawer that
2223
+ // mirrors the definition metadata drawer, with its ledger in a sticky
2224
+ // side column.
2225
+ /* @__PURE__ */ jsx(NodeEditDialogCrudForm, { node: selectedNode, isOpen: showNodeDialog, onClose: () => setShowNodeDialog(false), onSave: handleSaveNode, onDelete: handleDeleteNode, ledgerEntries: nodeDialogLedgerEntries, definitionId, samples: editorSamples, onPinSample: handlePinSample, onUnpinSample: handleUnpinSample, branchingRoutes: branchingRoutesValue, onSaveBranchingRoutes: handleSaveBranchingRoutes, routeOrder: routeOrderValue, onSaveRouteOrder: handleSaveRouteOrder, onConvertType: handleConvertNodeType, variant: "overlay", wide: true })
2226
+ ) : /* @__PURE__ */ jsx(NodeEditDialog, { node: selectedNode, isOpen: showNodeDialog, onClose: () => setShowNodeDialog(false), onSave: handleSaveNode, onDelete: handleDeleteNode }),
2227
+ crudFormDialogsEnabled ? /* @__PURE__ */ jsx(EdgeEditDialogCrudForm, { edge: selectedEdge, isOpen: showEdgeDialog, onClose: () => setShowEdgeDialog(false), onSave: handleSaveEdge, onDelete: handleDeleteEdge, ledgerEntries: edgeDialogLedgerEntries, focusFieldId: edgeDialogFocusFieldId, variant: "overlay", wide: true }) : /* @__PURE__ */ jsx(EdgeEditDialog, { edge: selectedEdge, isOpen: showEdgeDialog, onClose: () => setShowEdgeDialog(false), onSave: handleSaveEdge, onDelete: handleDeleteEdge })
2228
+ ] });
533
2229
  const sharedDialogs = /* @__PURE__ */ jsxs(Fragment, { children: [
534
- process.env.NEXT_PUBLIC_WORKFLOW_CRUDFORM_ENABLED === "true" ? /* @__PURE__ */ jsx(NodeEditDialogCrudForm, { node: selectedNode, isOpen: showNodeDialog, onClose: () => setShowNodeDialog(false), onSave: handleSaveNode, onDelete: handleDeleteNode }) : /* @__PURE__ */ jsx(NodeEditDialog, { node: selectedNode, isOpen: showNodeDialog, onClose: () => setShowNodeDialog(false), onSave: handleSaveNode, onDelete: handleDeleteNode }),
535
- process.env.NEXT_PUBLIC_WORKFLOW_CRUDFORM_ENABLED === "true" ? /* @__PURE__ */ jsx(EdgeEditDialogCrudForm, { edge: selectedEdge, isOpen: showEdgeDialog, onClose: () => setShowEdgeDialog(false), onSave: handleSaveEdge, onDelete: handleDeleteEdge }) : /* @__PURE__ */ jsx(EdgeEditDialog, { edge: selectedEdge, isOpen: showEdgeDialog, onClose: () => setShowEdgeDialog(false), onSave: handleSaveEdge, onDelete: handleDeleteEdge }),
536
- /* @__PURE__ */ jsx(Dialog, { open: showClearConfirm, onOpenChange: setShowClearConfirm, children: /* @__PURE__ */ jsxs(DialogContent, { className: "sm:max-w-md", children: [
537
- /* @__PURE__ */ jsxs(DialogHeader, { children: [
538
- /* @__PURE__ */ jsx(DialogTitle, { children: t("workflows.visualEditor.clearTitle") }),
539
- /* @__PURE__ */ jsx(DialogDescription, { children: t("workflows.visualEditor.clearDescription") })
540
- ] }),
541
- /* @__PURE__ */ jsxs(DialogFooter, { children: [
542
- /* @__PURE__ */ jsx(Button, { variant: "outline", onClick: () => setShowClearConfirm(false), children: t("common.cancel", "Cancel") }),
543
- /* @__PURE__ */ jsx(Button, { variant: "destructive-solid", onClick: confirmClear, children: t("common.clear", "Clear") })
544
- ] })
545
- ] }) })
2230
+ inspectorsDocked ? null : inspectorPanels,
2231
+ /* @__PURE__ */ jsx(
2232
+ WorkflowCommandPalette,
2233
+ {
2234
+ open: showCommandPalette,
2235
+ onOpenChange: setShowCommandPalette,
2236
+ commands: commandPaletteCommands
2237
+ }
2238
+ ),
2239
+ /* @__PURE__ */ jsx(
2240
+ DefinitionMetadataDrawer,
2241
+ {
2242
+ open: metadataOpen,
2243
+ onOpenChange: handleMetadataOpenChange,
2244
+ focusSection: metadataFocusSection,
2245
+ definitionId,
2246
+ readOnly: isCodeOnly,
2247
+ metadata,
2248
+ handlers: metadataHandlers,
2249
+ errorHandlerStepOptions,
2250
+ onSave: () => {
2251
+ void handleSave();
2252
+ },
2253
+ isSaving
2254
+ }
2255
+ ),
2256
+ /* @__PURE__ */ jsx(
2257
+ TriggersDialog,
2258
+ {
2259
+ open: triggersDialogOpen,
2260
+ onOpenChange: setTriggersDialogOpen,
2261
+ triggers,
2262
+ onSave: setTriggers
2263
+ }
2264
+ ),
2265
+ (() => {
2266
+ const edge = activityEdit ? edges.find((candidate) => candidate.id === activityEdit.edgeId) ?? null : null;
2267
+ const activities = edge?.data?.activities ?? [];
2268
+ const activity = activityEdit ? activities.find((item) => item.activityId === activityEdit.activityId) ?? null : null;
2269
+ const labelFor = (nodeId) => nodes.find((node) => node.id === nodeId)?.data?.label ?? nodeId;
2270
+ const routeLabel = edge ? `${labelFor(edge.source)} \u2192 ${labelFor(edge.target)}` : void 0;
2271
+ return /* @__PURE__ */ jsx(
2272
+ ActivityEditDialog,
2273
+ {
2274
+ open: !!activity,
2275
+ onOpenChange: (open) => {
2276
+ if (!open) setActivityEdit(null);
2277
+ },
2278
+ activity,
2279
+ routeLabel,
2280
+ onSave: (updated) => {
2281
+ if (!activityEdit) return;
2282
+ handleSaveEdge(activityEdit.edgeId, {
2283
+ activities: activities.map((item) => item.activityId === updated.activityId ? updated : item)
2284
+ });
2285
+ },
2286
+ onRemove: () => {
2287
+ if (!activityEdit) return;
2288
+ handleSaveEdge(activityEdit.edgeId, {
2289
+ activities: activities.filter((item) => item.activityId !== activityEdit.activityId)
2290
+ });
2291
+ },
2292
+ onOpenFullTransition: () => {
2293
+ if (activityEdit) handleRouteChipOpen(activityEdit.edgeId, "activities");
2294
+ }
2295
+ }
2296
+ );
2297
+ })(),
2298
+ /* @__PURE__ */ jsx(
2299
+ WorkflowCodeView,
2300
+ {
2301
+ isOpen: showCodeView,
2302
+ onClose: handleCloseCodeView,
2303
+ definitionJson: codeViewJson,
2304
+ issues: codeViewIssues,
2305
+ onCopy: () => {
2306
+ void handleCopyDefinitionJson();
2307
+ },
2308
+ onPasteSubgraph: () => {
2309
+ void handlePaste();
2310
+ },
2311
+ canPaste: !isCodeOnly,
2312
+ onIssueClick: handleProblemClick,
2313
+ draftText: codeDraftText,
2314
+ onDraftChange: setCodeDraft,
2315
+ onApply: handleApplyCodeDraft,
2316
+ onRevert: handleRevertCodeDraft,
2317
+ draftStatus: codeDraftStatus,
2318
+ severityByLine: codeIssueLocations.severityByLine,
2319
+ lineByIssueId: codeIssueLocations.lineByIssueId
2320
+ }
2321
+ ),
2322
+ /* @__PURE__ */ jsx(
2323
+ WorkflowAiDraftDialog,
2324
+ {
2325
+ open: showAiDraft,
2326
+ onOpenChange: setShowAiDraft,
2327
+ onApply: handleApplyAiDraft,
2328
+ canvasHasContent: nodes.length > 0
2329
+ }
2330
+ ),
2331
+ /* @__PURE__ */ jsx(
2332
+ AnnotationEditDialog,
2333
+ {
2334
+ node: selectedAnnotation,
2335
+ isOpen: showAnnotationDialog,
2336
+ onClose: () => setShowAnnotationDialog(false),
2337
+ onSave: handleSaveAnnotation,
2338
+ onDelete: handleDeleteAnnotation
2339
+ }
2340
+ ),
2341
+ /* @__PURE__ */ jsx(
2342
+ TemplateGalleryDialog,
2343
+ {
2344
+ open: showTemplateGallery,
2345
+ onOpenChange: setShowTemplateGallery,
2346
+ onSelect: handleTemplateSelect
2347
+ }
2348
+ ),
2349
+ /* @__PURE__ */ jsx(
2350
+ ConfirmDialog,
2351
+ {
2352
+ open: showClearConfirm,
2353
+ onOpenChange: setShowClearConfirm,
2354
+ title: t("workflows.visualEditor.clearTitle"),
2355
+ text: t("workflows.visualEditor.clearDescription"),
2356
+ confirmText: t("common.clear", "Clear"),
2357
+ cancelText: t("common.cancel", "Cancel"),
2358
+ variant: "destructive",
2359
+ onConfirm: confirmClear
2360
+ }
2361
+ ),
2362
+ /* @__PURE__ */ jsx(Drawer, { open: startOpen, onOpenChange: setStartOpen, children: /* @__PURE__ */ jsxs(
2363
+ DrawerContent,
2364
+ {
2365
+ "data-testid": "workflow-start-instance-drawer",
2366
+ closeAriaLabel: t("workflows.startInstance.close"),
2367
+ onKeyDown: (event) => {
2368
+ if (!(event.metaKey || event.ctrlKey) || event.key !== "Enter") return;
2369
+ event.preventDefault();
2370
+ event.stopPropagation();
2371
+ if (starting) return;
2372
+ void handleStartInstance();
2373
+ },
2374
+ children: [
2375
+ /* @__PURE__ */ jsxs(DrawerHeader, { children: [
2376
+ /* @__PURE__ */ jsx(DrawerTitle, { children: t("workflows.startInstance.title") }),
2377
+ /* @__PURE__ */ jsx(DrawerDescription, { children: t("workflows.startInstance.description") })
2378
+ ] }),
2379
+ /* @__PURE__ */ jsxs(DrawerBody, { className: "space-y-3 py-2", children: [
2380
+ startFixtureList.length > 0 ? /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
2381
+ /* @__PURE__ */ jsx("span", { className: "text-overline text-muted-foreground", children: t("workflows.fixtures.label") }),
2382
+ /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1", children: startFixtureList.map((fixture) => /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1 rounded-md border border-border bg-muted px-2 py-1", children: [
2383
+ /* @__PURE__ */ jsx(
2384
+ "button",
2385
+ {
2386
+ type: "button",
2387
+ className: "text-xs font-medium hover:underline",
2388
+ onClick: () => handleApplyFixture(fixture.name),
2389
+ children: fixture.name
2390
+ }
2391
+ ),
2392
+ /* @__PURE__ */ jsx(
2393
+ "button",
2394
+ {
2395
+ type: "button",
2396
+ className: "text-xs text-muted-foreground hover:text-foreground",
2397
+ "aria-label": t("workflows.fixtures.delete", { name: fixture.name }),
2398
+ onClick: () => handleDeleteFixture(fixture.name),
2399
+ children: "\xD7"
2400
+ }
2401
+ )
2402
+ ] }, fixture.name)) })
2403
+ ] }) : null,
2404
+ /* @__PURE__ */ jsx("label", { className: "text-sm font-medium", htmlFor: "workflow-start-context", children: t("workflows.startInstance.contextLabel") }),
2405
+ /* @__PURE__ */ jsx(
2406
+ Textarea,
2407
+ {
2408
+ id: "workflow-start-context",
2409
+ value: startContext,
2410
+ onChange: (e) => setStartContext(e.target.value),
2411
+ rows: 9,
2412
+ spellCheck: false,
2413
+ className: "font-mono text-sm"
2414
+ }
2415
+ ),
2416
+ /* @__PURE__ */ jsxs("div", { className: "flex items-end gap-2", children: [
2417
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 space-y-1", children: [
2418
+ /* @__PURE__ */ jsx(Label, { htmlFor: "workflow-fixture-name", className: "text-xs", children: t("workflows.fixtures.nameLabel") }),
2419
+ /* @__PURE__ */ jsx(
2420
+ Input,
2421
+ {
2422
+ id: "workflow-fixture-name",
2423
+ value: fixtureName,
2424
+ onChange: (e) => setFixtureName(e.target.value),
2425
+ placeholder: t("workflows.fixtures.namePlaceholder")
2426
+ }
2427
+ )
2428
+ ] }),
2429
+ /* @__PURE__ */ jsx(Button, { variant: "outline", onClick: handleSaveFixture, disabled: !fixtureName.trim(), children: t("workflows.fixtures.save") })
2430
+ ] }),
2431
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: t("workflows.fixtures.storageWarning") }),
2432
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2 rounded-md border border-border p-2", children: [
2433
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2", children: [
2434
+ /* @__PURE__ */ jsx(Label, { htmlFor: "workflow-start-dry-run", className: "text-sm font-medium", children: t("workflows.startInstance.dryRunLabel") }),
2435
+ /* @__PURE__ */ jsx(
2436
+ Switch,
2437
+ {
2438
+ id: "workflow-start-dry-run",
2439
+ checked: startDryRun,
2440
+ onCheckedChange: (checked) => setStartDryRun(checked === true)
2441
+ }
2442
+ )
2443
+ ] }),
2444
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: t("workflows.startInstance.dryRunHint") }),
2445
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2", children: [
2446
+ /* @__PURE__ */ jsx(Label, { htmlFor: "workflow-start-step-through", className: "text-sm font-medium", children: t("workflows.startInstance.stepThroughLabel") }),
2447
+ /* @__PURE__ */ jsx(
2448
+ Switch,
2449
+ {
2450
+ id: "workflow-start-step-through",
2451
+ checked: startStepThrough,
2452
+ onCheckedChange: (checked) => setStartStepThrough(checked === true)
2453
+ }
2454
+ )
2455
+ ] }),
2456
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: t("workflows.startInstance.stepThroughHint") })
2457
+ ] })
2458
+ ] }),
2459
+ /* @__PURE__ */ jsxs(DrawerFooter, { children: [
2460
+ /* @__PURE__ */ jsx(Button, { variant: "outline", onClick: () => setStartOpen(false), disabled: starting, children: t("workflows.startInstance.cancel") }),
2461
+ /* @__PURE__ */ jsx(Button, { onClick: () => void handleStartInstance(), disabled: starting, children: starting ? /* @__PURE__ */ jsx(Spinner, { className: "h-4 w-4" }) : t("workflows.startInstance.start") })
2462
+ ] })
2463
+ ]
2464
+ }
2465
+ ) })
546
2466
  ] });
2467
+ const structuralConflictBanner = structuralConflict ? /* @__PURE__ */ jsx("div", { className: "shrink-0 border-b border-border bg-background px-3 py-2 md:px-6 md:py-3", children: /* @__PURE__ */ jsxs(Alert, { variant: "warning", children: [
2468
+ /* @__PURE__ */ jsx(AlertTitle, { children: t("workflows.visualEditor.editSafety.bannerTitle", "This change needs a new version") }),
2469
+ /* @__PURE__ */ jsx(AlertDescription, { children: t(
2470
+ "workflows.visualEditor.editSafety.bannerDescription",
2471
+ "{count} instance(s) are still running on this version. Changing the steps or routes would re-point them mid-flight, so the change is saved to a new version instead.",
2472
+ { count: String(structuralConflict.activeInstanceCount) }
2473
+ ) }),
2474
+ /* @__PURE__ */ jsxs("div", { className: "mt-2 flex flex-wrap gap-2", children: [
2475
+ /* @__PURE__ */ jsx(Button, { size: "sm", onClick: handleCreateVersion, disabled: isCreatingVersion, className: "h-8 text-xs", children: t("workflows.visualEditor.editSafety.createVersion", "Create version") }),
2476
+ /* @__PURE__ */ jsx(
2477
+ Button,
2478
+ {
2479
+ size: "sm",
2480
+ variant: "outline",
2481
+ onClick: () => setStructuralConflict(null),
2482
+ disabled: isCreatingVersion,
2483
+ className: "h-8 text-xs",
2484
+ children: t("workflows.visualEditor.editSafety.keepEditing", "Keep editing")
2485
+ }
2486
+ )
2487
+ ] })
2488
+ ] }) }) : null;
2489
+ const draftRestoreBanner = pendingDraft ? /* @__PURE__ */ jsx("div", { className: "shrink-0 border-b border-border bg-background px-3 py-2 md:px-6 md:py-3", children: /* @__PURE__ */ jsxs(Alert, { variant: "info", children: [
2490
+ /* @__PURE__ */ jsx(AlertTitle, { children: t("workflows.visualEditor.draft.bannerTitle", "You have an unsaved draft from {time}", {
2491
+ time: formatRelativeTime(pendingDraft.draft.updatedAt, { translate: t }) ?? ""
2492
+ }) }),
2493
+ pendingDraft.baseMismatch && /* @__PURE__ */ jsx(AlertDescription, { children: t("workflows.visualEditor.draft.bannerConflict", "The workflow definition has changed since this draft was made. Restoring will apply your draft over the newer version.") }),
2494
+ /* @__PURE__ */ jsxs("div", { className: "mt-2 flex flex-wrap gap-2", children: [
2495
+ /* @__PURE__ */ jsx(Button, { size: "sm", onClick: handleRestoreDraft, className: "h-8 text-xs", children: t("workflows.visualEditor.draft.restore", "Restore draft") }),
2496
+ /* @__PURE__ */ jsx(Button, { size: "sm", variant: "outline", onClick: handleDiscardDraft, className: "h-8 text-xs", children: t("workflows.visualEditor.draft.discard", "Discard") })
2497
+ ] })
2498
+ ] }) }) : null;
2499
+ const { errors: problemErrorCount, warnings: problemWarningCount } = countIssuesBySeverity(problems);
2500
+ const problemsPanel = showProblems && problems.length > 0 ? /* @__PURE__ */ jsx("div", { className: "shrink-0 border-t border-border bg-background px-3 py-2 md:px-6 md:py-3", children: /* @__PURE__ */ jsxs("div", { className: `rounded-lg border bg-card ${problemErrorCount > 0 ? "border-status-error-border" : "border-status-warning-border"}`, children: [
2501
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2 px-3 py-2", children: [
2502
+ /* @__PURE__ */ jsxs(
2503
+ Button,
2504
+ {
2505
+ variant: "ghost",
2506
+ size: "sm",
2507
+ onClick: () => setProblemsCollapsed((collapsed) => !collapsed),
2508
+ "aria-expanded": !problemsCollapsed,
2509
+ className: "h-auto gap-2 p-0 text-sm font-semibold text-foreground hover:bg-transparent",
2510
+ children: [
2511
+ problemsCollapsed ? /* @__PURE__ */ jsx(ChevronRight, { className: "h-4 w-4", "aria-hidden": "true" }) : /* @__PURE__ */ jsx(ChevronDown, { className: "h-4 w-4", "aria-hidden": "true" }),
2512
+ t("workflows.visualEditor.problems.title", "Problems"),
2513
+ /* @__PURE__ */ jsx("span", { className: "text-xs font-normal text-muted-foreground", children: t("workflows.visualEditor.problems.counts", "{errors} error(s) \xB7 {warnings} warning(s)", { errors: problemErrorCount, warnings: problemWarningCount }) })
2514
+ ]
2515
+ }
2516
+ ),
2517
+ /* @__PURE__ */ jsx(
2518
+ Button,
2519
+ {
2520
+ variant: "ghost",
2521
+ size: "sm",
2522
+ onClick: () => setShowProblems(false),
2523
+ "aria-label": t("workflows.visualEditor.problems.dismiss", "Dismiss problems"),
2524
+ className: "h-7 w-7 p-0",
2525
+ children: /* @__PURE__ */ jsx(X, { className: "h-4 w-4", "aria-hidden": "true" })
2526
+ }
2527
+ )
2528
+ ] }),
2529
+ !problemsCollapsed && /* @__PURE__ */ jsx("ul", { className: "max-h-56 overflow-y-auto border-t border-border", children: problems.map((issue) => {
2530
+ const isNavigable = Boolean(issue.nodeId || issue.edgeId);
2531
+ return /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs(
2532
+ Button,
2533
+ {
2534
+ variant: "ghost",
2535
+ onClick: () => handleProblemClick(issue),
2536
+ disabled: !isNavigable,
2537
+ className: `flex h-auto w-full items-start justify-start gap-2 rounded-none px-3 py-1.5 text-left text-sm font-normal ${isNavigable ? "hover:bg-muted" : "cursor-default hover:bg-transparent"}`,
2538
+ children: [
2539
+ issue.severity === "error" ? /* @__PURE__ */ jsx(CircleAlert, { className: "mt-0.5 h-4 w-4 shrink-0 text-status-error-text", "aria-hidden": "true" }) : /* @__PURE__ */ jsx(TriangleAlert, { className: "mt-0.5 h-4 w-4 shrink-0 text-status-warning-text", "aria-hidden": "true" }),
2540
+ /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 text-foreground", children: issue.message }),
2541
+ issue.nodeLabel && /* @__PURE__ */ jsx("span", { className: "shrink-0 text-xs text-muted-foreground", children: issue.nodeLabel })
2542
+ ]
2543
+ }
2544
+ ) }, issue.id);
2545
+ }) })
2546
+ ] }) }) : null;
547
2547
  if (isMobile) {
548
2548
  return /* @__PURE__ */ jsxs(Page, { className: "flex h-[100svh] flex-col space-y-0 overflow-hidden", children: [
2549
+ structuralConflictBanner,
2550
+ draftRestoreBanner,
549
2551
  /* @__PURE__ */ jsx(
550
2552
  MobileVisualEditor,
551
2553
  {
@@ -558,500 +2560,419 @@ function VisualEditorPage() {
558
2560
  onNodeClick: handleNodeClick,
559
2561
  onEdgeClick: handleEdgeClick,
560
2562
  onConnect: handleConnect,
2563
+ onReconnect: handleReconnect,
561
2564
  onAddNode: handleAddNode,
562
2565
  onSave: handleSave,
563
2566
  onValidate: handleValidate,
564
- onTest: handleTest,
565
- onLoadExample: handleLoadExample,
2567
+ onStartInstance: () => setStartOpen(true),
2568
+ onLoadExample: handleOpenTemplateGallery,
566
2569
  onClear: handleClear,
567
2570
  metadata,
568
- metadataHandlers
2571
+ onMetadataOpenChange: handleMetadataOpenChange
569
2572
  }
570
2573
  ),
571
2574
  sharedDialogs,
572
2575
  ConfirmDialogElement
573
2576
  ] });
574
2577
  }
575
- return /* @__PURE__ */ jsxs(Page, { className: "space-y-0 overflow-x-hidden", children: [
576
- /* @__PURE__ */ jsx("div", { className: "shrink-0 border-b border-border bg-background px-3 py-2 md:px-6 md:py-3", children: /* @__PURE__ */ jsx(
577
- FormHeader,
578
- {
579
- mode: "detail",
580
- backHref: "/backend/definitions",
581
- backLabel: t("workflows.definitions.backToList", "Back to definitions"),
582
- title: definitionId ? workflowName || t("workflows.definitions.singular") : t("workflows.backend.definitions.visual_editor.title"),
583
- subtitle: definitionId ? t("workflows.definitions.detail.summary", "Editing workflow definition") : t("workflows.definitions.create.summary", "Create and edit workflow definitions visually with a drag-and-drop interface"),
584
- actionsContent: /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center justify-end gap-1 md:gap-2", children: [
585
- /* @__PURE__ */ jsxs(
586
- Button,
587
- {
588
- variant: "outline",
589
- size: "sm",
590
- onClick: () => setShowMetadata(!showMetadata),
591
- disabled: isSaving,
592
- className: "h-8 px-2 text-xs",
593
- "aria-label": showMetadata ? t("workflows.visualEditor.hideMetadata") : t("workflows.visualEditor.showMetadata"),
594
- children: [
595
- showMetadata ? /* @__PURE__ */ jsx(PanelTopClose, { className: "mr-1.5 h-4 w-4" }) : /* @__PURE__ */ jsx(PanelTopOpen, { className: "mr-1.5 h-4 w-4" }),
596
- showMetadata ? t("workflows.visualEditor.hideMetadata") : t("workflows.visualEditor.showMetadata")
597
- ]
598
- }
599
- ),
600
- !isCodeOnly && /* @__PURE__ */ jsx(
601
- Button,
602
- {
603
- variant: "outline",
604
- size: "sm",
605
- onClick: handleLoadExample,
606
- disabled: isSaving,
607
- className: "h-8 text-xs",
608
- children: t("workflows.visualEditor.loadExample")
609
- }
610
- ),
611
- !isCodeOnly && /* @__PURE__ */ jsxs(
612
- Button,
613
- {
614
- variant: "destructive",
615
- size: "sm",
616
- onClick: handleClear,
617
- disabled: isSaving,
618
- className: "h-8 px-2 text-xs",
619
- "aria-label": t("workflows.visualEditor.clear"),
620
- children: [
621
- /* @__PURE__ */ jsx(Trash2, { className: "mr-1.5 h-4 w-4" }),
622
- t("workflows.visualEditor.clear")
623
- ]
624
- }
625
- ),
626
- /* @__PURE__ */ jsxs(
627
- Button,
628
- {
629
- variant: "outline",
630
- size: "sm",
631
- onClick: handleValidate,
632
- disabled: isSaving,
633
- className: "h-8 px-2 text-xs",
634
- "aria-label": t("workflows.visualEditor.validate"),
635
- children: [
636
- /* @__PURE__ */ jsx(CircleQuestionMark, { className: "mr-1.5 h-4 w-4" }),
637
- t("workflows.visualEditor.validate")
638
- ]
639
- }
640
- ),
641
- !isCodeOnly && /* @__PURE__ */ jsxs(
642
- Button,
643
- {
644
- variant: "outline",
645
- size: "sm",
646
- onClick: handleTest,
647
- disabled: isSaving,
648
- className: "h-8 text-xs",
649
- children: [
650
- /* @__PURE__ */ jsx(Play, { className: "mr-1.5 h-4 w-4" }),
651
- t("workflows.visualEditor.runTest")
652
- ]
653
- }
654
- ),
655
- isCodeOverride && /* @__PURE__ */ jsx(
656
- Button,
657
- {
658
- variant: "outline",
659
- size: "sm",
660
- onClick: handleResetToCode,
661
- disabled: isSaving,
662
- className: "h-8 text-xs",
663
- children: t("workflows.actions.resetToCode")
664
- }
665
- ),
666
- isCodeOnly ? /* @__PURE__ */ jsx(
667
- Button,
668
- {
669
- size: "sm",
670
- onClick: handleCustomize,
671
- disabled: isSaving,
672
- className: "h-8 px-2 text-xs md:px-3",
673
- children: t("workflows.actions.customize")
674
- }
675
- ) : /* @__PURE__ */ jsxs(
676
- Button,
677
- {
678
- size: "sm",
679
- onClick: handleSave,
680
- disabled: isSaving,
681
- className: "h-8 px-2 text-xs md:px-3",
682
- "aria-label": isSaving ? t("workflows.mobile.saving") : definitionId ? t("workflows.common.update") : t("workflows.common.save"),
683
- children: [
684
- /* @__PURE__ */ jsx(Save, { className: "mr-1.5 h-4 w-4" }),
685
- isSaving ? t("workflows.mobile.saving") : definitionId ? t("workflows.common.update") : t("workflows.common.save")
686
- ]
687
- }
688
- )
689
- ] })
690
- }
691
- ) }),
692
- (isCodeOnly || isCodeOverride) && /* @__PURE__ */ jsxs("div", { className: "shrink-0 border-b border-border bg-background px-3 py-2 md:px-6 md:py-3", children: [
693
- isCodeOnly && /* @__PURE__ */ jsx(Alert, { status: "information", children: /* @__PURE__ */ jsx(AlertTitle, { children: t("workflows.source.code.readonlyBanner") }) }),
694
- isCodeOverride && /* @__PURE__ */ jsx(Alert, { status: "warning", children: /* @__PURE__ */ jsx(AlertTitle, { children: t("workflows.source.code_override.banner") }) })
695
- ] }),
696
- showMetadata && /* @__PURE__ */ jsxs("div", { className: isCompactViewport ? "shrink-0 border-b border-border bg-background px-3 py-2 max-h-[60svh] overflow-y-auto overscroll-contain md:px-6 md:py-3" : "shrink-0 border-b border-border bg-background px-3 py-2 md:px-6 md:py-3", children: [
697
- /* @__PURE__ */ jsxs("fieldset", { disabled: isCodeOnly, className: "rounded-lg border bg-card p-3 disabled:opacity-70 md:p-4", children: [
698
- /* @__PURE__ */ jsx("h2", { className: "mb-3 text-xs font-semibold uppercase text-muted-foreground", children: t("workflows.visualEditor.workflowMetadata") }),
699
- /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-3 md:gap-4", children: [
700
- /* @__PURE__ */ jsxs("div", { className: "min-w-0 space-y-1", children: [
701
- /* @__PURE__ */ jsxs(Label, { htmlFor: "workflowId", className: "text-xs", children: [
702
- t("workflows.form.workflowId"),
703
- " *"
704
- ] }),
705
- /* @__PURE__ */ jsx(
706
- Input,
2578
+ const fullBleedSideMargin = "calc(-1 * (max(0px, (100vw - var(--app-content-offset, 0px) - 1536px) / 2) + 24px))";
2579
+ const fullBleedStyle = !isCompactViewport ? {
2580
+ width: "calc(100vw - var(--app-content-offset, 0px))",
2581
+ marginLeft: fullBleedSideMargin,
2582
+ marginRight: fullBleedSideMargin
2583
+ } : {};
2584
+ return /* @__PURE__ */ jsxs(
2585
+ Page,
2586
+ {
2587
+ className: "flex min-h-[calc(100svh-7rem)] flex-col space-y-0 overflow-x-hidden",
2588
+ style: fullBleedStyle,
2589
+ children: [
2590
+ /* @__PURE__ */ jsx("div", { className: `shrink-0 border-b border-border bg-background ${focusMode ? "px-3 py-1.5 md:px-4" : "px-3 py-2 md:px-6 md:py-3"}`, children: /* @__PURE__ */ jsx(
2591
+ FormHeader,
2592
+ {
2593
+ mode: "detail",
2594
+ backHref: "/backend/definitions",
2595
+ backLabel: t("workflows.definitions.backToList", "Back to definitions"),
2596
+ title: definitionId ? workflowName || t("workflows.definitions.singular") : t("workflows.backend.definitions.visual_editor.title"),
2597
+ subtitle: focusMode ? void 0 : definitionId ? t("workflows.definitions.detail.summary", "Editing workflow definition") : t("workflows.definitions.create.summary", "Create and edit workflow definitions visually with a drag-and-drop interface"),
2598
+ actionsContent: /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center justify-end gap-1 md:gap-2", children: [
2599
+ draftEligible && (draftSaveFailed || draftSavedLabel) && /* @__PURE__ */ jsx("span", { role: "status", className: "text-xs text-muted-foreground", children: draftSaveFailed ? t("workflows.visualEditor.draft.saveFailed", "Draft not saved") : t("workflows.visualEditor.draft.saved", "Draft saved {time}", { time: draftSavedLabel ?? "" }) }),
2600
+ !isCodeOnly && definitionId && autosaveState !== "idle" && /* @__PURE__ */ jsx("span", { className: "mr-1 text-xs text-muted-foreground", "aria-live": "polite", children: autosaveState === "saving" ? t("workflows.visualEditor.autosaving") : t("workflows.visualEditor.autosaved") }),
2601
+ focusMode ? (
2602
+ // Focus mode collapses the toolbar to the essentials; the toggle
2603
+ // stays reachable so the author can leave, and Cmd+K still opens
2604
+ // every command.
2605
+ /* @__PURE__ */ jsxs(
2606
+ Button,
2607
+ {
2608
+ variant: "outline",
2609
+ size: "sm",
2610
+ onClick: toggleFocus,
2611
+ disabled: isSaving,
2612
+ className: "h-8 px-2 text-xs",
2613
+ "aria-label": t("workflows.visualEditor.exitFocusMode"),
2614
+ children: [
2615
+ /* @__PURE__ */ jsx(Minimize2, { className: "mr-1.5 h-4 w-4" }),
2616
+ t("workflows.visualEditor.exitFocusMode")
2617
+ ]
2618
+ }
2619
+ )
2620
+ ) : /* @__PURE__ */ jsxs(Fragment, { children: [
2621
+ /* @__PURE__ */ jsxs(
2622
+ Button,
2623
+ {
2624
+ variant: "outline",
2625
+ size: "sm",
2626
+ onClick: handleValidate,
2627
+ disabled: isSaving,
2628
+ className: "h-8 px-2 text-xs",
2629
+ "aria-label": problemErrorCount > 0 ? t("workflows.visualEditor.validateWithErrors", "{count} validation errors", { count: problemErrorCount }) : t("workflows.visualEditor.validate"),
2630
+ children: [
2631
+ problemErrorCount > 0 ? /* @__PURE__ */ jsx(CircleAlert, { className: "mr-1.5 h-4 w-4 text-destructive", "aria-hidden": "true" }) : /* @__PURE__ */ jsx(CircleQuestionMark, { className: "mr-1.5 h-4 w-4", "aria-hidden": "true" }),
2632
+ t("workflows.visualEditor.validate"),
2633
+ problemErrorCount > 0 && /* @__PURE__ */ jsx("span", { className: "ml-1.5 inline-flex h-4 min-w-4 items-center justify-center rounded-full bg-destructive px-1 text-xs font-semibold text-white", children: problemErrorCount })
2634
+ ]
2635
+ }
2636
+ ),
2637
+ (() => {
2638
+ const moreActions = [];
2639
+ moreActions.push({ id: "group-tools", header: t("workflows.visualEditor.menuGroups.tools", "Tools") });
2640
+ moreActions.push({ id: "commands", label: t("workflows.commandPalette.buttonLabel", "Commands"), icon: Command, onSelect: () => setShowCommandPalette(true) });
2641
+ if (!isCodeOnly) moreActions.push({ id: "ai-draft", label: t("workflows.aiDraft.toggleShort", "AI draft"), icon: Sparkles, onSelect: () => setShowAiDraft(true) });
2642
+ moreActions.push({ id: "group-view", header: t("workflows.visualEditor.menuGroups.view", "View") });
2643
+ moreActions.push({ id: "focus", label: t("workflows.visualEditor.enterFocusMode"), icon: Maximize2, onSelect: toggleFocus, disabled: isSaving });
2644
+ moreActions.push({ id: "settings", label: t("workflows.visualEditor.metadata.buttonLabel", "Settings"), icon: metadataIncomplete ? TriangleAlert : PanelRightOpen, onSelect: () => setMetadataOpen(true), disabled: isSaving });
2645
+ moreActions.push({ id: "code", label: t("workflows.visualEditor.codeView.title", "Code"), icon: Code, onSelect: () => setShowCodeView(true) });
2646
+ moreActions.push({ id: "group-canvas", header: t("workflows.visualEditor.menuGroups.canvas", "Canvas") });
2647
+ if (!isCodeOnly) moreActions.push({ id: "load-example", label: t("workflows.visualEditor.loadExample"), onSelect: handleOpenTemplateGallery, disabled: isSaving });
2648
+ if (!isCodeOnly) moreActions.push({ id: "auto-arrange", label: t("workflows.visualEditor.autoArrange"), icon: Network, onSelect: handleAutoArrange, disabled: isSaving || nodes.length === 0 });
2649
+ moreActions.push({ id: "compensation", label: t("workflows.compensation.toggleShort", "Compensation"), icon: ShieldMinus, onSelect: toggleCompensation });
2650
+ if (isCodeOverride) moreActions.push({ id: "reset-to-code", label: t("workflows.actions.resetToCode"), onSelect: handleResetToCode, disabled: isSaving });
2651
+ if (!isCodeOnly) {
2652
+ moreActions.push({ separator: true, id: "sep-destructive" });
2653
+ moreActions.push({ id: "clear", label: t("workflows.visualEditor.clear"), icon: Trash2, onSelect: handleClear, disabled: isSaving, destructive: true });
2654
+ }
2655
+ return /* @__PURE__ */ jsx(
2656
+ ActionsDropdown,
2657
+ {
2658
+ items: moreActions,
2659
+ label: t("workflows.visualEditor.more", "More"),
2660
+ ariaLabel: t("workflows.visualEditor.more", "More"),
2661
+ triggerClassName: "h-8 px-2 text-xs",
2662
+ triggerIcon: false,
2663
+ attention: metadataIncomplete
2664
+ }
2665
+ );
2666
+ })(),
2667
+ definitionId && /* @__PURE__ */ jsxs(
2668
+ Button,
2669
+ {
2670
+ variant: "outline",
2671
+ size: "sm",
2672
+ onClick: toggleLastRun,
2673
+ "aria-pressed": showLastRun,
2674
+ className: "h-8 px-2 text-xs",
2675
+ "aria-label": t("workflows.lastRun.toggle", "Show the last run on the canvas"),
2676
+ children: [
2677
+ /* @__PURE__ */ jsx(History, { className: "mr-1.5 h-4 w-4", "aria-hidden": "true" }),
2678
+ t("workflows.lastRun.toggleShort", "Last run"),
2679
+ showLastRun && lastRunOverlay.isUnavailable ? /* @__PURE__ */ jsx("span", { className: "ml-1.5 text-muted-foreground", children: t("workflows.lastRun.never", "(never run)") }) : null
2680
+ ]
2681
+ }
2682
+ ),
2683
+ definitionId && /* @__PURE__ */ jsxs(
2684
+ Button,
2685
+ {
2686
+ variant: "outline",
2687
+ size: "sm",
2688
+ onClick: () => setStartOpen(true),
2689
+ disabled: isSaving,
2690
+ className: "h-8 text-xs",
2691
+ children: [
2692
+ /* @__PURE__ */ jsx(Play, { className: "mr-1.5 h-4 w-4" }),
2693
+ t("workflows.actions.startInstance")
2694
+ ]
2695
+ }
2696
+ )
2697
+ ] }),
2698
+ isCodeOnly ? /* @__PURE__ */ jsx(
2699
+ Button,
2700
+ {
2701
+ size: "sm",
2702
+ onClick: handleCustomize,
2703
+ disabled: isSaving,
2704
+ className: "h-8 px-2 text-xs md:px-3",
2705
+ children: t("workflows.actions.customize")
2706
+ }
2707
+ ) : /* @__PURE__ */ jsxs(
2708
+ Button,
2709
+ {
2710
+ size: "sm",
2711
+ onClick: handleSave,
2712
+ disabled: isSaving,
2713
+ className: "h-8 px-2 text-xs md:px-3",
2714
+ "aria-label": isSaving ? t("workflows.mobile.saving") : definitionId ? t("workflows.common.update") : t("workflows.common.save"),
2715
+ children: [
2716
+ /* @__PURE__ */ jsx(Save, { className: "mr-1.5 h-4 w-4" }),
2717
+ isSaving ? t("workflows.mobile.saving") : definitionId ? t("workflows.common.update") : t("workflows.common.save")
2718
+ ]
2719
+ }
2720
+ )
2721
+ ] })
2722
+ }
2723
+ ) }),
2724
+ (isCodeOnly || isCodeOverride) && /* @__PURE__ */ jsxs("div", { className: "shrink-0 border-b border-border bg-background px-3 py-2 md:px-6 md:py-3", children: [
2725
+ isCodeOnly && /* @__PURE__ */ jsx(Alert, { status: "information", children: /* @__PURE__ */ jsx(AlertTitle, { children: t("workflows.source.code.readonlyBanner") }) }),
2726
+ isCodeOverride && /* @__PURE__ */ jsx(Alert, { status: "warning", children: /* @__PURE__ */ jsx(AlertTitle, { children: t("workflows.source.code_override.banner") }) })
2727
+ ] }),
2728
+ structuralConflictBanner,
2729
+ draftRestoreBanner,
2730
+ isCompactViewport ? /* @__PURE__ */ jsxs("div", { className: "px-3 py-3 md:px-6 md:py-4", children: [
2731
+ /* @__PURE__ */ jsxs("div", { className: "relative min-w-0", children: [
2732
+ /* @__PURE__ */ jsx("div", { className: "h-[64svh] min-h-[360px] rounded-lg border bg-card", children: /* @__PURE__ */ jsx(
2733
+ WorkflowGraph,
707
2734
  {
708
- id: "workflowId",
709
- value: workflowId,
710
- onChange: (e) => setWorkflowId(e.target.value),
711
- placeholder: "checkout_workflow",
712
- disabled: !!definitionId,
713
- className: "h-8 text-sm"
2735
+ initialNodes: nodes,
2736
+ initialEdges: edges,
2737
+ onNodesChange: handleNodesChange,
2738
+ onEdgesChange: handleEdgesChange,
2739
+ onNodeClick: handleNodeClick,
2740
+ onEdgeClick: handleEdgeClick,
2741
+ onConnect: handleConnect,
2742
+ onReconnect: handleReconnect,
2743
+ onCanvasDrop: handleCanvasDrop,
2744
+ editable: !isCodeOnly,
2745
+ height: "100%",
2746
+ focusTarget,
2747
+ nodeErrorCounts,
2748
+ showCompensation,
2749
+ runOverlay: lastRunOverlay.execution,
2750
+ runAgentLabels: lastRunOverlay.agentLabels,
2751
+ triggers,
2752
+ definitionEnabled: enabled,
2753
+ onOpenTriggers: handleOpenTriggers
714
2754
  }
715
- ),
716
- definitionId && /* @__PURE__ */ jsx("p", { className: "text-overline text-muted-foreground", children: t("workflows.visualEditor.readOnly") })
2755
+ ) }),
2756
+ nodes.length === 0 && /* @__PURE__ */ jsx("div", { className: "pointer-events-none absolute inset-0 flex items-center justify-center px-4", children: /* @__PURE__ */ jsxs("div", { className: "text-center", children: [
2757
+ /* @__PURE__ */ jsx("h2", { className: "mb-2 text-lg font-semibold text-foreground", children: t("workflows.visualEditor.startBuilding") }),
2758
+ /* @__PURE__ */ jsx("p", { className: "mb-4 text-sm text-muted-foreground", children: t("workflows.visualEditor.tapToAddBelow") }),
2759
+ /* @__PURE__ */ jsx(
2760
+ "button",
2761
+ {
2762
+ onClick: handleOpenTemplateGallery,
2763
+ className: "pointer-events-auto text-sm text-primary hover:underline",
2764
+ children: t("workflows.visualEditor.loadExampleWorkflow")
2765
+ }
2766
+ )
2767
+ ] }) })
717
2768
  ] }),
718
- /* @__PURE__ */ jsxs("div", { className: "min-w-0 space-y-1", children: [
719
- /* @__PURE__ */ jsxs(Label, { htmlFor: "workflowName", className: "text-xs", children: [
720
- t("workflows.form.workflowName"),
721
- " *"
2769
+ !isCodeOnly && /* @__PURE__ */ jsxs("div", { className: "mt-3 rounded-lg border bg-card p-3", children: [
2770
+ /* @__PURE__ */ jsx("h2", { className: "mb-2 text-xs font-semibold uppercase text-muted-foreground", children: t("workflows.visualEditor.stepPalette") }),
2771
+ /* @__PURE__ */ jsx("p", { className: "mb-3 text-xs text-muted-foreground", children: t("workflows.visualEditor.tapToAdd") }),
2772
+ /* @__PURE__ */ jsx("div", { className: "flex gap-2 overflow-x-auto pb-1", children: ["start", "userTask", "automated", "invokeAgent", "waitForSignal", "waitForTimer", "subWorkflow", "end"].map((nodeType) => {
2773
+ const Icon = NODE_TYPE_ICONS[nodeType];
2774
+ return /* @__PURE__ */ jsxs(
2775
+ "button",
2776
+ {
2777
+ draggable: true,
2778
+ onDragStart: (event) => handlePaletteStepDragStart(event, nodeType),
2779
+ onClick: () => handleAddNode(nodeType),
2780
+ className: "flex shrink-0 items-center gap-1 rounded-md border bg-background px-2 py-1 text-xs hover:bg-muted active:bg-muted/50",
2781
+ children: [
2782
+ /* @__PURE__ */ jsx(Icon, { className: "h-3.5 w-3.5" }),
2783
+ /* @__PURE__ */ jsx("span", { children: NODE_TYPE_LABELS[nodeType].title })
2784
+ ]
2785
+ },
2786
+ nodeType
2787
+ );
2788
+ }) })
2789
+ ] })
2790
+ ] }) : /* @__PURE__ */ jsxs("div", { "data-testid": "workflow-editor-row", className: "flex min-h-0 min-w-0 flex-1 border-t border-border pl-3 md:pl-6", children: [
2791
+ !isCodeOnly && /* @__PURE__ */ jsxs("div", { className: `${paletteCollapsed ? "w-14" : "w-48"} shrink-0 overflow-y-auto border-r border-border bg-background p-2`, children: [
2792
+ /* @__PURE__ */ jsxs("div", { className: `mb-2 flex items-center ${paletteCollapsed ? "justify-center" : "justify-between"}`, children: [
2793
+ !paletteCollapsed && /* @__PURE__ */ jsx("h2", { className: "px-1 text-xs font-semibold uppercase text-muted-foreground", children: t("workflows.visualEditor.stepPalette") }),
2794
+ /* @__PURE__ */ jsx(
2795
+ IconButton,
2796
+ {
2797
+ type: "button",
2798
+ variant: "ghost",
2799
+ size: "sm",
2800
+ onClick: togglePaletteCollapsed,
2801
+ title: paletteCollapsed ? t("workflows.visualEditor.expandPalette") : t("workflows.visualEditor.collapsePalette"),
2802
+ "aria-label": paletteCollapsed ? t("workflows.visualEditor.expandPalette") : t("workflows.visualEditor.collapsePalette"),
2803
+ children: paletteCollapsed ? /* @__PURE__ */ jsx(PanelLeftOpen, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx(PanelLeftClose, { className: "h-4 w-4" })
2804
+ }
2805
+ )
722
2806
  ] }),
723
- /* @__PURE__ */ jsx(
724
- Input,
725
- {
726
- id: "workflowName",
727
- value: workflowName,
728
- onChange: (e) => setWorkflowName(e.target.value),
729
- placeholder: "Checkout Process",
730
- className: "h-8 text-sm"
731
- }
732
- )
733
- ] }),
734
- /* @__PURE__ */ jsxs("div", { className: "min-w-0 space-y-1", children: [
735
- /* @__PURE__ */ jsx(Label, { htmlFor: "category", className: "text-xs", children: t("workflows.form.category") }),
736
- /* @__PURE__ */ jsx(
737
- Input,
738
- {
739
- id: "category",
740
- value: category,
741
- onChange: (e) => setCategory(e.target.value),
742
- placeholder: "E-Commerce",
743
- className: "h-8 text-sm"
744
- }
745
- )
746
- ] }),
747
- /* @__PURE__ */ jsxs("div", { className: "min-w-0 space-y-1 sm:col-span-2 lg:col-span-3", children: [
748
- /* @__PURE__ */ jsx(Label, { htmlFor: "description", className: "text-xs", children: t("workflows.form.description") }),
749
- /* @__PURE__ */ jsx(
750
- Textarea,
751
- {
752
- id: "description",
753
- value: description,
754
- onChange: (e) => setDescription(e.target.value),
755
- placeholder: t("workflows.form.placeholders.description"),
756
- rows: 2,
757
- className: "min-h-[60px] text-sm"
2807
+ !paletteCollapsed && /* @__PURE__ */ jsx("p", { className: "mb-2 px-1 text-xs text-muted-foreground", children: t("workflows.visualEditor.clickToAdd") }),
2808
+ /* @__PURE__ */ jsx("div", { className: `flex flex-col gap-1 ${paletteCollapsed ? "items-center" : ""}`, children: PALETTE_NODE_TYPES.map((nodeType) => {
2809
+ const Icon = NODE_TYPE_ICONS[nodeType];
2810
+ const label = NODE_TYPE_LABELS[nodeType];
2811
+ const tooltip = `${label.title} \u2014 ${label.description}`;
2812
+ if (paletteCollapsed) {
2813
+ return /* @__PURE__ */ jsx(
2814
+ "button",
2815
+ {
2816
+ type: "button",
2817
+ draggable: true,
2818
+ onDragStart: (event) => handlePaletteStepDragStart(event, nodeType),
2819
+ onClick: () => handleAddNode(nodeType),
2820
+ title: tooltip,
2821
+ "aria-label": tooltip,
2822
+ className: "flex h-9 w-9 items-center justify-center rounded-md border hover:bg-muted",
2823
+ children: /* @__PURE__ */ jsx(Icon, { className: `h-4 w-4 ${NODE_TYPE_COLORS[nodeType]}` })
2824
+ },
2825
+ nodeType
2826
+ );
758
2827
  }
759
- )
760
- ] }),
761
- /* @__PURE__ */ jsxs("div", { className: "min-w-0 space-y-1", children: [
762
- /* @__PURE__ */ jsxs(Label, { htmlFor: "version", className: "text-xs", children: [
763
- t("workflows.form.version"),
764
- " *"
2828
+ return /* @__PURE__ */ jsxs(
2829
+ "button",
2830
+ {
2831
+ type: "button",
2832
+ draggable: true,
2833
+ onDragStart: (event) => handlePaletteStepDragStart(event, nodeType),
2834
+ onClick: () => handleAddNode(nodeType),
2835
+ title: label.description,
2836
+ className: "flex w-full items-center gap-2 rounded-md border px-2 py-1.5 text-left text-xs hover:bg-muted",
2837
+ children: [
2838
+ /* @__PURE__ */ jsx(Icon, { className: `h-4 w-4 shrink-0 ${NODE_TYPE_COLORS[nodeType]}` }),
2839
+ /* @__PURE__ */ jsx("span", { className: "truncate font-medium text-foreground", children: label.title })
2840
+ ]
2841
+ },
2842
+ nodeType
2843
+ );
2844
+ }) }),
2845
+ !paletteCollapsed && /* @__PURE__ */ jsxs("div", { className: "mt-4", children: [
2846
+ /* @__PURE__ */ jsx("h2", { className: "mb-1 px-1 text-xs font-semibold uppercase text-muted-foreground", children: t("workflows.visualEditor.palette.actionsTitle", "Actions") }),
2847
+ /* @__PURE__ */ jsx("p", { className: "mb-2 px-1 text-xs text-muted-foreground", children: t("workflows.visualEditor.palette.dragActionHint", "Drop an action onto a route to add it there") }),
2848
+ /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-1", children: activityTypeOptions.map((option) => {
2849
+ const ActionIcon = resolveActivityIcon(option.value);
2850
+ return /* @__PURE__ */ jsxs(
2851
+ "button",
2852
+ {
2853
+ type: "button",
2854
+ draggable: true,
2855
+ onDragStart: (event) => handlePaletteActivityDragStart(event, option.value, option.label),
2856
+ onClick: () => flash(t("workflows.visualEditor.palette.dragActionHint", "Drop an action onto a route to add it there"), "info"),
2857
+ title: t("workflows.visualEditor.palette.dragActionHint", "Drop an action onto a route to add it there"),
2858
+ className: "flex w-full items-center gap-2 rounded-md border px-2 py-1.5 text-left text-xs hover:bg-muted",
2859
+ children: [
2860
+ /* @__PURE__ */ jsx(ActionIcon, { className: "h-4 w-4 shrink-0 text-muted-foreground" }),
2861
+ /* @__PURE__ */ jsx("span", { className: "truncate font-medium text-foreground", children: option.label })
2862
+ ]
2863
+ },
2864
+ option.value
2865
+ );
2866
+ }) })
765
2867
  ] }),
766
- /* @__PURE__ */ jsx(
767
- Input,
768
- {
769
- id: "version",
770
- type: "number",
771
- value: version,
772
- onChange: (e) => setVersion(parseInt(e.target.value) || 1),
773
- min: 1,
774
- disabled: !!definitionId,
775
- className: "h-8 text-sm"
776
- }
777
- )
778
- ] }),
779
- /* @__PURE__ */ jsxs("div", { className: "min-w-0 space-y-1", children: [
780
- /* @__PURE__ */ jsx(Label, { className: "text-xs", children: t("common.enabled", "Enabled") }),
781
- /* @__PURE__ */ jsxs("div", { className: "flex h-8 items-center gap-2", children: [
782
- /* @__PURE__ */ jsx(
783
- Switch,
2868
+ !paletteCollapsed && /* @__PURE__ */ jsxs("div", { className: "mt-4", children: [
2869
+ /* @__PURE__ */ jsx("h2", { className: "mb-1 px-1 text-xs font-semibold uppercase text-muted-foreground", children: t("workflows.annotations.paletteTitle", "Annotations") }),
2870
+ /* @__PURE__ */ jsx("p", { className: "mb-2 px-1 text-xs text-muted-foreground", children: t("workflows.annotations.paletteHint", "Documentation only \u2014 never executed") }),
2871
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", children: [
2872
+ /* @__PURE__ */ jsxs(
2873
+ "button",
2874
+ {
2875
+ type: "button",
2876
+ onClick: () => handleAddAnnotation("note"),
2877
+ title: t("workflows.annotations.note.addHint", "Add a markdown sticky note to the canvas"),
2878
+ className: "flex w-full items-center gap-2 rounded-md border px-2 py-1.5 text-left text-xs hover:bg-muted",
2879
+ children: [
2880
+ /* @__PURE__ */ jsx(StickyNote, { className: "h-4 w-4 shrink-0 text-status-warning-text", "aria-hidden": "true" }),
2881
+ /* @__PURE__ */ jsx("span", { className: "truncate font-medium text-foreground", children: t("workflows.annotations.note.add", "Note") })
2882
+ ]
2883
+ }
2884
+ ),
2885
+ /* @__PURE__ */ jsxs(
2886
+ "button",
2887
+ {
2888
+ type: "button",
2889
+ onClick: () => handleAddAnnotation("group"),
2890
+ title: t("workflows.annotations.group.addHint", "Add a named region around part of the canvas"),
2891
+ className: "flex w-full items-center gap-2 rounded-md border px-2 py-1.5 text-left text-xs hover:bg-muted",
2892
+ children: [
2893
+ /* @__PURE__ */ jsx(Group, { className: "h-4 w-4 shrink-0 text-muted-foreground", "aria-hidden": "true" }),
2894
+ /* @__PURE__ */ jsx("span", { className: "truncate font-medium text-foreground", children: t("workflows.annotations.group.add", "Group") })
2895
+ ]
2896
+ }
2897
+ )
2898
+ ] })
2899
+ ] }),
2900
+ !paletteCollapsed && /* @__PURE__ */ jsxs("div", { className: "mt-3", children: [
2901
+ /* @__PURE__ */ jsxs(
2902
+ "button",
784
2903
  {
785
- id: "enabled",
786
- checked: enabled,
787
- onCheckedChange: setEnabled
2904
+ type: "button",
2905
+ onClick: () => setShowPaletteHowTo((prev) => !prev),
2906
+ "aria-expanded": showPaletteHowTo,
2907
+ className: "flex w-full items-center gap-1.5 rounded-md px-1 py-1 text-xs text-muted-foreground hover:text-foreground",
2908
+ children: [
2909
+ /* @__PURE__ */ jsx(CircleQuestionMark, { className: "h-3.5 w-3.5" }),
2910
+ /* @__PURE__ */ jsx("span", { children: t("workflows.visualEditor.howToUse", "How to use:") })
2911
+ ]
788
2912
  }
789
2913
  ),
790
- /* @__PURE__ */ jsx(Label, { htmlFor: "enabled", className: "cursor-pointer text-xs font-normal", children: enabled ? t("common.on", "On") : t("common.off", "Off") })
2914
+ showPaletteHowTo && /* @__PURE__ */ jsxs(Alert, { variant: "info", className: "mt-2", children: [
2915
+ /* @__PURE__ */ jsx(AlertTitle, { className: "text-xs", children: t("workflows.visualEditor.howToUse", "How to use:") }),
2916
+ /* @__PURE__ */ jsx("div", { className: "mt-2", children: /* @__PURE__ */ jsxs("ul", { className: "list-inside list-disc space-y-1 text-xs", children: [
2917
+ /* @__PURE__ */ jsx("li", { children: t("workflows.visualEditor.hint.addSteps", "Click step types to add them") }),
2918
+ /* @__PURE__ */ jsx("li", { children: t("workflows.visualEditor.hint.dragFromPalette", "Drag a step onto the canvas, or onto a route to insert it there") }),
2919
+ /* @__PURE__ */ jsx("li", { children: t("workflows.visualEditor.hint.dragSteps", "Drag steps to position them") }),
2920
+ /* @__PURE__ */ jsx("li", { children: t("workflows.visualEditor.hint.connectSteps", "Connect steps by dragging from handles") }),
2921
+ /* @__PURE__ */ jsx("li", { children: t("workflows.visualEditor.hint.editSteps", "Click steps and transitions to edit them") }),
2922
+ /* @__PURE__ */ jsx("li", { children: t("workflows.visualEditor.hint.copyPaste", "Select steps and copy, paste or duplicate them with Cmd/Ctrl+C, +V and +D") }),
2923
+ /* @__PURE__ */ jsx("li", { children: t("workflows.visualEditor.hint.commandPalette", "Press Cmd/Ctrl+K for every command without the mouse") }),
2924
+ /* @__PURE__ */ jsx("li", { children: t("workflows.visualEditor.hint.keyboardCanvas", "With a step selected: Enter opens it, Del removes it, arrows nudge it (hold Shift for bigger steps)") }),
2925
+ /* @__PURE__ */ jsx("li", { children: t("workflows.visualEditor.hint.validate", "Validate before saving") })
2926
+ ] }) })
2927
+ ] })
791
2928
  ] })
792
2929
  ] }),
793
- /* @__PURE__ */ jsxs("div", { className: "min-w-0 space-y-1", children: [
794
- /* @__PURE__ */ jsx(Label, { className: "text-xs", children: t("workflows.form.tags") }),
795
- /* @__PURE__ */ jsx(
796
- TagsInput,
2930
+ /* @__PURE__ */ jsx("div", { className: "min-w-0 flex-1 p-6", children: /* @__PURE__ */ jsxs("div", { className: "relative h-full min-h-[480px]", children: [
2931
+ /* @__PURE__ */ jsx("div", { className: "h-full rounded-lg border bg-card", children: /* @__PURE__ */ jsx(
2932
+ WorkflowGraph,
797
2933
  {
798
- value: tags,
799
- onChange: setTags,
800
- placeholder: t("workflows.form.placeholders.tags")
2934
+ initialNodes: nodes,
2935
+ initialEdges: edges,
2936
+ onNodesChange: handleNodesChange,
2937
+ onEdgesChange: handleEdgesChange,
2938
+ onNodeClick: handleNodeClick,
2939
+ onEdgeClick: handleEdgeClick,
2940
+ onConnect: handleConnect,
2941
+ onReconnect: handleReconnect,
2942
+ onCanvasDrop: handleCanvasDrop,
2943
+ editable: !isCodeOnly,
2944
+ height: "100%",
2945
+ focusTarget,
2946
+ nodeErrorCounts,
2947
+ showCompensation,
2948
+ runOverlay: lastRunOverlay.execution,
2949
+ runAgentLabels: lastRunOverlay.agentLabels,
2950
+ triggers,
2951
+ definitionEnabled: enabled,
2952
+ onOpenTriggers: handleOpenTriggers
801
2953
  }
802
- )
803
- ] }),
804
- /* @__PURE__ */ jsxs("div", { className: "min-w-0 space-y-1", children: [
805
- /* @__PURE__ */ jsx(Label, { htmlFor: "icon", className: "text-xs", children: t("workflows.form.icon") }),
806
- /* @__PURE__ */ jsx(
807
- Input,
808
- {
809
- id: "icon",
810
- value: icon,
811
- onChange: (e) => setIcon(e.target.value),
812
- placeholder: "ShoppingCart",
813
- className: "h-8 text-sm"
814
- }
815
- )
816
- ] }),
817
- /* @__PURE__ */ jsxs("div", { className: "min-w-0 space-y-1", children: [
818
- /* @__PURE__ */ jsx(Label, { htmlFor: "effectiveFrom", className: "text-xs", children: t("workflows.form.effectiveFrom") }),
819
- /* @__PURE__ */ jsx(
820
- Input,
821
- {
822
- id: "effectiveFrom",
823
- type: "date",
824
- value: effectiveFrom,
825
- onChange: (e) => setEffectiveFrom(e.target.value),
826
- className: "h-8 text-sm"
827
- }
828
- )
829
- ] }),
830
- /* @__PURE__ */ jsxs("div", { className: "min-w-0 space-y-1", children: [
831
- /* @__PURE__ */ jsx(Label, { htmlFor: "effectiveTo", className: "text-xs", children: t("workflows.form.effectiveTo") }),
832
- /* @__PURE__ */ jsx(
833
- Input,
834
- {
835
- id: "effectiveTo",
836
- type: "date",
837
- value: effectiveTo,
838
- onChange: (e) => setEffectiveTo(e.target.value),
839
- className: "h-8 text-sm"
840
- }
841
- )
842
- ] })
843
- ] })
844
- ] }),
845
- /* @__PURE__ */ jsx("fieldset", { disabled: isCodeOnly, className: "mt-3 disabled:opacity-70", children: /* @__PURE__ */ jsx(
846
- DefinitionTriggersEditor,
847
- {
848
- value: triggers,
849
- onChange: setTriggers
850
- }
851
- ) })
852
- ] }),
853
- isCompactViewport ? /* @__PURE__ */ jsxs("div", { className: "px-3 py-3 md:px-6 md:py-4", children: [
854
- /* @__PURE__ */ jsxs("div", { className: "relative min-w-0", children: [
855
- /* @__PURE__ */ jsx("div", { className: "h-[64svh] min-h-[360px] rounded-lg border bg-card", children: /* @__PURE__ */ jsx(
856
- WorkflowGraph,
857
- {
858
- initialNodes: nodes,
859
- initialEdges: edges,
860
- onNodesChange: handleNodesChange,
861
- onEdgesChange: handleEdgesChange,
862
- onNodeClick: handleNodeClick,
863
- onEdgeClick: handleEdgeClick,
864
- onConnect: handleConnect,
865
- editable: !isCodeOnly,
866
- height: "100%"
867
- }
868
- ) }),
869
- nodes.length === 0 && /* @__PURE__ */ jsx("div", { className: "pointer-events-none absolute inset-0 flex items-center justify-center px-4", children: /* @__PURE__ */ jsxs("div", { className: "text-center", children: [
870
- /* @__PURE__ */ jsx("h2", { className: "mb-2 text-lg font-semibold text-foreground", children: t("workflows.visualEditor.startBuilding") }),
871
- /* @__PURE__ */ jsx("p", { className: "mb-4 text-sm text-muted-foreground", children: t("workflows.visualEditor.tapToAddBelow") }),
872
- /* @__PURE__ */ jsx(
873
- "button",
874
- {
875
- onClick: handleLoadExample,
876
- className: "pointer-events-auto text-sm text-primary hover:underline",
877
- children: t("workflows.visualEditor.loadExampleWorkflow")
878
- }
879
- )
880
- ] }) })
881
- ] }),
882
- !isCodeOnly && /* @__PURE__ */ jsxs("div", { className: "mt-3 rounded-lg border bg-card p-3", children: [
883
- /* @__PURE__ */ jsx("h2", { className: "mb-2 text-xs font-semibold uppercase text-muted-foreground", children: t("workflows.visualEditor.stepPalette") }),
884
- /* @__PURE__ */ jsx("p", { className: "mb-3 text-xs text-muted-foreground", children: t("workflows.visualEditor.tapToAdd") }),
885
- /* @__PURE__ */ jsx("div", { className: "flex gap-2 overflow-x-auto pb-1", children: ["start", "userTask", "automated", "waitForSignal", "waitForTimer", "subWorkflow", "end"].map((nodeType) => {
886
- const Icon = NODE_TYPE_ICONS[nodeType];
887
- return /* @__PURE__ */ jsxs(
888
- "button",
889
- {
890
- onClick: () => handleAddNode(nodeType),
891
- className: "flex shrink-0 items-center gap-1 rounded-md border bg-background px-2 py-1 text-xs hover:bg-muted active:bg-muted/50",
892
- children: [
893
- /* @__PURE__ */ jsx(Icon, { className: "h-3.5 w-3.5" }),
894
- /* @__PURE__ */ jsx("span", { children: NODE_TYPE_LABELS[nodeType].title })
895
- ]
896
- },
897
- nodeType
898
- );
899
- }) })
900
- ] })
901
- ] }) : /* @__PURE__ */ jsxs("div", { className: "flex min-h-[72svh] min-w-0 flex-1 border-t border-border", children: [
902
- !isCodeOnly && /* @__PURE__ */ jsx("div", { className: "w-[24rem] shrink-0 overflow-y-auto border-r border-border bg-background p-6", children: /* @__PURE__ */ jsxs("div", { className: "rounded-lg border bg-card p-4", children: [
903
- /* @__PURE__ */ jsx("h2", { className: "mb-2 text-sm font-semibold uppercase text-muted-foreground", children: t("workflows.visualEditor.stepPalette") }),
904
- /* @__PURE__ */ jsx("p", { className: "mb-4 text-xs text-muted-foreground", children: t("workflows.visualEditor.clickToAdd") }),
905
- /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
906
- /* @__PURE__ */ jsxs(
907
- "button",
908
- {
909
- onClick: () => handleAddNode("start"),
910
- className: "group relative w-full cursor-pointer rounded-xl border-2 border-border bg-background px-4 py-3 text-left transition-all hover:border-muted-foreground/30 hover:shadow-md",
911
- children: [
912
- /* @__PURE__ */ jsx("div", { className: `absolute right-2 top-2 ${NODE_TYPE_COLORS.start} opacity-60 transition-opacity group-hover:opacity-100`, children: (() => {
913
- const Icon = NODE_TYPE_ICONS.start;
914
- return /* @__PURE__ */ jsx(Icon, { className: "h-4 w-4" });
915
- })() }),
916
- /* @__PURE__ */ jsx("div", { className: "text-sm font-semibold text-foreground", children: NODE_TYPE_LABELS.start.title }),
917
- /* @__PURE__ */ jsx("div", { className: "mt-0.5 text-xs text-muted-foreground", children: NODE_TYPE_LABELS.start.description })
918
- ]
919
- }
920
- ),
921
- /* @__PURE__ */ jsxs(
922
- "button",
923
- {
924
- onClick: () => handleAddNode("userTask"),
925
- className: "group relative w-full cursor-pointer rounded-xl border-2 border-border bg-background px-4 py-3 text-left transition-all hover:border-muted-foreground/30 hover:shadow-md",
926
- children: [
927
- /* @__PURE__ */ jsx("div", { className: `absolute right-2 top-2 ${NODE_TYPE_COLORS.userTask} opacity-60 transition-opacity group-hover:opacity-100`, children: (() => {
928
- const Icon = NODE_TYPE_ICONS.userTask;
929
- return /* @__PURE__ */ jsx(Icon, { className: "h-4 w-4" });
930
- })() }),
931
- /* @__PURE__ */ jsx("div", { className: "text-sm font-semibold text-foreground", children: NODE_TYPE_LABELS.userTask.title }),
932
- /* @__PURE__ */ jsx("div", { className: "mt-0.5 text-xs text-muted-foreground", children: NODE_TYPE_LABELS.userTask.description })
933
- ]
934
- }
935
- ),
936
- /* @__PURE__ */ jsxs(
937
- "button",
938
- {
939
- onClick: () => handleAddNode("automated"),
940
- className: "group relative w-full cursor-pointer rounded-xl border-2 border-border bg-background px-4 py-3 text-left transition-all hover:border-muted-foreground/30 hover:shadow-md",
941
- children: [
942
- /* @__PURE__ */ jsx("div", { className: `absolute right-2 top-2 ${NODE_TYPE_COLORS.automated} opacity-60 transition-opacity group-hover:opacity-100`, children: (() => {
943
- const Icon = NODE_TYPE_ICONS.automated;
944
- return /* @__PURE__ */ jsx(Icon, { className: "h-4 w-4" });
945
- })() }),
946
- /* @__PURE__ */ jsx("div", { className: "text-sm font-semibold text-foreground", children: NODE_TYPE_LABELS.automated.title }),
947
- /* @__PURE__ */ jsx("div", { className: "mt-0.5 text-xs text-muted-foreground", children: NODE_TYPE_LABELS.automated.description })
948
- ]
949
- }
950
- ),
951
- /* @__PURE__ */ jsxs(
952
- "button",
953
- {
954
- onClick: () => handleAddNode("waitForSignal"),
955
- className: "group relative w-full cursor-pointer rounded-xl border-2 border-border bg-background px-4 py-3 text-left transition-all hover:border-muted-foreground/30 hover:shadow-md",
956
- children: [
957
- /* @__PURE__ */ jsx("div", { className: `absolute right-2 top-2 ${NODE_TYPE_COLORS.waitForSignal} opacity-60 transition-opacity group-hover:opacity-100`, children: (() => {
958
- const Icon = NODE_TYPE_ICONS.waitForSignal;
959
- return /* @__PURE__ */ jsx(Icon, { className: "h-4 w-4" });
960
- })() }),
961
- /* @__PURE__ */ jsx("div", { className: "text-sm font-semibold text-foreground", children: NODE_TYPE_LABELS.waitForSignal.title }),
962
- /* @__PURE__ */ jsx("div", { className: "mt-0.5 text-xs text-muted-foreground", children: NODE_TYPE_LABELS.waitForSignal.description })
963
- ]
964
- }
965
- ),
966
- /* @__PURE__ */ jsxs(
967
- "button",
968
- {
969
- onClick: () => handleAddNode("waitForTimer"),
970
- className: "group relative w-full cursor-pointer rounded-xl border-2 border-border bg-background px-4 py-3 text-left transition-all hover:border-muted-foreground/30 hover:shadow-md",
971
- children: [
972
- /* @__PURE__ */ jsx("div", { className: `absolute right-2 top-2 ${NODE_TYPE_COLORS.waitForTimer} opacity-60 transition-opacity group-hover:opacity-100`, children: (() => {
973
- const Icon = NODE_TYPE_ICONS.waitForTimer;
974
- return /* @__PURE__ */ jsx(Icon, { className: "h-4 w-4" });
975
- })() }),
976
- /* @__PURE__ */ jsx("div", { className: "text-sm font-semibold text-foreground", children: NODE_TYPE_LABELS.waitForTimer.title }),
977
- /* @__PURE__ */ jsx("div", { className: "mt-0.5 text-xs text-muted-foreground", children: NODE_TYPE_LABELS.waitForTimer.description })
978
- ]
979
- }
980
- ),
981
- /* @__PURE__ */ jsxs(
982
- "button",
983
- {
984
- onClick: () => handleAddNode("subWorkflow"),
985
- className: "group relative w-full cursor-pointer rounded-xl border-2 border-border bg-background px-4 py-3 text-left transition-all hover:border-muted-foreground/30 hover:shadow-md",
986
- children: [
987
- /* @__PURE__ */ jsx("div", { className: `absolute right-2 top-2 ${NODE_TYPE_COLORS.subWorkflow} opacity-60 transition-opacity group-hover:opacity-100`, children: (() => {
988
- const Icon = NODE_TYPE_ICONS.subWorkflow;
989
- return /* @__PURE__ */ jsx(Icon, { className: "h-4 w-4" });
990
- })() }),
991
- /* @__PURE__ */ jsx("div", { className: "text-sm font-semibold text-foreground", children: NODE_TYPE_LABELS.subWorkflow.title }),
992
- /* @__PURE__ */ jsx("div", { className: "mt-0.5 text-xs text-muted-foreground", children: NODE_TYPE_LABELS.subWorkflow.description })
993
- ]
994
- }
995
- ),
996
- /* @__PURE__ */ jsxs(
997
- "button",
998
- {
999
- onClick: () => handleAddNode("end"),
1000
- className: "group relative w-full cursor-pointer rounded-xl border-2 border-border bg-background px-4 py-3 text-left transition-all hover:border-muted-foreground/30 hover:shadow-md",
1001
- children: [
1002
- /* @__PURE__ */ jsx("div", { className: `absolute right-2 top-2 ${NODE_TYPE_COLORS.end} opacity-60 transition-opacity group-hover:opacity-100`, children: (() => {
1003
- const Icon = NODE_TYPE_ICONS.end;
1004
- return /* @__PURE__ */ jsx(Icon, { className: "h-4 w-4" });
1005
- })() }),
1006
- /* @__PURE__ */ jsx("div", { className: "text-sm font-semibold text-foreground", children: NODE_TYPE_LABELS.end.title }),
1007
- /* @__PURE__ */ jsx("div", { className: "mt-0.5 text-xs text-muted-foreground", children: NODE_TYPE_LABELS.end.description })
1008
- ]
1009
- }
1010
- )
2954
+ ) }),
2955
+ nodes.length === 0 && /* @__PURE__ */ jsx("div", { className: "pointer-events-none absolute inset-0 flex items-center justify-center px-4", children: /* @__PURE__ */ jsxs("div", { className: "text-center", children: [
2956
+ /* @__PURE__ */ jsx("h2", { className: "mb-2 text-xl font-semibold text-foreground", children: t("workflows.visualEditor.startBuilding") }),
2957
+ /* @__PURE__ */ jsx("p", { className: "mb-4 text-muted-foreground", children: t("workflows.visualEditor.clickToAddFromPalette") }),
2958
+ /* @__PURE__ */ jsx(
2959
+ "button",
2960
+ {
2961
+ onClick: handleOpenTemplateGallery,
2962
+ className: "pointer-events-auto text-sm text-primary hover:underline",
2963
+ children: t("workflows.visualEditor.loadExampleWorkflow")
2964
+ }
2965
+ )
2966
+ ] }) })
2967
+ ] }) }),
2968
+ inspectorsDocked ? inspectorPanels : null
1011
2969
  ] }),
1012
- /* @__PURE__ */ jsxs(Alert, { status: "information", className: "mt-6", children: [
1013
- /* @__PURE__ */ jsx(AlertTitle, { className: "text-xs", children: t("workflows.visualEditor.howToUse", "How to use:") }),
1014
- /* @__PURE__ */ jsx("div", { className: "mt-2", children: /* @__PURE__ */ jsxs("ul", { className: "list-inside list-disc space-y-1 text-xs", children: [
1015
- /* @__PURE__ */ jsx("li", { children: t("workflows.visualEditor.hint.addSteps", "Click step types to add them") }),
1016
- /* @__PURE__ */ jsx("li", { children: t("workflows.visualEditor.hint.dragSteps", "Drag steps to position them") }),
1017
- /* @__PURE__ */ jsx("li", { children: t("workflows.visualEditor.hint.connectSteps", "Connect steps by dragging from handles") }),
1018
- /* @__PURE__ */ jsx("li", { children: t("workflows.visualEditor.hint.editSteps", "Click steps and transitions to edit them") }),
1019
- /* @__PURE__ */ jsx("li", { children: t("workflows.visualEditor.hint.validate", "Validate before saving") })
1020
- ] }) })
1021
- ] })
1022
- ] }) }),
1023
- /* @__PURE__ */ jsx("div", { className: "min-w-0 flex-1 p-6", children: /* @__PURE__ */ jsxs("div", { className: "relative h-[72svh] min-h-[640px]", children: [
1024
- /* @__PURE__ */ jsx("div", { className: "h-full rounded-lg border bg-card", children: /* @__PURE__ */ jsx(
1025
- WorkflowGraph,
1026
- {
1027
- initialNodes: nodes,
1028
- initialEdges: edges,
1029
- onNodesChange: handleNodesChange,
1030
- onEdgesChange: handleEdgesChange,
1031
- onNodeClick: handleNodeClick,
1032
- onEdgeClick: handleEdgeClick,
1033
- onConnect: handleConnect,
1034
- editable: !isCodeOnly,
1035
- height: "100%"
1036
- }
1037
- ) }),
1038
- nodes.length === 0 && /* @__PURE__ */ jsx("div", { className: "pointer-events-none absolute inset-0 flex items-center justify-center px-4", children: /* @__PURE__ */ jsxs("div", { className: "text-center", children: [
1039
- /* @__PURE__ */ jsx("h2", { className: "mb-2 text-xl font-semibold text-foreground", children: t("workflows.visualEditor.startBuilding") }),
1040
- /* @__PURE__ */ jsx("p", { className: "mb-4 text-muted-foreground", children: t("workflows.visualEditor.clickToAddFromPalette") }),
1041
- /* @__PURE__ */ jsx(
1042
- "button",
1043
- {
1044
- onClick: handleLoadExample,
1045
- className: "pointer-events-auto text-sm text-primary hover:underline",
1046
- children: t("workflows.visualEditor.loadExampleWorkflow")
1047
- }
1048
- )
1049
- ] }) })
1050
- ] }) })
1051
- ] }),
1052
- sharedDialogs,
1053
- ConfirmDialogElement
1054
- ] });
2970
+ problemsPanel,
2971
+ sharedDialogs,
2972
+ ConfirmDialogElement
2973
+ ]
2974
+ }
2975
+ );
1055
2976
  }
1056
2977
  function getDefaultLabel(nodeType) {
1057
2978
  const labels = {
@@ -1061,7 +2982,11 @@ function getDefaultLabel(nodeType) {
1061
2982
  automated: "New Automated Task",
1062
2983
  decision: "Decision Point",
1063
2984
  waitForSignal: "Wait for Signal",
1064
- waitForTimer: "Wait for Timer"
2985
+ waitForTimer: "Wait for Timer",
2986
+ waitForCondition: "Wait for Condition",
2987
+ invokeAgent: "Invoke Agent",
2988
+ ifElse: "If / Else",
2989
+ switch: "Switch"
1065
2990
  };
1066
2991
  return labels[nodeType] || "New Step";
1067
2992
  }
@@ -1073,7 +2998,11 @@ function getDefaultBadge(nodeType) {
1073
2998
  automated: "Automated",
1074
2999
  decision: "Decision",
1075
3000
  waitForSignal: "Wait for Signal",
1076
- waitForTimer: "Wait for Timer"
3001
+ waitForTimer: "Wait for Timer",
3002
+ waitForCondition: "Wait for Condition",
3003
+ invokeAgent: "Invoke Agent",
3004
+ ifElse: "If / Else",
3005
+ switch: "Switch"
1077
3006
  };
1078
3007
  return badges[nodeType] || "Task";
1079
3008
  }