@oomol-lab/open-flow 0.1.0-beta.73 → 0.1.0-beta.75

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 (444) hide show
  1. package/LICENSE +36 -0
  2. package/NOTICE +7 -0
  3. package/README.md +41 -0
  4. package/dist/browser/{ContentIcon-CUQHjQIw.js → ContentIcon-FCpKiwjp.js} +2 -1
  5. package/dist/browser/{FlowCanvas-Dtf8_cQp.js → FlowCanvas-dzRBnlUT.js} +1 -1
  6. package/dist/browser/{createResourceDialog-BWJkjiOO.js → createResourceDialog-BeYDdUl-.js} +2 -2
  7. package/dist/browser/{dialog-CUy_fwx9.js → dialog-ZtpsFbl4.js} +337 -31
  8. package/dist/browser/{exportSvg-BOoy7-Db.js → exportSvg-DqXw1yf-.js} +2 -2
  9. package/dist/browser/{flowChanges-CgSnSqqP.js → flowChanges-b_5UobFj.js} +586 -87
  10. package/dist/browser/{flowWorkspace-Bf7H-r0t.js → flowWorkspace-ItojZbO4.js} +21162 -20242
  11. package/dist/browser/preview.css +254 -179
  12. package/dist/browser/preview.js +22201 -20625
  13. package/dist/browser/{typeScriptSession-B0hC-C8y.js → typeScriptSession-CSRwWmZm.js} +1 -1
  14. package/dist/browser/ui.css +72 -2
  15. package/dist/browser/workbench.css +254 -179
  16. package/dist/browser/workbench.js +22 -78
  17. package/dist/browser/{workbenchSelect-C3e4J93X.js → workbenchSelect-M441Lz0d.js} +1 -1
  18. package/dist/command/index.js +42 -42
  19. package/dist/common/authoring.d.ts +5 -5
  20. package/dist/common/authoringExamples.d.ts +3 -2
  21. package/dist/common/authoringFields.d.ts +1 -1
  22. package/dist/common/authoringSchema.d.ts +95 -8
  23. package/dist/common/connector-proxy.d.ts +1 -1
  24. package/dist/common/control-api-conformance.d.ts +1 -1
  25. package/dist/common/control-api-conformance.js +119 -31
  26. package/dist/common/control-api.d.ts +4 -3
  27. package/dist/common/control-api.js +117 -29
  28. package/dist/common/control-requests.d.ts +5 -4
  29. package/dist/common/control-requests.js +437 -103
  30. package/dist/common/cron-trigger.d.ts +1 -1
  31. package/dist/common/decision.d.ts +1 -1
  32. package/dist/common/draftOperations.d.ts +1 -1
  33. package/dist/common/engine-contract.d.ts +4 -2
  34. package/dist/common/event-source-definition.d.ts +1 -1
  35. package/dist/common/flow-agent.d.ts +1 -1
  36. package/dist/{browser → common}/flow-authoring-node.d.ts +3 -3
  37. package/dist/{browser → common}/flow-authoring.js +131 -30
  38. package/dist/{browser → common}/flow-change-schema.d.ts +177 -6
  39. package/dist/{browser → common}/flow-change.d.ts +35 -15
  40. package/dist/{browser → common}/flow-change.js +133 -32
  41. package/dist/common/flow-connectionUsage.d.ts +1 -1
  42. package/dist/common/flow-encoding.d.ts +1 -1
  43. package/dist/common/flow-encoding.js +89 -23
  44. package/dist/common/flow-errorHandling.d.ts +1 -1
  45. package/dist/common/flow-graph.d.ts +3 -2
  46. package/dist/common/flow-modules.d.ts +1 -1
  47. package/dist/common/flow-promptTemplate.d.ts +1 -1
  48. package/dist/common/flow-retry.d.ts +2 -2
  49. package/dist/common/flow-semantics.d.ts +1 -1
  50. package/dist/common/flow-semantics.js +966 -614
  51. package/dist/common/flow-workflowFile.d.ts +1 -1
  52. package/dist/common/flowInspection.d.ts +3 -3
  53. package/dist/common/http-request.d.ts +156 -0
  54. package/dist/common/http-request.js +5254 -0
  55. package/dist/common/integration-trigger.d.ts +1 -1
  56. package/dist/common/licenses.md +118 -1
  57. package/dist/common/liveDetails.d.ts +1 -1
  58. package/dist/common/mcp.d.ts +5 -4
  59. package/dist/common/mcp.js +117 -29
  60. package/dist/common/openapi.d.ts +1 -1
  61. package/dist/common/poll-trigger.d.ts +1 -1
  62. package/dist/common/run-events.js +11 -9
  63. package/dist/common/run-results.d.ts +8 -1
  64. package/dist/common/runtime-contract.d.ts +8 -2
  65. package/dist/common/runtime-contract.js +25 -5
  66. package/dist/common/scheduler-checkpoint.d.ts +1 -1
  67. package/dist/common/scheduler-dependencies.d.ts +1 -1
  68. package/dist/common/scheduler-node-execution.d.ts +1 -1
  69. package/dist/common/scheduler.d.ts +3 -3
  70. package/dist/common/scheduler.js +101 -33
  71. package/dist/common/trigger-catalog.d.ts +1 -1
  72. package/dist/common/trigger-config-options.d.ts +1 -1
  73. package/dist/common/trigger-config.d.ts +1 -1
  74. package/dist/common/trigger-contract.d.ts +2 -2
  75. package/dist/common/trigger-localization.d.ts +1 -1
  76. package/dist/common/webhook-response.d.ts +1 -3
  77. package/dist/common/webhook-trigger.d.ts +1 -1
  78. package/dist/common/webhook-trigger.js +12 -11
  79. package/dist/common/workflow.d.ts +17 -1
  80. package/dist/common/workflow.js +68 -22
  81. package/dist/index.d.ts +6 -3
  82. package/package.json +11 -6
  83. package/skills/open-flow/references/flow-authoring.md +1 -0
  84. package/skills/open-flow/references/flow-n8n-conversion.md +19 -15
  85. package/src/base/common/json.ts +90 -0
  86. package/src/canvas/common/commands.ts +15 -0
  87. package/src/connector/common/actionSchema.ts +168 -0
  88. package/src/connector/common/proxy.ts +44 -0
  89. package/src/control/common/api.ts +1506 -0
  90. package/src/control/common/authoring.ts +923 -0
  91. package/src/control/common/authoringCondition.ts +110 -0
  92. package/src/control/common/authoringDiagnosticFields.ts +136 -0
  93. package/src/control/common/authoringExamples.ts +200 -0
  94. package/src/control/common/authoringFields.ts +53 -0
  95. package/src/control/common/authoringLlm.ts +70 -0
  96. package/src/control/common/authoringOpenApi.ts +129 -0
  97. package/src/control/common/authoringSchema.ts +410 -0
  98. package/src/control/common/authoringTools.ts +123 -0
  99. package/src/control/common/authoringTriggers.ts +26 -0
  100. package/src/control/common/checkDecoders.ts +65 -0
  101. package/src/control/common/conformance.ts +1989 -0
  102. package/src/control/common/connectorDecoders.ts +370 -0
  103. package/src/control/common/connectorQueries.ts +61 -0
  104. package/src/control/common/decoding.ts +60 -0
  105. package/src/control/common/draftOperations.ts +61 -0
  106. package/src/control/common/errors.ts +114 -0
  107. package/src/control/common/eventSourceSchemas.ts +13 -0
  108. package/src/control/common/eventSources.ts +186 -0
  109. package/src/control/common/flowDecoders.ts +153 -0
  110. package/src/control/common/flowInspection.ts +181 -0
  111. package/src/control/common/flowNotifications.ts +37 -0
  112. package/src/control/common/liveDetails.ts +124 -0
  113. package/src/control/common/mcp.ts +241 -0
  114. package/src/control/common/mcpConformance.ts +142 -0
  115. package/src/control/common/providerAccess.ts +28 -0
  116. package/src/control/common/providerIconSprite.ts +42 -0
  117. package/src/control/common/publicationDecoders.ts +98 -0
  118. package/src/control/common/random.ts +16 -0
  119. package/src/control/common/requests.ts +112 -0
  120. package/src/control/common/resultQuery.ts +8 -0
  121. package/src/control/common/results.ts +176 -0
  122. package/src/control/common/revisionDecoders.ts +52 -0
  123. package/src/control/common/runDecoders.ts +240 -0
  124. package/src/control/common/triggerCatalog.ts +89 -0
  125. package/src/control/common/triggerDecoders.ts +175 -0
  126. package/src/decision/common/decision.ts +168 -0
  127. package/src/event-source/common/catalog.generated.json +65 -0
  128. package/src/event-source/common/definition.ts +66 -0
  129. package/src/event-source/common/index.ts +99 -0
  130. package/src/execution/common/checkpoint.ts +311 -0
  131. package/src/execution/common/dependencies.ts +440 -0
  132. package/src/execution/common/engineContract.ts +84 -0
  133. package/src/execution/common/events.ts +331 -0
  134. package/src/execution/common/node-execution.ts +469 -0
  135. package/src/execution/common/runLifecycle.ts +250 -0
  136. package/src/execution/common/runtime.ts +563 -0
  137. package/src/execution/common/scheduler.ts +697 -0
  138. package/src/execution/common/wait.ts +36 -0
  139. package/src/flow/common/agent.ts +305 -0
  140. package/src/flow/common/authoring.ts +30 -0
  141. package/src/flow/common/change.ts +1037 -0
  142. package/src/flow/common/changeSchema.ts +792 -0
  143. package/src/flow/common/codePermissions.ts +2 -0
  144. package/src/flow/common/condition.ts +265 -0
  145. package/src/flow/common/connectionUsage.ts +131 -0
  146. package/src/flow/common/edgeChanges.ts +35 -0
  147. package/src/flow/common/encoding.ts +437 -0
  148. package/src/flow/common/errorHandling.ts +34 -0
  149. package/src/flow/common/graph.ts +898 -0
  150. package/src/flow/common/inputValue.ts +16 -0
  151. package/src/flow/common/json.ts +8 -0
  152. package/src/flow/common/moduleChanges.ts +31 -0
  153. package/src/flow/common/modules.ts +292 -0
  154. package/src/flow/common/nodeChanges.ts +762 -0
  155. package/src/flow/common/portChanges.ts +36 -0
  156. package/src/flow/common/promptTemplate.ts +15 -0
  157. package/src/flow/common/retry.ts +20 -0
  158. package/src/flow/common/schema.ts +162 -0
  159. package/src/flow/common/schemaCompare.ts +176 -0
  160. package/src/flow/common/schemaComparer.ts +178 -0
  161. package/src/flow/common/schemaKeywords.ts +53 -0
  162. package/src/flow/common/semantics.ts +531 -0
  163. package/src/flow/common/sourceField.ts +35 -0
  164. package/src/flow/common/triggerScheduleSchema.ts +8 -0
  165. package/src/flow/common/wait.ts +30 -0
  166. package/src/flow/common/webhookMethod.ts +7 -0
  167. package/src/flow/common/webhookResponse.ts +71 -0
  168. package/src/flow/common/workflowFile.ts +97 -0
  169. package/src/http/common/authBindings.ts +12 -0
  170. package/src/http/common/headers.ts +5 -0
  171. package/src/http/common/httpRequest.ts +251 -0
  172. package/src/json-schema-subset/checker/array.ts +142 -0
  173. package/src/json-schema-subset/checker/cache.ts +57 -0
  174. package/src/json-schema-subset/checker/checker.ts +230 -0
  175. package/src/json-schema-subset/checker/enumAndConst.ts +113 -0
  176. package/src/json-schema-subset/checker/equals.ts +93 -0
  177. package/src/json-schema-subset/checker/numeric/checker.ts +50 -0
  178. package/src/json-schema-subset/checker/numeric/float.ts +121 -0
  179. package/src/json-schema-subset/checker/numeric/index.ts +1 -0
  180. package/src/json-schema-subset/checker/numeric/integer.ts +119 -0
  181. package/src/json-schema-subset/checker/numeric/integerNormalizer.ts +72 -0
  182. package/src/json-schema-subset/checker/object.ts +354 -0
  183. package/src/json-schema-subset/checker/range.ts +91 -0
  184. package/src/json-schema-subset/checker/string.ts +60 -0
  185. package/src/json-schema-subset/checker/tools/index.ts +3 -0
  186. package/src/json-schema-subset/checker/tools/mark.ts +82 -0
  187. package/src/json-schema-subset/checker/tools/quantum.ts +106 -0
  188. package/src/json-schema-subset/checker/tools/range.ts +210 -0
  189. package/src/json-schema-subset/checker/validator.ts +23 -0
  190. package/src/json-schema-subset/compiler/array.ts +163 -0
  191. package/src/json-schema-subset/compiler/combination.ts +109 -0
  192. package/src/json-schema-subset/compiler/common.ts +99 -0
  193. package/src/json-schema-subset/compiler/compiler.ts +188 -0
  194. package/src/json-schema-subset/compiler/enumAndConst.ts +75 -0
  195. package/src/json-schema-subset/compiler/index.ts +11 -0
  196. package/src/json-schema-subset/compiler/object.ts +411 -0
  197. package/src/json-schema-subset/compiler/primitive.ts +144 -0
  198. package/src/json-schema-subset/compiler/utils.ts +24 -0
  199. package/src/json-schema-subset/compiler/wrapper.ts +192 -0
  200. package/src/json-schema-subset/error.ts +55 -0
  201. package/src/json-schema-subset/expression/calculator.ts +442 -0
  202. package/src/json-schema-subset/expression/combination.ts +49 -0
  203. package/src/json-schema-subset/expression/expression.ts +191 -0
  204. package/src/json-schema-subset/expression/index.ts +25 -0
  205. package/src/json-schema-subset/expression/result.ts +88 -0
  206. package/src/json-schema-subset/expression/types.ts +36 -0
  207. package/src/json-schema-subset/extends/extendsContext.ts +89 -0
  208. package/src/json-schema-subset/extends/index.ts +2 -0
  209. package/src/json-schema-subset/extends/types.ts +12 -0
  210. package/src/json-schema-subset/global.ts +60 -0
  211. package/src/json-schema-subset/index.ts +8 -0
  212. package/src/json-schema-subset/preprocessing.ts +74 -0
  213. package/src/json-schema-subset/types.ts +8 -0
  214. package/src/json-schema-subset/utils.ts +95 -0
  215. package/src/localization/common/languages.ts +118 -0
  216. package/src/localization/common/timeZone.ts +25 -0
  217. package/src/openapi/common/openapi.ts +459 -0
  218. package/src/trigger/common/config.ts +56 -0
  219. package/src/trigger/common/configOptions.ts +15 -0
  220. package/src/trigger/common/configSchema.ts +8 -0
  221. package/src/trigger/common/contract.ts +132 -0
  222. package/src/trigger/common/cron.ts +151 -0
  223. package/src/trigger/common/integration.ts +498 -0
  224. package/src/trigger/common/poll.ts +187 -0
  225. package/src/trigger/common/webhook.ts +413 -0
  226. package/src/trigger/providers/airtable/locales/fr.json +18 -0
  227. package/src/trigger/providers/airtable/locales/ja.json +18 -0
  228. package/src/trigger/providers/airtable/locales/ko.json +18 -0
  229. package/src/trigger/providers/airtable/locales/ru.json +18 -0
  230. package/src/trigger/providers/airtable/locales/zh-CN.json +18 -0
  231. package/src/trigger/providers/airtable/locales/zh-TW.json +18 -0
  232. package/src/trigger/providers/asana/locales/fr.json +15 -0
  233. package/src/trigger/providers/asana/locales/ja.json +15 -0
  234. package/src/trigger/providers/asana/locales/ko.json +15 -0
  235. package/src/trigger/providers/asana/locales/ru.json +15 -0
  236. package/src/trigger/providers/asana/locales/zh-CN.json +15 -0
  237. package/src/trigger/providers/asana/locales/zh-TW.json +15 -0
  238. package/src/trigger/providers/calendly/locales/fr.json +16 -0
  239. package/src/trigger/providers/calendly/locales/ja.json +16 -0
  240. package/src/trigger/providers/calendly/locales/ko.json +16 -0
  241. package/src/trigger/providers/calendly/locales/ru.json +16 -0
  242. package/src/trigger/providers/calendly/locales/zh-CN.json +16 -0
  243. package/src/trigger/providers/calendly/locales/zh-TW.json +16 -0
  244. package/src/trigger/providers/catalog.generated.json +4996 -0
  245. package/src/trigger/providers/clickup/locales/fr.json +16 -0
  246. package/src/trigger/providers/clickup/locales/ja.json +16 -0
  247. package/src/trigger/providers/clickup/locales/ko.json +16 -0
  248. package/src/trigger/providers/clickup/locales/ru.json +16 -0
  249. package/src/trigger/providers/clickup/locales/zh-CN.json +16 -0
  250. package/src/trigger/providers/clickup/locales/zh-TW.json +16 -0
  251. package/src/trigger/providers/definitions.ts +19 -0
  252. package/src/trigger/providers/feishu/locales/fr.json +20 -0
  253. package/src/trigger/providers/feishu/locales/ja.json +20 -0
  254. package/src/trigger/providers/feishu/locales/ko.json +20 -0
  255. package/src/trigger/providers/feishu/locales/ru.json +20 -0
  256. package/src/trigger/providers/feishu/locales/zh-CN.json +20 -0
  257. package/src/trigger/providers/feishu/locales/zh-TW.json +20 -0
  258. package/src/trigger/providers/fieldDescriptions.ts +392 -0
  259. package/src/trigger/providers/fieldLabels.ts +256 -0
  260. package/src/trigger/providers/fireflies/locales/fr.json +17 -0
  261. package/src/trigger/providers/fireflies/locales/ja.json +17 -0
  262. package/src/trigger/providers/fireflies/locales/ko.json +17 -0
  263. package/src/trigger/providers/fireflies/locales/ru.json +17 -0
  264. package/src/trigger/providers/fireflies/locales/zh-CN.json +17 -0
  265. package/src/trigger/providers/fireflies/locales/zh-TW.json +17 -0
  266. package/src/trigger/providers/github/locales/fr.json +30 -0
  267. package/src/trigger/providers/github/locales/ja.json +30 -0
  268. package/src/trigger/providers/github/locales/ko.json +30 -0
  269. package/src/trigger/providers/github/locales/ru.json +30 -0
  270. package/src/trigger/providers/github/locales/zh-CN.json +30 -0
  271. package/src/trigger/providers/github/locales/zh-TW.json +30 -0
  272. package/src/trigger/providers/gitlab/locales/fr.json +18 -0
  273. package/src/trigger/providers/gitlab/locales/ja.json +18 -0
  274. package/src/trigger/providers/gitlab/locales/ko.json +18 -0
  275. package/src/trigger/providers/gitlab/locales/ru.json +18 -0
  276. package/src/trigger/providers/gitlab/locales/zh-CN.json +18 -0
  277. package/src/trigger/providers/gitlab/locales/zh-TW.json +18 -0
  278. package/src/trigger/providers/gmail/locales/fr.json +18 -0
  279. package/src/trigger/providers/gmail/locales/ja.json +18 -0
  280. package/src/trigger/providers/gmail/locales/ko.json +18 -0
  281. package/src/trigger/providers/gmail/locales/ru.json +18 -0
  282. package/src/trigger/providers/gmail/locales/zh-CN.json +18 -0
  283. package/src/trigger/providers/gmail/locales/zh-TW.json +18 -0
  284. package/src/trigger/providers/google-calendar/locales/fr.json +15 -0
  285. package/src/trigger/providers/google-calendar/locales/ja.json +15 -0
  286. package/src/trigger/providers/google-calendar/locales/ko.json +15 -0
  287. package/src/trigger/providers/google-calendar/locales/ru.json +15 -0
  288. package/src/trigger/providers/google-calendar/locales/zh-CN.json +15 -0
  289. package/src/trigger/providers/google-calendar/locales/zh-TW.json +15 -0
  290. package/src/trigger/providers/google-drive/locales/fr.json +48 -0
  291. package/src/trigger/providers/google-drive/locales/ja.json +48 -0
  292. package/src/trigger/providers/google-drive/locales/ko.json +48 -0
  293. package/src/trigger/providers/google-drive/locales/ru.json +48 -0
  294. package/src/trigger/providers/google-drive/locales/zh-CN.json +48 -0
  295. package/src/trigger/providers/google-drive/locales/zh-TW.json +48 -0
  296. package/src/trigger/providers/google-sheets/locales/fr.json +19 -0
  297. package/src/trigger/providers/google-sheets/locales/ja.json +19 -0
  298. package/src/trigger/providers/google-sheets/locales/ko.json +19 -0
  299. package/src/trigger/providers/google-sheets/locales/ru.json +19 -0
  300. package/src/trigger/providers/google-sheets/locales/zh-CN.json +19 -0
  301. package/src/trigger/providers/google-sheets/locales/zh-TW.json +19 -0
  302. package/src/trigger/providers/intercom/locales/fr.json +17 -0
  303. package/src/trigger/providers/intercom/locales/ja.json +17 -0
  304. package/src/trigger/providers/intercom/locales/ko.json +17 -0
  305. package/src/trigger/providers/intercom/locales/ru.json +17 -0
  306. package/src/trigger/providers/intercom/locales/zh-CN.json +17 -0
  307. package/src/trigger/providers/intercom/locales/zh-TW.json +17 -0
  308. package/src/trigger/providers/jira/locales/fr.json +17 -0
  309. package/src/trigger/providers/jira/locales/ja.json +17 -0
  310. package/src/trigger/providers/jira/locales/ko.json +17 -0
  311. package/src/trigger/providers/jira/locales/ru.json +17 -0
  312. package/src/trigger/providers/jira/locales/zh-CN.json +17 -0
  313. package/src/trigger/providers/jira/locales/zh-TW.json +17 -0
  314. package/src/trigger/providers/linear/locales/fr.json +13 -0
  315. package/src/trigger/providers/linear/locales/ja.json +13 -0
  316. package/src/trigger/providers/linear/locales/ko.json +13 -0
  317. package/src/trigger/providers/linear/locales/ru.json +13 -0
  318. package/src/trigger/providers/linear/locales/zh-CN.json +13 -0
  319. package/src/trigger/providers/linear/locales/zh-TW.json +13 -0
  320. package/src/trigger/providers/locales/fr.ts +71 -0
  321. package/src/trigger/providers/locales/ja.ts +71 -0
  322. package/src/trigger/providers/locales/ko.ts +71 -0
  323. package/src/trigger/providers/locales/ru.ts +71 -0
  324. package/src/trigger/providers/locales/zh-CN.ts +71 -0
  325. package/src/trigger/providers/locales/zh-TW.ts +71 -0
  326. package/src/trigger/providers/localization.ts +59 -0
  327. package/src/trigger/providers/notion/locales/fr.json +16 -0
  328. package/src/trigger/providers/notion/locales/ja.json +16 -0
  329. package/src/trigger/providers/notion/locales/ko.json +16 -0
  330. package/src/trigger/providers/notion/locales/ru.json +16 -0
  331. package/src/trigger/providers/notion/locales/zh-CN.json +16 -0
  332. package/src/trigger/providers/notion/locales/zh-TW.json +16 -0
  333. package/src/trigger/providers/one-drive/locales/fr.json +16 -0
  334. package/src/trigger/providers/one-drive/locales/ja.json +16 -0
  335. package/src/trigger/providers/one-drive/locales/ko.json +16 -0
  336. package/src/trigger/providers/one-drive/locales/ru.json +16 -0
  337. package/src/trigger/providers/one-drive/locales/zh-CN.json +16 -0
  338. package/src/trigger/providers/one-drive/locales/zh-TW.json +16 -0
  339. package/src/trigger/providers/outlook/locales/fr.json +18 -0
  340. package/src/trigger/providers/outlook/locales/ja.json +18 -0
  341. package/src/trigger/providers/outlook/locales/ko.json +18 -0
  342. package/src/trigger/providers/outlook/locales/ru.json +18 -0
  343. package/src/trigger/providers/outlook/locales/zh-CN.json +18 -0
  344. package/src/trigger/providers/outlook/locales/zh-TW.json +18 -0
  345. package/src/trigger/providers/pandadoc/locales/fr.json +12 -0
  346. package/src/trigger/providers/pandadoc/locales/ja.json +12 -0
  347. package/src/trigger/providers/pandadoc/locales/ko.json +12 -0
  348. package/src/trigger/providers/pandadoc/locales/ru.json +12 -0
  349. package/src/trigger/providers/pandadoc/locales/zh-CN.json +12 -0
  350. package/src/trigger/providers/pandadoc/locales/zh-TW.json +12 -0
  351. package/src/trigger/providers/pipedrive/locales/fr.json +15 -0
  352. package/src/trigger/providers/pipedrive/locales/ja.json +15 -0
  353. package/src/trigger/providers/pipedrive/locales/ko.json +15 -0
  354. package/src/trigger/providers/pipedrive/locales/ru.json +15 -0
  355. package/src/trigger/providers/pipedrive/locales/zh-CN.json +15 -0
  356. package/src/trigger/providers/pipedrive/locales/zh-TW.json +15 -0
  357. package/src/trigger/providers/remote.ts +163 -0
  358. package/src/trigger/providers/resend/locales/fr.json +16 -0
  359. package/src/trigger/providers/resend/locales/ja.json +16 -0
  360. package/src/trigger/providers/resend/locales/ko.json +16 -0
  361. package/src/trigger/providers/resend/locales/ru.json +16 -0
  362. package/src/trigger/providers/resend/locales/zh-CN.json +16 -0
  363. package/src/trigger/providers/resend/locales/zh-TW.json +16 -0
  364. package/src/trigger/providers/sentry/locales/fr.json +19 -0
  365. package/src/trigger/providers/sentry/locales/ja.json +19 -0
  366. package/src/trigger/providers/sentry/locales/ko.json +19 -0
  367. package/src/trigger/providers/sentry/locales/ru.json +19 -0
  368. package/src/trigger/providers/sentry/locales/zh-CN.json +19 -0
  369. package/src/trigger/providers/sentry/locales/zh-TW.json +19 -0
  370. package/src/trigger/providers/shopify/locales/fr.json +18 -0
  371. package/src/trigger/providers/shopify/locales/ja.json +18 -0
  372. package/src/trigger/providers/shopify/locales/ko.json +18 -0
  373. package/src/trigger/providers/shopify/locales/ru.json +18 -0
  374. package/src/trigger/providers/shopify/locales/zh-CN.json +18 -0
  375. package/src/trigger/providers/shopify/locales/zh-TW.json +18 -0
  376. package/src/trigger/providers/slack/locales/fr.json +18 -0
  377. package/src/trigger/providers/slack/locales/ja.json +18 -0
  378. package/src/trigger/providers/slack/locales/ko.json +18 -0
  379. package/src/trigger/providers/slack/locales/ru.json +18 -0
  380. package/src/trigger/providers/slack/locales/zh-CN.json +18 -0
  381. package/src/trigger/providers/slack/locales/zh-TW.json +18 -0
  382. package/src/trigger/providers/slackbot/locales/fr.json +23 -0
  383. package/src/trigger/providers/slackbot/locales/ja.json +23 -0
  384. package/src/trigger/providers/slackbot/locales/ko.json +23 -0
  385. package/src/trigger/providers/slackbot/locales/ru.json +23 -0
  386. package/src/trigger/providers/slackbot/locales/zh-CN.json +23 -0
  387. package/src/trigger/providers/slackbot/locales/zh-TW.json +23 -0
  388. package/src/trigger/providers/source-events.ts +25 -0
  389. package/src/trigger/providers/stripe/locales/fr.json +17 -0
  390. package/src/trigger/providers/stripe/locales/ja.json +17 -0
  391. package/src/trigger/providers/stripe/locales/ko.json +17 -0
  392. package/src/trigger/providers/stripe/locales/ru.json +17 -0
  393. package/src/trigger/providers/stripe/locales/zh-CN.json +17 -0
  394. package/src/trigger/providers/stripe/locales/zh-TW.json +17 -0
  395. package/src/trigger/providers/tally/locales/fr.json +12 -0
  396. package/src/trigger/providers/tally/locales/ja.json +12 -0
  397. package/src/trigger/providers/tally/locales/ko.json +12 -0
  398. package/src/trigger/providers/tally/locales/ru.json +12 -0
  399. package/src/trigger/providers/tally/locales/zh-CN.json +12 -0
  400. package/src/trigger/providers/tally/locales/zh-TW.json +12 -0
  401. package/src/trigger/providers/telegram/locales/fr.json +17 -0
  402. package/src/trigger/providers/telegram/locales/ja.json +17 -0
  403. package/src/trigger/providers/telegram/locales/ko.json +17 -0
  404. package/src/trigger/providers/telegram/locales/ru.json +17 -0
  405. package/src/trigger/providers/telegram/locales/zh-CN.json +17 -0
  406. package/src/trigger/providers/telegram/locales/zh-TW.json +17 -0
  407. package/src/trigger/providers/typeform/locales/fr.json +12 -0
  408. package/src/trigger/providers/typeform/locales/ja.json +12 -0
  409. package/src/trigger/providers/typeform/locales/ko.json +12 -0
  410. package/src/trigger/providers/typeform/locales/ru.json +12 -0
  411. package/src/trigger/providers/typeform/locales/zh-CN.json +12 -0
  412. package/src/trigger/providers/typeform/locales/zh-TW.json +12 -0
  413. package/src/trigger/providers/vercel/locales/fr.json +18 -0
  414. package/src/trigger/providers/vercel/locales/ja.json +18 -0
  415. package/src/trigger/providers/vercel/locales/ko.json +18 -0
  416. package/src/trigger/providers/vercel/locales/ru.json +18 -0
  417. package/src/trigger/providers/vercel/locales/zh-CN.json +18 -0
  418. package/src/trigger/providers/vercel/locales/zh-TW.json +18 -0
  419. package/src/trigger/providers/woocommerce/locales/fr.json +19 -0
  420. package/src/trigger/providers/woocommerce/locales/ja.json +19 -0
  421. package/src/trigger/providers/woocommerce/locales/ko.json +19 -0
  422. package/src/trigger/providers/woocommerce/locales/ru.json +19 -0
  423. package/src/trigger/providers/woocommerce/locales/zh-CN.json +19 -0
  424. package/src/trigger/providers/woocommerce/locales/zh-TW.json +19 -0
  425. package/src/trigger/providers/zendesk/locales/fr.json +15 -0
  426. package/src/trigger/providers/zendesk/locales/ja.json +15 -0
  427. package/src/trigger/providers/zendesk/locales/ko.json +15 -0
  428. package/src/trigger/providers/zendesk/locales/ru.json +15 -0
  429. package/src/trigger/providers/zendesk/locales/zh-CN.json +15 -0
  430. package/src/trigger/providers/zendesk/locales/zh-TW.json +15 -0
  431. package/src/trigger/providers/zoom/locales/fr.json +17 -0
  432. package/src/trigger/providers/zoom/locales/ja.json +17 -0
  433. package/src/trigger/providers/zoom/locales/ko.json +17 -0
  434. package/src/trigger/providers/zoom/locales/ru.json +17 -0
  435. package/src/trigger/providers/zoom/locales/zh-CN.json +17 -0
  436. package/src/trigger/providers/zoom/locales/zh-TW.json +17 -0
  437. package/src/types/index.ts +112 -0
  438. package/src/workflow/common/workflow.ts +273 -0
  439. package/dist/browser/lib-DrKav3HG.js +0 -11403
  440. /package/dist/{browser → common}/flow-authoring-edge.d.ts +0 -0
  441. /package/dist/{browser → common}/flow-authoring-module.d.ts +0 -0
  442. /package/dist/{browser → common}/flow-authoring.d.ts +0 -0
  443. /package/dist/{browser → common}/flow-input-value.d.ts +0 -0
  444. /package/dist/{browser → common}/webhook-method.d.ts +0 -0
@@ -0,0 +1,923 @@
1
+ import type {
2
+ ChangeOperation,
3
+ GraphNode,
4
+ InputMapping,
5
+ InputPort,
6
+ JsonValue,
7
+ ManagedTaskDefinition,
8
+ RevisionContent,
9
+ TriggerKeySnapshot,
10
+ } from '../../flow/common/change.ts'
11
+ import type { WorkflowContract, WorkflowCandidate } from '../../workflow/common/workflow.ts'
12
+ import type { ConnectorAction, Diagnostic } from './api.ts'
13
+ import type { AuthoringConditionConfiguration, AuthoringConditionSource } from './authoringCondition.ts'
14
+ import type { AuthoringOpenApiConfig } from './authoringOpenApi.ts'
15
+ import type { AuthoringRead, AuthoringRequest, AuthoringSearch, AuthoringSource, AuthoringType } from './authoringSchema.ts'
16
+ import type { AuthoringAgentTool, AuthoringCapability } from './authoringTools.ts'
17
+
18
+ import { dequal } from 'dequal/lite'
19
+ import { connectorOutputMode } from '../../connector/common/actionSchema.ts'
20
+ import { decisionTask } from '../../decision/common/decision.ts'
21
+ import { supportsNodeRetry, isTriggerNode, applyFlowChanges, decodeChangeOperations, upgradeRevisionContent } from '../../flow/common/change.ts'
22
+ import { nodeInputMappings } from '../../flow/common/condition.ts'
23
+ import { connect, disconnect } from '../../flow/common/edgeChanges.ts'
24
+ import { nodeSystemOutputPorts, supportsNodeErrorHandling } from '../../flow/common/errorHandling.ts'
25
+ import { nodeInputPorts, nodeOutputPorts } from '../../flow/common/graph.ts'
26
+ import { imports as moduleImports } from '../../flow/common/moduleChanges.ts'
27
+ import {
28
+ createAgentTask,
29
+ createCodeTask,
30
+ defaultCodeTaskPorts,
31
+ createCondition,
32
+ createDecisionTask,
33
+ createLlmTask,
34
+ createManagedTask,
35
+ createProviderTrigger,
36
+ createValue,
37
+ createWait,
38
+ createApproval,
39
+ cleanVariableBindings,
40
+ updateStartInputDefs,
41
+ updateOutputsDefs,
42
+ renameFlowOutputSources,
43
+ } from '../../flow/common/nodeChanges.ts'
44
+ import { retryConfigurationSchema } from '../../flow/common/retry.ts'
45
+ import { httpRequestTask, httpRequestConfigSchema, httpRequestInputs } from '../../http/common/httpRequest.ts'
46
+ import { withWorkflowTask, workflowTask, workflowSelectionTask, workflowCapability, workflowAgentTool } from '../../workflow/common/workflow.ts'
47
+ import { conditionConfigurationView, prepareConditionConfiguration } from './authoringCondition.ts'
48
+ import { authoringDiagnosticField } from './authoringDiagnosticFields.ts'
49
+ import { fieldsView, fieldsToPorts } from './authoringFields.ts'
50
+ import { llmConfigurationView, prepareLlmConfiguration, initialAuthoringLlmTask } from './authoringLlm.ts'
51
+ import { AuthoringOpenApiAuthenticationError, openApiConfigView, openApiAuthenticationChoices, prepareOpenApiTask } from './authoringOpenApi.ts'
52
+ import { authoringConfigurations, authoringReadSchema, authoringRequestSchema, authoringSearchSchema } from './authoringSchema.ts'
53
+ import { AuthoringToolInputError, outwardAgentTools, prepareAgentTools, outwardCapabilities, prepareCapabilities } from './authoringTools.ts'
54
+ import { triggerConfiguration } from './authoringTriggers.ts'
55
+ export * from './authoringSchema.ts'
56
+
57
+ const record = (value: unknown): Record<string, unknown> =>
58
+ value != null && typeof value == 'object' && !Array.isArray(value) ? (value as Record<string, unknown>) : {}
59
+ const json = (value: unknown): JsonValue => JSON.parse(JSON.stringify(value)) as JsonValue
60
+ export class AuthoringError extends Error {
61
+ readonly code: string
62
+ readonly details: Record<string, unknown>
63
+ constructor(code: string, message: string, details: Record<string, unknown> = {}) {
64
+ super(message)
65
+ this.code = code
66
+ this.details = details
67
+ this.name = 'AuthoringError'
68
+ }
69
+ }
70
+ function fail(code: string, message: string, details: Record<string, unknown> = {}): never {
71
+ throw new AuthoringError(code, message, details)
72
+ }
73
+ function nodeAt(content: RevisionContent, ref: string): GraphNode {
74
+ return (
75
+ (Object.hasOwn(content.document.graph.nodes, ref) ? content.document.graph.nodes[ref] : undefined) ??
76
+ fail('node.not-found', `Node ${JSON.stringify(ref)} does not exist.`, { node: ref })
77
+ )
78
+ }
79
+ function taskAt(node: GraphNode) {
80
+ return node.kind == 'task' ? node.task : undefined
81
+ }
82
+ const llmReservedInputs = ['model', 'template', 'messages']
83
+ function inputDefinitions(node: GraphNode) {
84
+ if (node.kind == 'condition') return {}
85
+ if (node.kind == 'poll' || node.kind == 'integration')
86
+ return Object.fromEntries(node.definition.configInputs.flatMap((port) => ('handle' in port ? [[port.handle, port]] : [])))
87
+ return nodeInputPorts(node)
88
+ }
89
+ function inputMappings(node: GraphNode) {
90
+ return node.kind == 'condition' ? {} : node.kind == 'poll' || node.kind == 'integration' ? node.config : nodeInputMappings(node)
91
+ }
92
+ function typeOf(node: GraphNode): AuthoringType {
93
+ const task = taskAt(node)
94
+ return node.kind == 'manual' ? 'start' : node.kind != 'task' ? node.kind : task != null && 'executor' in task ? task.executor.kind : 'code'
95
+ }
96
+ function outwardSource(content: RevisionContent, value: unknown): unknown {
97
+ if (Array.isArray(value)) return value.map((v) => outwardSource(content, v))
98
+ if (value == null || typeof value != 'object') return value
99
+ const v = record(value)
100
+ if (v.kind == 'node' && typeof v.nodeId == 'string' && typeof v.output == 'string')
101
+ return { kind: v.system === true ? 'system' : 'output', node: v.nodeId, port: v.output, ...(v.field == null ? {} : { field: v.field }) }
102
+ if (v.kind == 'binding' && typeof v.bindingId == 'string')
103
+ return { kind: 'variable', name: content.document.bindings[v.bindingId]?.target ?? '[missing variable]' }
104
+ // Literal JSON is user data, including objects that resemble references.
105
+ if (v.kind == 'value') return value
106
+ return Object.fromEntries(Object.entries(v).map(([k, item]) => [k, outwardSource(content, item)]))
107
+ }
108
+ function authoringEdges(edges: RevisionContent['document']['graph']['edges']) {
109
+ return edges.map((edge) =>
110
+ edge.sourceHandle == null ? { source: edge.source, target: edge.target } : { source: edge.source, target: edge.target, branch: edge.sourceHandle },
111
+ )
112
+ }
113
+ export function authoringNode(content: RevisionContent, ref: string) {
114
+ const node = nodeAt(content, ref),
115
+ type = typeOf(node),
116
+ task = taskAt(node)
117
+ let config: Record<string, unknown> = {}
118
+ const texts: Record<string, string> = {}
119
+ if (task != null && 'executor' in task) {
120
+ const { kind: _, ...executor } = task.executor
121
+ config = { ...executor }
122
+ if (task.executor.kind == 'agent') {
123
+ const { prompt, tools, notification, ...rest } = task.executor
124
+ const { kind: _agentKind, ...settings } = rest
125
+ config = {
126
+ ...settings,
127
+ inputs: fieldsView([...task.inputs, ...(node.kind == 'task' ? (node.additionalInputs ?? []) : [])]),
128
+ resultSchema: task.outputs.find((port) => 'handle' in port)?.jsonSchema ?? { type: 'string' },
129
+ tools: outwardAgentTools(tools),
130
+ }
131
+ texts.prompt = prompt
132
+ if (notification != null) {
133
+ const { inputDefinitions: _definitions, ...noticeSettings } = notification
134
+ config.notification = noticeSettings
135
+ }
136
+ } else if (task.executor.kind == 'connector') {
137
+ const { kind: _kind, outputMode: _outputMode, ...settings } = task.executor
138
+ config = settings
139
+ } else if (task.executor.kind == 'llm')
140
+ config = {
141
+ ...llmConfigurationView(task),
142
+ inputs: fieldsView(
143
+ [...task.inputs, ...(node.kind == 'task' ? (node.additionalInputs ?? []) : [])].filter(
144
+ (port) => !('handle' in port) || !llmReservedInputs.includes(port.handle),
145
+ ),
146
+ ),
147
+ }
148
+ else if (task.executor.kind == 'workflow') config = { flowId: task.executor.flowId }
149
+ else if (task.executor.kind == 'http-request') {
150
+ const { kind: _kind, ...settings } = task.executor
151
+ config = settings
152
+ } else if (task.executor.kind == 'openapi') {
153
+ config = { ...openApiConfigView(task.executor) }
154
+ }
155
+ } else if (node.kind == 'task' && 'moduleId' in node.task) {
156
+ config = {
157
+ inputs: fieldsView([...node.task.inputs, ...(node.additionalInputs ?? [])]),
158
+ outputs: fieldsView(node.task.outputs),
159
+ ...(node.task.capabilities == null ? {} : { capabilities: outwardCapabilities(node.task.capabilities) }),
160
+ }
161
+ texts.code = content.modules[node.task.moduleId]?.source ?? ''
162
+ } else {
163
+ switch (node.kind) {
164
+ case 'outputs':
165
+ config = { outputDefs: fieldsView(node.outputDefs) }
166
+ break
167
+ case 'condition':
168
+ config = { ...conditionConfigurationView(node, (value) => outwardSource(content, value) as AuthoringConditionSource) }
169
+ break
170
+ case 'value':
171
+ config = {
172
+ values: Object.fromEntries(node.values.filter((port) => Object.hasOwn(port, 'value')).map((port) => [port.handle, port.value])),
173
+ outputs: fieldsView(node.values.map(({ value: _value, ...port }) => port)),
174
+ }
175
+ break
176
+ case 'wait':
177
+ case 'approval':
178
+ config = { inputs: fieldsView(node.inputDefinitions), ...(node.kind == 'wait' && node.autoContinue != null ? { autoContinue: node.autoContinue } : {}) }
179
+ texts.prompt = node.prompt
180
+ break
181
+ case 'webhook-response':
182
+ case 'manual':
183
+ config = {}
184
+ break
185
+ case 'start':
186
+ case 'webhook':
187
+ case 'cron':
188
+ case 'error':
189
+ case 'poll':
190
+ case 'integration':
191
+ config = triggerConfiguration(node)
192
+ break
193
+ }
194
+ }
195
+ const edges = authoringEdges(content.document.graph.edges)
196
+ return {
197
+ ref,
198
+ type,
199
+ name: node.name ?? ref,
200
+ readOnly: false,
201
+ ...(node.description == null ? {} : { description: node.description }),
202
+ ...(node.icon == null ? {} : { icon: node.icon }),
203
+ ...('timeoutMs' in node && node.timeoutMs != null ? { timeoutMs: node.timeoutMs } : {}),
204
+ ...('maxExecutions' in node && node.maxExecutions != null ? { maxExecutions: node.maxExecutions } : {}),
205
+ ...(node.onError == null ? {} : { onError: node.onError }),
206
+ ...('retry' in node && node.retry != null ? { retry: node.retry } : {}),
207
+ config: json(config),
208
+ texts: Object.fromEntries(Object.entries(texts).map(([field, text]) => [field, { lines: text.split('\n').length, characters: text.length }])),
209
+ inputs: outwardSource(content, inputMappings(node)),
210
+ inputPorts: fieldsView(Object.entries(inputDefinitions(node)).map(([handle, port]) => Object.assign({}, port, { handle }))),
211
+ ...(Object.keys(nodeSystemOutputPorts(node)).length == 0
212
+ ? {}
213
+ : { systemOutputs: fieldsView(Object.entries(nodeSystemOutputPorts(node)).map(([handle, port]) => Object.assign({ handle }, port))) }),
214
+ outputPorts: fieldsView(Object.entries(nodeOutputPorts(node)).map(([handle, port]) => Object.assign({}, port, { handle }))),
215
+ ...(task != null && 'executor' in task && task.executor.kind == 'openapi' ? { authenticationChoices: openApiAuthenticationChoices(task.executor) } : {}),
216
+ incoming: edges.filter((e) => e.target == ref),
217
+ outgoing: edges.filter((e) => e.source == ref),
218
+ }
219
+ }
220
+ function textAt(content: RevisionContent, node: GraphNode, field: 'code' | 'prompt'): string {
221
+ if (field == 'code' && node.kind == 'task' && 'moduleId' in node.task)
222
+ return content.modules[node.task.moduleId]?.source ?? fail('text.missing', 'Node source is missing.')
223
+ const task = taskAt(node)
224
+ if (field == 'prompt' && task != null && 'executor' in task && task.executor.kind == 'agent') return task.executor.prompt
225
+ if (field == 'prompt' && (node.kind == 'wait' || node.kind == 'approval')) return node.prompt
226
+ return fail('text.unsupported', `This node has no ${field} field.`)
227
+ }
228
+ export function readAuthoring(content: RevisionContent, query: AuthoringRead = {}) {
229
+ const q = authoringReadSchema.parse(query)
230
+ if (q.text != null) {
231
+ const node = nodeAt(content, q.text.node),
232
+ source = textAt(content, node, q.text.field),
233
+ lines = source.split('\n')
234
+ const selected = lines.slice(q.text.start - 1, q.text.start - 1 + q.text.lines)
235
+ // Bound by characters as well as lines; never present a partial line as complete.
236
+ let count = 0,
237
+ characters = 0
238
+ for (const line of selected) {
239
+ if (characters + line.length + 1 > 24000) break
240
+ characters += line.length + 1
241
+ count++
242
+ }
243
+ if (count == 0 && selected.length > 0)
244
+ fail('text.line-too-large', 'Selected line exceeds 24000 characters; use search to locate a smaller edit.', { node: q.text.node, field: q.text.field })
245
+ const end = q.text.start - 1 + count
246
+ return {
247
+ text: {
248
+ ...q.text,
249
+ content: selected.slice(0, count).join('\n'),
250
+ totalLines: lines.length,
251
+ ...(end < lines.length ? { nextStart: end + 1 } : {}),
252
+ truncated: end < lines.length,
253
+ },
254
+ }
255
+ }
256
+ if (q.nodes != null) return { nodes: q.nodes.map((ref) => authoringNode(content, ref)) }
257
+ return {
258
+ nodes: Object.keys(content.document.graph.nodes).map((ref) => {
259
+ const node = authoringNode(content, ref)
260
+ return {
261
+ ref,
262
+ type: node.type,
263
+ name: node.name,
264
+ readOnly: node.readOnly,
265
+ inputHandles: Object.keys(node.inputPorts),
266
+ outputHandles: Object.keys(node.outputPorts),
267
+ inputs: compactValue(node.inputs),
268
+ texts: node.texts,
269
+ }
270
+ }),
271
+ edges: authoringEdges(content.document.graph.edges),
272
+ }
273
+ }
274
+ function compactValue(value: unknown, depth = 0): unknown {
275
+ if (depth >= 4 && value != null && typeof value == 'object') return { truncated: true }
276
+ if (typeof value == 'string') return value.length > 160 ? { preview: value.slice(0, 160), truncated: true } : value
277
+ if (Array.isArray(value))
278
+ return value.length > 8
279
+ ? { preview: value.slice(0, 8).map((v) => compactValue(v, depth + 1)), truncated: true }
280
+ : value.map((v) => compactValue(v, depth + 1))
281
+ if (value != null && typeof value == 'object')
282
+ return Object.fromEntries([
283
+ ...Object.entries(value)
284
+ .slice(0, 8)
285
+ .map(([key, v]) => [key, compactValue(v, depth + 1)]),
286
+ ...(Object.keys(value).length > 8 ? [['truncated', true]] : []),
287
+ ])
288
+ return value
289
+ }
290
+ export function searchAuthoring(content: RevisionContent, query: AuthoringSearch) {
291
+ const q = authoringSearchSchema.parse(query),
292
+ needle = q.query.toLowerCase()
293
+ const matches: { node?: string; field: string; line?: number; excerpt: string }[] = []
294
+ for (const [ref, node] of Object.entries(content.document.graph.nodes)) {
295
+ const view = authoringNode(content, ref)
296
+ if (q.type != null && q.type != view.type) continue
297
+ const fields = {
298
+ name: view.name,
299
+ description: view.description ?? '',
300
+ onError: view.onError ?? '',
301
+ retry: JSON.stringify(view.retry ?? {}),
302
+ inputs: JSON.stringify(view.inputs),
303
+ config: JSON.stringify(view.config),
304
+ ...Object.fromEntries(Object.keys(view.texts).map((field) => [field, textAt(content, node, field as 'code' | 'prompt')])),
305
+ }
306
+ for (const [field, value] of Object.entries(fields)) {
307
+ for (const [index, line] of value.split('\n').entries()) {
308
+ const at = line.toLowerCase().indexOf(needle)
309
+ if (at >= 0) matches.push({ node: ref, field, line: index + 1, excerpt: line.slice(Math.max(0, at - 100), at + Math.max(needle.length, 100)) })
310
+ }
311
+ }
312
+ }
313
+ const end = q.offset + q.limit
314
+ return { matches: matches.slice(q.offset, end), total: matches.length, ...(end < matches.length ? { nextOffset: end } : {}) }
315
+ }
316
+
317
+ export interface AuthoringHost {
318
+ workflow?(flowId: string): Promise<WorkflowContract | WorkflowCandidate>
319
+ id(label: string): string
320
+ action(id: string): Promise<ConnectorAction>
321
+ trigger(key: string): TriggerKeySnapshot
322
+ openapi(url: string): Promise<JsonValue>
323
+ }
324
+ function merge(base: unknown, patch: unknown): unknown {
325
+ if (patch == null || typeof patch != 'object' || Array.isArray(patch)) return patch
326
+ const result = { ...record(base) }
327
+ for (const [key, value] of Object.entries(patch)) {
328
+ if (['__proto__', 'constructor', 'prototype'].includes(key)) fail('edit.invalid-field', `Invalid field ${key}.`)
329
+ Object.defineProperty(result, key, { value: merge(result[key], value), enumerable: true, writable: true, configurable: true })
330
+ }
331
+ return result
332
+ }
333
+ function clearPath(value: Record<string, unknown>, path: readonly string[]) {
334
+ let current = value
335
+ for (const part of path.slice(0, -1)) {
336
+ if (!Object.hasOwn(current, part) || Array.isArray(current[part]) || current[part] == null || typeof current[part] != 'object')
337
+ fail('edit.invalid-field', `Cannot clear ${path.join('.')}.`)
338
+ current = record(current[part])
339
+ }
340
+ const last = path.at(-1)!
341
+ if (!Object.hasOwn(current, last)) fail('edit.invalid-field', `Cannot clear absent field ${path.join('.')}.`)
342
+ delete current[last]
343
+ }
344
+ export async function compileAuthoring(base: RevisionContent, request: AuthoringRequest, host: AuthoringHost) {
345
+ const parsed = authoringRequestSchema.parse(request)
346
+ const upgraded = upgradeRevisionContent(base)
347
+ let content = upgraded,
348
+ index = 0,
349
+ sequence = 0
350
+ const operations: ChangeOperation[] = [],
351
+ aliases: Record<string, string> = {}
352
+ const touchedModules = new Set<string>()
353
+ const explicitMessages = new Set<string>()
354
+ function fresh(label: string) {
355
+ return host.id(`${index}:${label}:${sequence++}`)
356
+ }
357
+ function apply(changes: readonly ChangeOperation[]) {
358
+ const checked = decodeChangeOperations(changes)
359
+ content = applyFlowChanges(content, checked)
360
+ operations.push(...checked)
361
+ }
362
+ function resolve(ref: string) {
363
+ return ref.startsWith('$')
364
+ ? ((Object.hasOwn(aliases, ref.slice(1)) ? aliases[ref.slice(1)] : undefined) ?? fail('node.alias-missing', `Unknown alias ${ref}.`))
365
+ : ref
366
+ }
367
+ function source(
368
+ value: Exclude<AuthoringSource, { kind: 'value' | 'default' | 'unset' | 'sources' }>,
369
+ ): Extract<InputMapping, { kind: 'sources' }>['sources'][number] {
370
+ if (value.kind == 'output' || value.kind == 'system') {
371
+ const ref = resolve(value.node)
372
+ const choices = Object.keys(nodeOutputPorts(nodeAt(content, ref)))
373
+ const port = value.port ?? (choices.length == 1 ? choices[0] : undefined)
374
+ if (port == null) fail('source.port-required', 'Choose an output name; this node does not have exactly one output.', { node: ref, choices })
375
+ return {
376
+ kind: 'node',
377
+ ...(value.kind == 'system' ? { system: true as const } : {}),
378
+ nodeId: ref,
379
+ output: port,
380
+ ...(value.field == null ? {} : { field: value.field }),
381
+ }
382
+ }
383
+ const existing = Object.entries(content.document.bindings).find(([, binding]) => binding.target == value.name)
384
+ const bindingId = existing?.[0] ?? fresh('binding')
385
+ if (existing == null) apply([{ kind: 'binding.create', bindingId, binding: { kind: 'variable', target: value.name } }])
386
+ return { kind: 'binding', bindingId }
387
+ }
388
+ function replace(ref: string, node: GraphNode) {
389
+ const before = nodeAt(content, ref)
390
+ if (!dequal(before, node)) {
391
+ const previous = before.kind == 'task' ? before.task.outputs : before.kind == 'value' ? before.values : []
392
+ const next = node.kind == 'task' ? node.task.outputs : node.kind == 'value' ? node.values : []
393
+ apply([
394
+ { kind: 'graph.node.replace', nodeId: ref, before, node },
395
+ ...(node.kind == 'task' && 'executor' in node.task && node.task.executor.kind == 'workflow'
396
+ ? []
397
+ : renameFlowOutputSources(content, new Set([ref]), previous, next)),
398
+ ])
399
+ }
400
+ }
401
+ function taskNode(ref: string, task: ManagedTaskDefinition) {
402
+ const node = nodeAt(content, ref)
403
+ if (node.kind != 'task' || !('executor' in node.task)) fail('node.invalid-type', 'Expected a managed node.')
404
+ replace(ref, { ...node, task })
405
+ }
406
+
407
+ async function connectorTask(config: Record<string, unknown>, name: string): Promise<ManagedTaskDefinition> {
408
+ const action = await host.action(String(config.action))
409
+ return {
410
+ name,
411
+ executor: {
412
+ kind: 'connector',
413
+ action: action.actionId,
414
+ ...(config.connectionId == null ? {} : { connectionId: String(config.connectionId) }),
415
+ ...(action.outputSchema !== undefined && connectorOutputMode(action.outputSchema) == 'value' ? { outputMode: 'value' as const } : {}),
416
+ },
417
+ inputs: Object.entries(action.inputs).map(([handle, port]) => Object.assign({ handle }, port)),
418
+ outputs: Object.entries(action.outputs).map(([handle, port]) => Object.assign({ handle }, port)),
419
+ }
420
+ }
421
+ async function workflowTarget(flowId: string) {
422
+ if (host.workflow == null) fail('workflow.unavailable', 'Workflow calls are unavailable.')
423
+ return host.workflow(flowId)
424
+ }
425
+ async function resolveWorkflow(flowId: string, name: string, previous?: ManagedTaskDefinition) {
426
+ const target = await workflowTarget(flowId)
427
+ return 'inputs' in target ? workflowTask(target, name) : workflowSelectionTask({ ...target, name }, undefined, previous)
428
+ }
429
+ async function configure(ref: string, patch: Record<string, unknown> = {}, clear: readonly (readonly string[])[] = []) {
430
+ const node = nodeAt(content, ref),
431
+ view = authoringNode(content, ref),
432
+ type = view.type as AuthoringType
433
+ const original = {
434
+ name: view.name,
435
+ ...(view.description == null ? {} : { description: view.description }),
436
+ ...(view.icon == null ? {} : { icon: view.icon }),
437
+ ...(view.timeoutMs == null ? {} : { timeoutMs: view.timeoutMs }),
438
+ ...(view.maxExecutions == null ? {} : { maxExecutions: view.maxExecutions }),
439
+ ...(view.onError == null ? {} : { onError: view.onError }),
440
+ ...(view.retry == null ? {} : { retry: view.retry }),
441
+ config: view.config,
442
+ }
443
+ const update = record(merge(original, patch))
444
+ const explicit = (key: string) => Object.hasOwn(record(patch.config), key) || clear.some((path) => path[0] == 'config' && path[1] == key)
445
+ if (type == 'wait') {
446
+ const timing = record(record(patch.config).autoContinue)
447
+ if (Object.hasOwn(timing, 'kind') && timing.kind != record(record(view.config).autoContinue).kind) record(update.config).autoContinue = timing
448
+ }
449
+ if ((type == 'openapi' || type == 'http-request') && Object.hasOwn(record(patch.config), 'authentication'))
450
+ record(update.config).authentication = record(patch.config).authentication
451
+ for (const path of clear) clearPath(update, path)
452
+ if (typeof update.name != 'string') fail('node.name-required', 'Node name cannot be cleared.')
453
+ const config = authoringConfigurations[type].parse(update.config) as Record<string, unknown>
454
+ if (Object.hasOwn(update, 'retry')) update.retry = retryConfigurationSchema.parse(update.retry)
455
+ const { config: _, ...metadata } = update
456
+ const { name: _name, description: _description, icon: _icon, ...rest } = node
457
+ let next = { ...rest, ...metadata } as GraphNode
458
+ if (!isTriggerNode(node)) {
459
+ const clean = { ...next } as Record<string, unknown>
460
+ if (!Object.hasOwn(update, 'onError')) delete clean.onError
461
+ if (!Object.hasOwn(update, 'retry')) delete clean.retry
462
+ if (!Object.hasOwn(update, 'timeoutMs')) delete clean.timeoutMs
463
+ if (!Object.hasOwn(update, 'maxExecutions')) delete clean.maxExecutions
464
+ next = clean as unknown as GraphNode
465
+ } else if (update.timeoutMs != null || update.maxExecutions != null) fail('node.invalid-field', 'Trigger nodes do not have execution limits.')
466
+ if (update.onError != null && (!supportsNodeErrorHandling(next) || !['stop', 'error'].includes(String(update.onError))))
467
+ fail('node.invalid-field', 'This node does not support this error handling setting.')
468
+ if (update.retry != null && !supportsNodeRetry(next)) fail('node.invalid-field', 'This node does not support retries.')
469
+ if (node.kind == 'start' || node.kind == 'outputs') {
470
+ replace(ref, next)
471
+ const fields = node.kind == 'start' ? config.inputDefs : config.outputDefs
472
+ const ports = fieldsToPorts(fields, node.kind == 'start' ? node.inputDefs : node.outputDefs).filter((port): port is InputPort => 'handle' in port)
473
+ const changes = node.kind == 'start' ? updateStartInputDefs(content, ref, ports) : updateOutputsDefs(content, ref, ports)
474
+ if (changes.length > 0) apply(changes)
475
+ return
476
+ }
477
+ function taskInputs(task: ManagedTaskDefinition | NonNullable<Extract<GraphNode, { kind: 'task' }>['task']>, reserved: readonly string[] = []) {
478
+ if (node.kind != 'task') throw new Error('Expected a task.')
479
+ const fields = record(config.inputs)
480
+ for (const key of reserved)
481
+ if (Object.hasOwn(fields, key)) fail('input.reserved', `Configure ${key} through config.${key} or input.set.`, { node: ref, input: key })
482
+ const extraNames = new Set(node.additionalInputs?.map((port) => port.handle) ?? [])
483
+ const main = Object.fromEntries(Object.entries(fields).filter(([key]) => !extraNames.has(key)))
484
+ for (const port of task.inputs)
485
+ if ('handle' in port && (reserved.includes(port.handle) || (extraNames.has(port.handle) && Object.hasOwn(fields, port.handle))))
486
+ main[port.handle] = fieldsView([port])[port.handle]
487
+ if (node.additionalInputs != null) {
488
+ const extra = Object.fromEntries(Object.entries(fields).filter(([key]) => extraNames.has(key)))
489
+ for (const port of node.additionalInputs) if (reserved.includes(port.handle)) extra[port.handle] = fieldsView([port])[port.handle]
490
+ next = { ...next, additionalInputs: fieldsToPorts(extra, node.additionalInputs) as readonly InputPort[] } as GraphNode
491
+ }
492
+ return fieldsToPorts(main, task.inputs)
493
+ }
494
+ if (node.kind == 'task' && 'moduleId' in node.task) {
495
+ const inputs = taskInputs(node.task)
496
+ next = {
497
+ ...next,
498
+ task: {
499
+ moduleId: node.task.moduleId,
500
+ name: String(update.name),
501
+ ...config,
502
+ ...(config.capabilities == null
503
+ ? {}
504
+ : { capabilities: await prepareCapabilities(config.capabilities as readonly AuthoringCapability[], node.task.capabilities ?? [], workflowTarget) }),
505
+ inputs,
506
+ outputs: fieldsToPorts(config.outputs, node.task.outputs),
507
+ },
508
+ } as GraphNode
509
+ } else if (node.kind == 'task' && 'executor' in node.task) {
510
+ const before = node.task
511
+ let task: ManagedTaskDefinition
512
+ if (type == 'workflow') {
513
+ task =
514
+ before.executor.kind == 'workflow' && before.executor.flowId == config.flowId
515
+ ? { ...before, name: String(update.name) }
516
+ : await resolveWorkflow(String(config.flowId), String(update.name))
517
+ next = withWorkflowTask({ ...node, ...metadata } as typeof node, task)
518
+ } else if (type == 'connector') {
519
+ if (before.executor.kind == 'connector' && config.action == before.executor.action) {
520
+ const { connectionId: _connectionId, ...executor } = before.executor
521
+ task = { ...before, name: String(update.name), executor: { ...executor, ...config } } as ManagedTaskDefinition
522
+ } else task = await connectorTask(config, String(update.name))
523
+ } else if (type == 'decision') task = decisionTask(config.questions as Parameters<typeof decisionTask>[0], String(update.name))
524
+ else if (type == 'http-request') {
525
+ task = httpRequestTask(httpRequestConfigSchema.parse(config), String(update.name), before)
526
+ next = { ...next, inputs: httpRequestInputs(node.inputs, before, task) } as GraphNode
527
+ } else if (type == 'openapi') {
528
+ task = await prepareOpenApiTask(config as AuthoringOpenApiConfig, before, (url) => host.openapi(url), String(update.name), {
529
+ authentication: explicit('authentication'),
530
+ serverUrl: explicit('serverUrl'),
531
+ })
532
+ } else if (type == 'llm') {
533
+ task = prepareLlmConfiguration(config, before, String(update.name))
534
+ task = { ...task, inputs: taskInputs(task, llmReservedInputs) }
535
+ } else {
536
+ const { inputs: _inputs, resultSchema, notification, tools, ...executor } = config
537
+ const prepared = { ...executor, kind: type, ...(before.executor.kind == 'agent' ? { prompt: before.executor.prompt } : {}) }
538
+ if (type == 'agent') {
539
+ const previous = before.executor.kind == 'agent' ? before.executor : undefined
540
+ Object.assign(prepared, {
541
+ tools: await prepareAgentTools(
542
+ tools as AuthoringAgentTool[],
543
+ previous?.tools ?? [],
544
+ (id) => host.action(id),
545
+ () => fresh('tool'),
546
+ workflowTarget,
547
+ ),
548
+ })
549
+ Reflect.deleteProperty(prepared, 'notification')
550
+ if (notification != null) {
551
+ if (dequal(record(view.config).notification, notification) && previous?.notification != null)
552
+ Object.assign(prepared, { notification: previous.notification })
553
+ else {
554
+ const settings = record(notification)
555
+ const definition = await connectorTask(settings, `${update.name} notification`)
556
+ Object.assign(prepared, {
557
+ notification: {
558
+ action: settings.action,
559
+ ...(settings.connectionId == null ? {} : { connectionId: settings.connectionId }),
560
+ inputDefinitions: definition.inputs,
561
+ messageHandle: settings.messageHandle,
562
+ inputs: settings.inputs,
563
+ },
564
+ })
565
+ }
566
+ }
567
+ }
568
+ const outputs = dequal(record(view.config).resultSchema, resultSchema)
569
+ ? before.outputs
570
+ : before.outputs.map((port) => ('handle' in port ? { ...port, jsonSchema: resultSchema as JsonValue } : port))
571
+ task = {
572
+ name: String(update.name),
573
+ inputs: type == 'agent' ? taskInputs(before) : before.inputs,
574
+ outputs,
575
+ executor: prepared,
576
+ } as ManagedTaskDefinition
577
+ }
578
+ next = { ...next, task } as GraphNode
579
+ } else
580
+ switch (node.kind) {
581
+ case 'condition':
582
+ next = { ...next, ...prepareConditionConfiguration(config as unknown as AuthoringConditionConfiguration, source) } as GraphNode
583
+ break
584
+ case 'value': {
585
+ const fields = { ...record(config.outputs) }
586
+ for (const key of Object.keys(record(config.values))) if (!Object.hasOwn(fields, key)) fields[key] = {}
587
+ const values = fieldsToPorts(
588
+ fields,
589
+ node.values.map(({ value: _value, ...port }) => port),
590
+ )
591
+ .filter((port): port is InputPort => 'handle' in port)
592
+ .map((port) => Object.assign({}, port, Object.hasOwn(record(config.values), port.handle) ? { value: record(config.values)[port.handle] } : {}))
593
+ next = { ...next, values } as GraphNode
594
+ break
595
+ }
596
+ case 'wait':
597
+ case 'approval':
598
+ next = { ...next, inputDefinitions: fieldsToPorts(config.inputs, node.inputDefinitions) } as GraphNode
599
+ if (node.kind == 'wait') {
600
+ Reflect.deleteProperty(next, 'autoContinue')
601
+ if (config.autoContinue != null) Object.assign(next, { autoContinue: config.autoContinue })
602
+ }
603
+ break
604
+ case 'webhook': {
605
+ const { body, ...settings } = config
606
+ const responseOptions = record(settings.options)
607
+ const responsePolicy = record(responseOptions.responseBody)
608
+ if (responsePolicy.kind === 'webhook-response' && typeof responsePolicy.nodeId === 'string')
609
+ settings.options = { ...responseOptions, responseBody: { ...responsePolicy, nodeId: resolve(responsePolicy.nodeId) } }
610
+ const { options: _options, ...withoutOptions } = next as Extract<GraphNode, { kind: 'webhook' }>
611
+ next = { ...withoutOptions, ...settings, bodyFields: fieldsToPorts(body, node.bodyFields) } as GraphNode
612
+ break
613
+ }
614
+ case 'cron':
615
+ next = { ...next, cronTimes: config.schedule } as GraphNode
616
+ break
617
+ case 'poll':
618
+ case 'integration': {
619
+ if (config.key != node.definition.key) fail('trigger.key-immutable', 'Create a new node to change its Trigger type.')
620
+ const { connectionId: _connection, ...withoutConnection } = next as Extract<GraphNode, { kind: 'poll' | 'integration' }>
621
+ next = {
622
+ ...withoutConnection,
623
+ ...(config.connectionId == null ? {} : { connectionId: config.connectionId }),
624
+ ...(node.kind == 'poll' ? { pollTimes: config.schedule } : {}),
625
+ } as GraphNode
626
+ break
627
+ }
628
+ default: {
629
+ const fields = { ...next } as Record<string, unknown>
630
+ for (const key of Object.keys(record(view.config))) delete fields[key]
631
+ next = { ...fields, ...config } as unknown as GraphNode
632
+ }
633
+ }
634
+ replace(ref, next)
635
+ }
636
+ async function setText(ref: string, field: 'code' | 'prompt', text: string) {
637
+ const node = nodeAt(content, ref),
638
+ before = textAt(content, node, field)
639
+ if (before == text) return
640
+ if (field == 'code' && node.kind == 'task' && 'moduleId' in node.task) {
641
+ const moduleId = node.task.moduleId,
642
+ module = content.modules[moduleId]!
643
+ const shared =
644
+ Object.values(content.document.graph.nodes).some((n) => n.kind == 'task' && 'moduleId' in n.task && n.task.moduleId == moduleId && n !== node) ||
645
+ Object.values(content.modules).some((m) => m.imports.includes(moduleId))
646
+ const imports = await moduleImports(text)
647
+ if (shared) {
648
+ const id = fresh('module')
649
+ apply([{ kind: 'module.create', moduleId: id, module: { ...module, source: text, imports } }])
650
+ replace(ref, { ...node, task: { ...node.task, moduleId: id } })
651
+ } else apply([{ kind: 'module.source.replace', moduleId, beforeSource: before, beforeImports: module.imports, source: text, imports }])
652
+ } else if (node.kind == 'task' && 'executor' in node.task) {
653
+ const task = node.task
654
+ taskNode(ref, { ...task, executor: { ...task.executor, prompt: text } } as ManagedTaskDefinition)
655
+ } else if (node.kind == 'wait' || node.kind == 'approval') replace(ref, { ...node, prompt: text })
656
+ }
657
+ async function setInput(ref: string, input: string, value: AuthoringSource) {
658
+ if (input == 'message') explicitMessages.add(ref)
659
+ const node = nodeAt(content, ref)
660
+ if (node.kind == 'condition')
661
+ fail('input.unsupported', 'Edit the branch condition operands through config.branches.', { node: ref, field: 'config.branches' })
662
+ if (node.kind == 'poll' || node.kind == 'integration') {
663
+ if (!Object.hasOwn(inputDefinitions(node), input))
664
+ fail('input.not-found', `Unknown input ${input}.`, { node: ref, input, choices: Object.keys(inputDefinitions(node)) })
665
+ if (value.kind != 'value' && value.kind != 'unset' && value.kind != 'default')
666
+ fail('input.source-unsupported', 'Trigger parameters accept only value, unset or default; they cannot depend on execution outputs or variables.', {
667
+ node: ref,
668
+ input,
669
+ allowed: ['value', 'unset', 'default'],
670
+ })
671
+ const config = { ...node.config }
672
+ if (value.kind == 'default') delete config[input]
673
+ else config[input] = value
674
+ replace(ref, { ...node, config })
675
+ return
676
+ }
677
+ if (!Object.hasOwn(inputDefinitions(node), input)) {
678
+ const type = typeOf(node)
679
+ if (!['agent', 'code', 'llm', 'wait', 'approval'].includes(type))
680
+ fail('input.not-found', `Unknown input ${input}.`, { node: ref, input, choices: Object.keys(inputDefinitions(node)) })
681
+ if (type == 'llm' && llmReservedInputs.includes(input))
682
+ fail('input.not-found', `Missing built-in LLM input ${input}.`, { node: ref, input, choices: Object.keys(inputDefinitions(node)) })
683
+ await configure(ref, { config: { inputs: { [input]: {} } } })
684
+ }
685
+ const mapping: InputMapping | undefined =
686
+ value.kind == 'default'
687
+ ? undefined
688
+ : value.kind == 'value' || value.kind == 'unset'
689
+ ? value
690
+ : { kind: 'sources', sources: (value.kind == 'sources' ? value.sources : [value]).map(source) }
691
+ const before = nodeInputMappings(nodeAt(content, ref))[input]
692
+ if (!dequal(before, mapping)) apply([{ kind: 'graph.node.input.set', nodeId: ref, handle: input, before, ...(mapping == null ? {} : { value: mapping }) }])
693
+ }
694
+ for (const edit of parsed.edits) {
695
+ try {
696
+ switch (edit.op) {
697
+ case 'node.refresh': {
698
+ const ref = resolve(edit.node)
699
+ const node = nodeAt(content, ref)
700
+ if (node.kind != 'task') fail('node.invalid-type', 'Only workflow nodes and tools support refresh.')
701
+ if (edit.tool != null) {
702
+ if ('moduleId' in node.task) {
703
+ const selected = node.task.capabilities?.find((item) => item.kind == 'workflow' && item.name == edit.tool)
704
+ if (selected?.kind != 'workflow') fail('tool.not-found', 'Workflow tool was not found.')
705
+ const target = await workflowTarget(selected.flowId)
706
+ if (!('inputs' in target)) fail('workflow.unavailable', 'The workflow interface is unavailable.')
707
+ replace(ref, {
708
+ ...node,
709
+ task: {
710
+ ...node.task,
711
+ capabilities: node.task.capabilities!.map((item) => (item === selected ? workflowCapability(selected.name, target) : item)),
712
+ },
713
+ })
714
+ } else if (node.task.executor.kind == 'agent') {
715
+ const selected = node.task.executor.tools.find((item) => item.kind == 'workflow' && item.name == edit.tool)
716
+ if (selected?.kind != 'workflow') fail('tool.not-found', 'Workflow tool was not found.')
717
+ const target = await workflowTarget(selected.flowId)
718
+ if (!('inputs' in target)) fail('workflow.unavailable', 'The workflow interface is unavailable.')
719
+ replace(ref, {
720
+ ...node,
721
+ task: {
722
+ ...node.task,
723
+ executor: {
724
+ ...node.task.executor,
725
+ tools: node.task.executor.tools.map((item) => (item === selected ? workflowAgentTool(selected.id, selected.name, target, selected) : item)),
726
+ },
727
+ },
728
+ })
729
+ } else fail('node.invalid-type', 'Only Agent and Code nodes have workflow tools.')
730
+ } else {
731
+ if (!('executor' in node.task) || node.task.executor.kind != 'workflow') fail('node.invalid-type', 'Specify a workflow tool name.')
732
+ const task = await resolveWorkflow(node.task.executor.flowId, node.name ?? node.task.name, node.task)
733
+ apply([{ kind: 'graph.node.replace', nodeId: ref, before: node, node: withWorkflowTask(node, task) }])
734
+ }
735
+ break
736
+ }
737
+ case 'node.add': {
738
+ if (Object.hasOwn(aliases, edit.as)) fail('node.alias-duplicate', `Duplicate alias ${edit.as}.`)
739
+ const nodeId = host.id(`node:${edit.as}`),
740
+ identity = { nodeId, moduleId: fresh('module') },
741
+ name = edit.name
742
+ aliases[edit.as] = nodeId
743
+ const config = edit.config ?? {}
744
+ let changes: readonly ChangeOperation[]
745
+ switch (edit.type) {
746
+ case 'workflow':
747
+ changes = createManagedTask(identity, await resolveWorkflow(String(authoringConfigurations.workflow.parse(config).flowId), name))
748
+ break
749
+ case 'webhook-response':
750
+ changes = [{ kind: 'graph.node.create', nodeId, node: { kind: 'webhook-response', name, inputs: {} } }]
751
+ break
752
+ case 'outputs':
753
+ changes = [{ kind: 'graph.node.create', nodeId, node: { outputDefs: [], outputMappings: {}, kind: 'outputs', name } }]
754
+ break
755
+ case 'code':
756
+ changes = createCodeTask(identity, name, undefined, {
757
+ inputs: [],
758
+ outputs: config.outputs == null ? defaultCodeTaskPorts.outputs : [],
759
+ })
760
+ break
761
+ case 'agent':
762
+ changes = createAgentTask(identity, name, { inputs: [], prompt: edit.prompt ?? '' })
763
+ break
764
+ case 'llm':
765
+ changes = createLlmTask(identity, name, 'chat', 'Generated response.').map((change) =>
766
+ change.kind == 'graph.node.create' && change.node.kind == 'task' && 'executor' in change.node.task
767
+ ? Object.assign({}, change, { node: Object.assign({}, change.node, { task: initialAuthoringLlmTask(change.node.task) }) })
768
+ : change,
769
+ )
770
+ break
771
+ case 'decision':
772
+ changes = createDecisionTask(identity, name)
773
+ break
774
+ case 'connector':
775
+ changes = createManagedTask(identity, await connectorTask(config, name))
776
+ break
777
+ case 'http-request':
778
+ changes = createManagedTask(identity, httpRequestTask(httpRequestConfigSchema.parse(config), name))
779
+ break
780
+ case 'openapi':
781
+ changes = createManagedTask(
782
+ identity,
783
+ await prepareOpenApiTask(authoringConfigurations.openapi.parse(config), undefined, (url) => host.openapi(url), name),
784
+ )
785
+ break
786
+ case 'condition':
787
+ changes = createCondition(nodeId, name)
788
+ break
789
+ case 'value':
790
+ changes = createValue(nodeId, name, [])
791
+ break
792
+ case 'wait':
793
+ changes = createWait(nodeId, name)
794
+ break
795
+ case 'approval':
796
+ changes = createApproval(nodeId, name)
797
+ break
798
+ case 'poll':
799
+ case 'integration': {
800
+ const definition = host.trigger(String(config.key))
801
+ if (definition.type != edit.type) fail('trigger.type-mismatch', 'Trigger key has a different type.')
802
+ changes = createProviderTrigger(nodeId, definition, { name, config: {} })
803
+ break
804
+ }
805
+ default:
806
+ changes = [
807
+ {
808
+ kind: 'graph.node.create',
809
+ nodeId,
810
+ node: {
811
+ kind: edit.type,
812
+ name,
813
+ ...(edit.type == 'start' ? { inputDefs: [] } : {}),
814
+ ...(edit.type == 'cron' ? { cronTimes: [{ type: 'every', unit: 'hour', value: 1 }] } : {}),
815
+ ...(edit.type == 'webhook' ? { method: 'POST', bodyFields: [] } : {}),
816
+ } as GraphNode,
817
+ },
818
+ ]
819
+ }
820
+ apply(changes)
821
+ await configure(nodeId, { config, ...(edit.onError == null ? {} : { onError: edit.onError }), ...(edit.retry == null ? {} : { retry: edit.retry }) })
822
+ const added = nodeAt(content, nodeId)
823
+ if (added.kind == 'task') replace(nodeId, { ...added, inputs: {} })
824
+ if (edit.code != null) await setText(nodeId, 'code', edit.code)
825
+ if (edit.prompt != null) await setText(nodeId, 'prompt', edit.prompt)
826
+ for (const [input, binding] of Object.entries(edit.inputs ?? {})) await setInput(nodeId, input, binding)
827
+ break
828
+ }
829
+ case 'node.update':
830
+ await configure(resolve(edit.node), edit.set, edit.clear)
831
+ break
832
+ case 'node.remove': {
833
+ const ref = resolve(edit.node),
834
+ node = nodeAt(content, ref)
835
+ if (node.kind == 'task' && 'moduleId' in node.task) touchedModules.add(node.task.moduleId)
836
+ apply([{ kind: 'graph.node.delete', nodeId: ref }])
837
+ break
838
+ }
839
+ case 'input.set': {
840
+ await setInput(resolve(edit.node), edit.input, edit.source)
841
+ break
842
+ }
843
+ case 'edge.connect':
844
+ case 'edge.disconnect': {
845
+ const from = resolve(edit.source),
846
+ to = resolve(edit.target)
847
+ nodeAt(content, from)
848
+ nodeAt(content, to)
849
+ const edge = { source: from, target: to, ...(edit.branch == null ? {} : { sourceHandle: edit.branch }) }
850
+ const changes = edit.op == 'edge.connect' ? connect(content, edge) : disconnect(content, edge)
851
+ apply(explicitMessages.has(to) ? changes.filter((change) => change.kind != 'graph.node.input.set') : changes)
852
+ break
853
+ }
854
+ case 'text.set':
855
+ await setText(resolve(edit.node), edit.field, edit.text)
856
+ break
857
+ case 'text.edit': {
858
+ const ref = resolve(edit.node),
859
+ value = textAt(content, nodeAt(content, ref), edit.field),
860
+ count = value.split(edit.oldText).length - 1
861
+ if (count != 1)
862
+ fail('text.match-count', `Expected one exact match, found ${count}. Read the field and include more context.`, {
863
+ node: ref,
864
+ field: edit.field,
865
+ matches: count,
866
+ })
867
+ await setText(
868
+ ref,
869
+ edit.field,
870
+ value.replace(edit.oldText, () => edit.newText),
871
+ )
872
+ break
873
+ }
874
+ }
875
+ } catch (error) {
876
+ if (error instanceof AuthoringError || error instanceof AuthoringToolInputError || error instanceof AuthoringOpenApiAuthenticationError)
877
+ throw new AuthoringError(error.code, error.message, { ...error.details, editIndex: index })
878
+ throw new AuthoringError('edit.invalid', error instanceof Error ? error.message : String(error), { editIndex: index })
879
+ }
880
+ index++
881
+ }
882
+ for (const moduleId of touchedModules)
883
+ if (
884
+ !Object.values(content.document.graph.nodes).some((n) => n.kind == 'task' && 'moduleId' in n.task && n.task.moduleId == moduleId) &&
885
+ !Object.values(content.modules).some((m) => m.imports.includes(moduleId))
886
+ )
887
+ apply([{ kind: 'module.delete', moduleId }])
888
+ // Shared binding lifecycle remains owned by the existing authoring implementation.
889
+ const cleaned = cleanVariableBindings(upgraded, operations)
890
+ return { operations: cleaned, content: cleaned.length == 0 ? upgraded : applyFlowChanges(upgraded, cleaned), nodes: aliases }
891
+ }
892
+
893
+ const escapePointer = (value: string) => value.replaceAll('~', '~0').replaceAll('/', '~1')
894
+ export function authoringDiagnostics(content: RevisionContent, diagnostics: readonly Diagnostic[]) {
895
+ const nodes = Object.entries(content.document.graph.nodes)
896
+ return diagnostics.map((diagnostic) => {
897
+ const affected = nodes.filter(([id, node]) => {
898
+ const roots = [`/document/graph/nodes/${escapePointer(id)}`]
899
+ if (node.kind == 'task' && 'moduleId' in node.task) roots.push(`/modules/${escapePointer(node.task.moduleId)}`)
900
+ return roots.some((root) => diagnostic.path == root || diagnostic.path.startsWith(`${root}/`))
901
+ })
902
+ // Diagnostic wording comes from the shared validators; storage identities are replaced at this boundary.
903
+ const message = diagnostic.message
904
+ .replace(/(CodeModule|Managed Task|Task|Binding) "([^"\n]+)"/g, (_, kind: string, id: string) => {
905
+ if (kind == 'Binding') return `Variable "${content.document.bindings[id]?.target ?? 'unresolved'}"`
906
+ const owners = nodes.filter(([, n]) => n.kind == 'task' && kind == 'CodeModule' && 'moduleId' in n.task && n.task.moduleId == id)
907
+ return owners.length
908
+ ? `${kind == 'CodeModule' ? 'Code for' : 'Node'} ${owners.map(([ref, n]) => JSON.stringify(n.name ?? ref)).join(', ')}`
909
+ : kind == 'CodeModule'
910
+ ? 'Referenced code'
911
+ : 'Node configuration'
912
+ })
913
+ .replaceAll('CodeModule', 'Code')
914
+ const parts = diagnostic.path.split('/').map((part) => part.replaceAll('~1', '/').replaceAll('~0', '~'))
915
+ return {
916
+ code: diagnostic.code,
917
+ message,
918
+ nodes: affected.map(([id]) => id),
919
+ field: authoringDiagnosticField(affected[0]?.[1], parts),
920
+ ...(diagnostic.line ? { line: diagnostic.line, column: diagnostic.column } : {}),
921
+ }
922
+ })
923
+ }