@ai-setting/roy-agent-core 1.3.10 → 1.3.14

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 (379) hide show
  1. package/dist/config/index.js +1647 -0
  2. package/dist/index.js +12579 -89691
  3. package/package.json +19 -56
  4. package/src/config/config-component.test.ts +0 -627
  5. package/src/config/config-component.ts +0 -906
  6. package/src/config/config-parser.test.ts +0 -319
  7. package/src/config/config-parser.ts +0 -203
  8. package/src/config/decentralized-config.test.ts +0 -740
  9. package/src/config/env-key.ts +0 -210
  10. package/src/config/env-source.test.ts +0 -252
  11. package/src/config/env-source.ts +0 -301
  12. package/src/config/file-source.test.ts +0 -357
  13. package/src/config/file-source.ts +0 -421
  14. package/src/config/index.ts +0 -24
  15. package/src/config/protocol-resolver.test.ts +0 -217
  16. package/src/config/protocol-resolver.ts +0 -228
  17. package/src/env/agent/agent-component.abort.test.ts +0 -511
  18. package/src/env/agent/agent-component.record-session.test.ts +0 -349
  19. package/src/env/agent/agent-component.test.ts +0 -1389
  20. package/src/env/agent/agent-component.tool-error.test.ts +0 -327
  21. package/src/env/agent/agent-component.ts +0 -1711
  22. package/src/env/agent/agent-config-registration.test.ts +0 -226
  23. package/src/env/agent/agent-config-registration.ts +0 -46
  24. package/src/env/agent/agent-reminder-plugin.integration.test.ts +0 -243
  25. package/src/env/agent/index.ts +0 -10
  26. package/src/env/agent/summary-agent.parse-hint.test.ts +0 -360
  27. package/src/env/agent/summary-agent.ts +0 -508
  28. package/src/env/agent/types.ts +0 -536
  29. package/src/env/commands/commands-component.test.ts +0 -364
  30. package/src/env/commands/commands-component.ts +0 -604
  31. package/src/env/commands/commands-config-registration.test.ts +0 -198
  32. package/src/env/commands/commands-config-registration.ts +0 -38
  33. package/src/env/commands/index.ts +0 -21
  34. package/src/env/commands/parser.test.ts +0 -203
  35. package/src/env/commands/parser.ts +0 -115
  36. package/src/env/commands/types.ts +0 -184
  37. package/src/env/commands-prompt-integration.test.ts +0 -243
  38. package/src/env/component-env.test.ts +0 -119
  39. package/src/env/component.ts +0 -335
  40. package/src/env/constants.test.ts +0 -72
  41. package/src/env/constants.ts +0 -123
  42. package/src/env/debug/debug-component.test.ts +0 -114
  43. package/src/env/debug/debug-component.ts +0 -547
  44. package/src/env/debug/formatters/index.ts +0 -9
  45. package/src/env/debug/formatters/repl-formatter.test.ts +0 -139
  46. package/src/env/debug/formatters/repl-formatter.ts +0 -358
  47. package/src/env/debug/formatters/trace-formatter.test.ts +0 -119
  48. package/src/env/debug/formatters/trace-formatter.ts +0 -191
  49. package/src/env/debug/formatters/tree-formatter.test.ts +0 -107
  50. package/src/env/debug/formatters/tree-formatter.ts +0 -325
  51. package/src/env/debug/index.ts +0 -38
  52. package/src/env/debug/parser/regex-parser.test.ts +0 -201
  53. package/src/env/debug/parser/regex-parser.ts +0 -196
  54. package/src/env/debug/parser/span-builder.test.ts +0 -241
  55. package/src/env/debug/parser/span-builder.ts +0 -386
  56. package/src/env/debug/reader/log-reader.test.ts +0 -170
  57. package/src/env/debug/reader/log-reader.ts +0 -186
  58. package/src/env/debug/reader/span-db-reader.test.ts +0 -118
  59. package/src/env/debug/reader/span-db-reader.ts +0 -201
  60. package/src/env/debug/types.test.ts +0 -187
  61. package/src/env/debug/types.ts +0 -171
  62. package/src/env/environment-init.test.ts +0 -183
  63. package/src/env/environment-lifecycle.test.ts +0 -516
  64. package/src/env/environment-service.test.ts +0 -332
  65. package/src/env/environment.handle-query.test.ts +0 -96
  66. package/src/env/environment.test.ts +0 -232
  67. package/src/env/environment.ts +0 -708
  68. package/src/env/errors.test.ts +0 -165
  69. package/src/env/errors.ts +0 -157
  70. package/src/env/event-source/event-source-agent-handler.test.ts +0 -193
  71. package/src/env/event-source/event-source-agent-handler.ts +0 -111
  72. package/src/env/event-source/event-source-component.process-cleanup.test.ts +0 -236
  73. package/src/env/event-source/event-source-component.stop.test.ts +0 -346
  74. package/src/env/event-source/event-source-component.test.ts +0 -1207
  75. package/src/env/event-source/event-source-component.ts +0 -1379
  76. package/src/env/event-source/event-source-config-registration.test.ts +0 -242
  77. package/src/env/event-source/event-source-config-registration.ts +0 -37
  78. package/src/env/event-source/event-source-integration.test.ts +0 -320
  79. package/src/env/event-source/event-source-platform.test.ts +0 -630
  80. package/src/env/event-source/types.ts +0 -298
  81. package/src/env/hook/global-hook-manager.ts +0 -162
  82. package/src/env/hook/hook-manager.test.ts +0 -374
  83. package/src/env/hook/hook-manager.ts +0 -309
  84. package/src/env/hook/index.ts +0 -38
  85. package/src/env/hook/types.ts +0 -138
  86. package/src/env/index.ts +0 -144
  87. package/src/env/interface.ts +0 -203
  88. package/src/env/llm/hooks.test.ts +0 -293
  89. package/src/env/llm/hooks.ts +0 -316
  90. package/src/env/llm/index.ts +0 -61
  91. package/src/env/llm/invoke-threshold-check.test.ts +0 -88
  92. package/src/env/llm/invoke-timeout.test.ts +0 -54
  93. package/src/env/llm/invoke.test.ts +0 -71
  94. package/src/env/llm/invoke.ts +0 -1039
  95. package/src/env/llm/llm-config.test.ts +0 -523
  96. package/src/env/llm/llm.test.ts +0 -233
  97. package/src/env/llm/llm.ts +0 -568
  98. package/src/env/llm/provider.test.ts +0 -182
  99. package/src/env/llm/provider.ts +0 -108
  100. package/src/env/llm/transform.test.ts +0 -251
  101. package/src/env/llm/transform.ts +0 -286
  102. package/src/env/llm/types.test.ts +0 -580
  103. package/src/env/llm/types.ts +0 -424
  104. package/src/env/log-trace/decorator-otel.test.ts +0 -182
  105. package/src/env/log-trace/decorator.ts +0 -230
  106. package/src/env/log-trace/index.ts +0 -79
  107. package/src/env/log-trace/log-trace-component.test.ts +0 -242
  108. package/src/env/log-trace/log-trace-component.ts +0 -497
  109. package/src/env/log-trace/log-trace-config-registration.test.ts +0 -348
  110. package/src/env/log-trace/log-trace-config-registration.ts +0 -45
  111. package/src/env/log-trace/logger.test.ts +0 -149
  112. package/src/env/log-trace/logger.ts +0 -522
  113. package/src/env/log-trace/opentelemetry/cli-propagation.test.ts +0 -147
  114. package/src/env/log-trace/opentelemetry/cli-propagation.ts +0 -194
  115. package/src/env/log-trace/opentelemetry/integration.test.ts +0 -668
  116. package/src/env/log-trace/opentelemetry/mod.ts +0 -25
  117. package/src/env/log-trace/opentelemetry/propagation-env.test.ts +0 -181
  118. package/src/env/log-trace/opentelemetry/propagation-env.ts +0 -136
  119. package/src/env/log-trace/opentelemetry/propagation.test.ts +0 -259
  120. package/src/env/log-trace/opentelemetry/propagation.ts +0 -215
  121. package/src/env/log-trace/opentelemetry/tracer-provider-context.test.ts +0 -166
  122. package/src/env/log-trace/opentelemetry/tracer-provider.test.ts +0 -379
  123. package/src/env/log-trace/opentelemetry/tracer-provider.ts +0 -612
  124. package/src/env/log-trace/span-storage.test.ts +0 -145
  125. package/src/env/log-trace/span-storage.ts +0 -230
  126. package/src/env/log-trace/trace-context.test.ts +0 -187
  127. package/src/env/log-trace/trace-context.ts +0 -162
  128. package/src/env/log-trace/types.test.ts +0 -63
  129. package/src/env/log-trace/types.ts +0 -172
  130. package/src/env/mcp/README.md +0 -244
  131. package/src/env/mcp/__integration__/mcp-component.integration.test.ts +0 -373
  132. package/src/env/mcp/config.test.ts +0 -74
  133. package/src/env/mcp/config.ts +0 -116
  134. package/src/env/mcp/index.ts +0 -41
  135. package/src/env/mcp/loader.test.ts +0 -161
  136. package/src/env/mcp/loader.ts +0 -209
  137. package/src/env/mcp/mcp-component.test.ts +0 -111
  138. package/src/env/mcp/mcp-component.ts +0 -358
  139. package/src/env/mcp/mcp-config-registration.test.ts +0 -304
  140. package/src/env/mcp/mcp-config-registration.ts +0 -50
  141. package/src/env/mcp/scanner.test.ts +0 -170
  142. package/src/env/mcp/scanner.ts +0 -246
  143. package/src/env/mcp/tool/adapter.test.ts +0 -520
  144. package/src/env/mcp/tool/adapter.ts +0 -521
  145. package/src/env/mcp/tool/index.ts +0 -5
  146. package/src/env/mcp/types.test.ts +0 -171
  147. package/src/env/mcp/types.ts +0 -79
  148. package/src/env/memory/README.md +0 -177
  149. package/src/env/memory/built-in/index.ts +0 -59
  150. package/src/env/memory/built-in/recall-memory.ts +0 -103
  151. package/src/env/memory/built-in/record-memory.ts +0 -148
  152. package/src/env/memory/index.ts +0 -20
  153. package/src/env/memory/memory-component.test.ts +0 -239
  154. package/src/env/memory/memory-component.ts +0 -503
  155. package/src/env/memory/memory-config-registration.test.ts +0 -67
  156. package/src/env/memory/memory-config-registration.ts +0 -48
  157. package/src/env/memory/memory-config.ts +0 -45
  158. package/src/env/memory/memory-file.test.ts +0 -268
  159. package/src/env/memory/plugin/index.ts +0 -48
  160. package/src/env/memory/plugin/memory-agent.test.ts +0 -249
  161. package/src/env/memory/plugin/memory-agent.ts +0 -365
  162. package/src/env/memory/plugin/memory-manager.ts +0 -198
  163. package/src/env/memory/plugin/memory-plugin-agent.test.ts +0 -145
  164. package/src/env/memory/plugin/memory-plugin.ts +0 -210
  165. package/src/env/memory/plugin/plugin-simplified.test.ts +0 -51
  166. package/src/env/memory/plugin/recall-memory.test.ts +0 -106
  167. package/src/env/memory/plugin/recall-memory.ts +0 -53
  168. package/src/env/memory/plugin/types.ts +0 -101
  169. package/src/env/memory/tools/memory-agent-tools.ts +0 -228
  170. package/src/env/memory/types.ts +0 -85
  171. package/src/env/paths.ts +0 -118
  172. package/src/env/prompt/index.ts +0 -18
  173. package/src/env/prompt/memory-prompts.test.ts +0 -91
  174. package/src/env/prompt/prompt-component.test.ts +0 -491
  175. package/src/env/prompt/prompt-component.ts +0 -619
  176. package/src/env/prompt/prompt-config-registration.test.ts +0 -213
  177. package/src/env/prompt/prompt-config-registration.ts +0 -39
  178. package/src/env/prompt/prompts-index.ts +0 -504
  179. package/src/env/prompt/renderer.ts +0 -67
  180. package/src/env/prompt/types.ts +0 -136
  181. package/src/env/session/hooks.ts +0 -18
  182. package/src/env/session/index.ts +0 -37
  183. package/src/env/session/search-query-parser.test.ts +0 -425
  184. package/src/env/session/search-query-parser.ts +0 -171
  185. package/src/env/session/session-checkpoint.test.ts +0 -523
  186. package/src/env/session/session-component.extract-recent-messages.test.ts +0 -209
  187. package/src/env/session/session-component.test.ts +0 -132
  188. package/src/env/session/session-component.ts +0 -1249
  189. package/src/env/session/session-config-registration.test.ts +0 -138
  190. package/src/env/session/session-config-registration.ts +0 -52
  191. package/src/env/session/session-message-converter.test.ts +0 -763
  192. package/src/env/session/session-message-converter.ts +0 -415
  193. package/src/env/session/session-message-e2e.test.ts +0 -448
  194. package/src/env/session/session-search.test.ts +0 -391
  195. package/src/env/session/session-store.test.ts +0 -362
  196. package/src/env/session/session-store.ts +0 -141
  197. package/src/env/session/storage/index.ts +0 -6
  198. package/src/env/session/storage/memory.ts +0 -502
  199. package/src/env/session/storage/sqlite.ts +0 -794
  200. package/src/env/session/types.ts +0 -742
  201. package/src/env/skill/config.ts +0 -39
  202. package/src/env/skill/index.ts +0 -6
  203. package/src/env/skill/parser.test.ts +0 -116
  204. package/src/env/skill/parser.ts +0 -77
  205. package/src/env/skill/scanner.test.ts +0 -211
  206. package/src/env/skill/scanner.ts +0 -119
  207. package/src/env/skill/skill-component.test.ts +0 -234
  208. package/src/env/skill/skill-component.ts +0 -352
  209. package/src/env/skill/skill-config-registration.test.ts +0 -60
  210. package/src/env/skill/skill-config-registration.ts +0 -43
  211. package/src/env/skill/tool/index.ts +0 -1
  212. package/src/env/skill/tool/skill-tool.test.ts +0 -100
  213. package/src/env/skill/tool/skill-tool.ts +0 -72
  214. package/src/env/skill/types.ts +0 -64
  215. package/src/env/task/delegate/delegate-tool.test.ts +0 -498
  216. package/src/env/task/delegate/delegate-tool.ts +0 -1014
  217. package/src/env/task/delegate/index.ts +0 -18
  218. package/src/env/task/delegate/stop-tool.test.ts +0 -140
  219. package/src/env/task/delegate/stop-tool.ts +0 -119
  220. package/src/env/task/delegate/task-events.test.ts +0 -178
  221. package/src/env/task/delegate/task-events.ts +0 -143
  222. package/src/env/task/hooks/contexts.test.ts +0 -92
  223. package/src/env/task/hooks/contexts.ts +0 -192
  224. package/src/env/task/hooks/index.ts +0 -23
  225. package/src/env/task/hooks/task-hook-points.test.ts +0 -32
  226. package/src/env/task/hooks/task-hook-points.ts +0 -54
  227. package/src/env/task/index.ts +0 -7
  228. package/src/env/task/plugins/index.ts +0 -13
  229. package/src/env/task/plugins/task-plugin.test.ts +0 -74
  230. package/src/env/task/plugins/task-plugin.ts +0 -89
  231. package/src/env/task/plugins/task-tag-plugin.test.ts +0 -377
  232. package/src/env/task/plugins/task-tag-plugin.ts +0 -319
  233. package/src/env/task/plugins/task-workflow-extractor.integration.test.ts +0 -226
  234. package/src/env/task/plugins/workflow-extractor-agent.test.ts +0 -107
  235. package/src/env/task/plugins/workflow-extractor-agent.ts +0 -225
  236. package/src/env/task/storage/index.ts +0 -6
  237. package/src/env/task/storage/sqlite-task-store.test.ts +0 -283
  238. package/src/env/task/storage/sqlite-task-store.ts +0 -903
  239. package/src/env/task/storage/task-search.test.ts +0 -291
  240. package/src/env/task/tag-service.test.ts +0 -198
  241. package/src/env/task/tag-service.ts +0 -264
  242. package/src/env/task/task-component.test.ts +0 -193
  243. package/src/env/task/task-component.ts +0 -658
  244. package/src/env/task/task-config-registration.test.ts +0 -57
  245. package/src/env/task/task-config-registration.ts +0 -37
  246. package/src/env/task/task-types.test.ts +0 -137
  247. package/src/env/task/tools/complete-tool.ts +0 -44
  248. package/src/env/task/tools/create-tool.ts +0 -49
  249. package/src/env/task/tools/delete-tool.ts +0 -43
  250. package/src/env/task/tools/get-tool.ts +0 -59
  251. package/src/env/task/tools/index.ts +0 -10
  252. package/src/env/task/tools/list-tool.ts +0 -40
  253. package/src/env/task/tools/operation/create-tool.ts +0 -48
  254. package/src/env/task/tools/operation/delete-tool.ts +0 -43
  255. package/src/env/task/tools/operation/get-tool.ts +0 -43
  256. package/src/env/task/tools/operation/index.ts +0 -9
  257. package/src/env/task/tools/operation/list-tool.ts +0 -40
  258. package/src/env/task/tools/operation/operation-tools.test.ts +0 -274
  259. package/src/env/task/tools/operation/operation-types.ts +0 -75
  260. package/src/env/task/tools/operation/update-tool.ts +0 -47
  261. package/src/env/task/tools/task-tools.test.ts +0 -203
  262. package/src/env/task/tools/task-types.test.ts +0 -75
  263. package/src/env/task/tools/task-types.ts +0 -68
  264. package/src/env/task/tools/update-tool.ts +0 -70
  265. package/src/env/task/types.ts +0 -160
  266. package/src/env/tool/built-in/bash.ts +0 -201
  267. package/src/env/tool/built-in/echo.ts +0 -29
  268. package/src/env/tool/built-in/edit-file.test.ts +0 -136
  269. package/src/env/tool/built-in/edit-file.ts +0 -92
  270. package/src/env/tool/built-in/glob.test.ts +0 -94
  271. package/src/env/tool/built-in/glob.ts +0 -65
  272. package/src/env/tool/built-in/grep.test.ts +0 -122
  273. package/src/env/tool/built-in/grep.ts +0 -108
  274. package/src/env/tool/built-in/index.ts +0 -44
  275. package/src/env/tool/built-in/read-file.test.ts +0 -84
  276. package/src/env/tool/built-in/read-file.ts +0 -75
  277. package/src/env/tool/built-in/write-file.test.ts +0 -119
  278. package/src/env/tool/built-in/write-file.ts +0 -68
  279. package/src/env/tool/index.ts +0 -24
  280. package/src/env/tool/registry.test.ts +0 -257
  281. package/src/env/tool/registry.ts +0 -167
  282. package/src/env/tool/tool-component.test.ts +0 -559
  283. package/src/env/tool/tool-component.ts +0 -563
  284. package/src/env/tool/tool-config-registration.test.ts +0 -249
  285. package/src/env/tool/tool-config-registration.ts +0 -46
  286. package/src/env/tool/types.ts +0 -267
  287. package/src/env/tool/validator.test.ts +0 -143
  288. package/src/env/tool/validator.ts +0 -44
  289. package/src/env/types.ts +0 -180
  290. package/src/env/workflow/ask-user-tool-registration.test.ts +0 -216
  291. package/src/env/workflow/complex-workflow.integration.test.ts +0 -1900
  292. package/src/env/workflow/decorators/decorator-node.ts +0 -229
  293. package/src/env/workflow/decorators/decorator.test.ts +0 -196
  294. package/src/env/workflow/decorators/edge.ts +0 -82
  295. package/src/env/workflow/decorators/index.ts +0 -31
  296. package/src/env/workflow/decorators/node-as.ts +0 -98
  297. package/src/env/workflow/decorators/workflow.ts +0 -54
  298. package/src/env/workflow/engine/dag-manager.test.ts +0 -570
  299. package/src/env/workflow/engine/dag-manager.ts +0 -594
  300. package/src/env/workflow/engine/engine.ts +0 -1422
  301. package/src/env/workflow/engine/event-bus.test.ts +0 -359
  302. package/src/env/workflow/engine/event-bus.ts +0 -156
  303. package/src/env/workflow/engine/executor-agent-session.test.ts +0 -84
  304. package/src/env/workflow/engine/executor.test.ts +0 -619
  305. package/src/env/workflow/engine/executor.ts +0 -593
  306. package/src/env/workflow/engine/index.ts +0 -24
  307. package/src/env/workflow/engine/node-registry.test.ts +0 -560
  308. package/src/env/workflow/engine/node-registry.ts +0 -289
  309. package/src/env/workflow/engine/resume-removed.test.ts +0 -22
  310. package/src/env/workflow/engine/scheduler.test.ts +0 -715
  311. package/src/env/workflow/engine/scheduler.ts +0 -318
  312. package/src/env/workflow/engine/workflow-engine.test.ts +0 -815
  313. package/src/env/workflow/extractor/workflow-converter.ts +0 -306
  314. package/src/env/workflow/fixtures.ts +0 -380
  315. package/src/env/workflow/index.ts +0 -38
  316. package/src/env/workflow/integration/run-resume-unified.test.ts +0 -186
  317. package/src/env/workflow/integration/service-integration.test.ts +0 -267
  318. package/src/env/workflow/metadata/keys.ts +0 -12
  319. package/src/env/workflow/nodes/agent-component-adapter.test.ts +0 -318
  320. package/src/env/workflow/nodes/agent-component-adapter.ts +0 -448
  321. package/src/env/workflow/nodes/agent-node.test.ts +0 -371
  322. package/src/env/workflow/nodes/agent-node.ts +0 -598
  323. package/src/env/workflow/nodes/ask-user-node.ts +0 -113
  324. package/src/env/workflow/nodes/condition-node.ts +0 -200
  325. package/src/env/workflow/nodes/index.ts +0 -9
  326. package/src/env/workflow/nodes/merge-node.ts +0 -141
  327. package/src/env/workflow/nodes/skill-node.test.ts +0 -253
  328. package/src/env/workflow/nodes/skill-node.ts +0 -393
  329. package/src/env/workflow/nodes/tool-node.test.ts +0 -251
  330. package/src/env/workflow/nodes/tool-node.ts +0 -493
  331. package/src/env/workflow/nodes/workflow-llm-history.test.ts +0 -455
  332. package/src/env/workflow/nodes/workflow-node.test.ts +0 -315
  333. package/src/env/workflow/nodes/workflow-node.ts +0 -311
  334. package/src/env/workflow/service/index.ts +0 -27
  335. package/src/env/workflow/service/registry.test.ts +0 -133
  336. package/src/env/workflow/service/registry.ts +0 -71
  337. package/src/env/workflow/service/workflow-service.test.ts +0 -310
  338. package/src/env/workflow/service/workflow-service.ts +0 -393
  339. package/src/env/workflow/storage/index.ts +0 -28
  340. package/src/env/workflow/storage/mock-repositories.ts +0 -385
  341. package/src/env/workflow/storage/sqlite.test.ts +0 -179
  342. package/src/env/workflow/storage/sqlite.ts +0 -163
  343. package/src/env/workflow/storage/workflow-repo.test.ts +0 -780
  344. package/src/env/workflow/storage/workflow-repo.ts +0 -342
  345. package/src/env/workflow/tools/ask-user-tool.ts +0 -82
  346. package/src/env/workflow/tools/index.ts +0 -26
  347. package/src/env/workflow/tools/run-workflow.test.ts +0 -352
  348. package/src/env/workflow/tools/run-workflow.ts +0 -214
  349. package/src/env/workflow/types/context.ts +0 -18
  350. package/src/env/workflow/types/decorators-types.ts +0 -198
  351. package/src/env/workflow/types/event.test.ts +0 -515
  352. package/src/env/workflow/types/event.ts +0 -193
  353. package/src/env/workflow/types/index.ts +0 -49
  354. package/src/env/workflow/types/run.test.ts +0 -437
  355. package/src/env/workflow/types/run.ts +0 -173
  356. package/src/env/workflow/types/workflow-hil.ts +0 -114
  357. package/src/env/workflow/types/workflow-message.test.ts +0 -138
  358. package/src/env/workflow/types/workflow-message.ts +0 -196
  359. package/src/env/workflow/types/workflow-session.test.ts +0 -95
  360. package/src/env/workflow/types/workflow-session.ts +0 -59
  361. package/src/env/workflow/types/workflow.test.ts +0 -495
  362. package/src/env/workflow/types/workflow.ts +0 -195
  363. package/src/env/workflow/types_compat.ts +0 -51
  364. package/src/env/workflow/utils/create-workflow.ts +0 -47
  365. package/src/env/workflow/utils/execution-state.ts +0 -245
  366. package/src/env/workflow/utils/index.ts +0 -18
  367. package/src/env/workflow/utils/node-registry-helper.ts +0 -58
  368. package/src/env/workflow/utils/recovery-validator.test.ts +0 -460
  369. package/src/env/workflow/utils/recovery-validator.ts +0 -377
  370. package/src/env/workflow/utils/session-parser.test.ts +0 -111
  371. package/src/env/workflow/utils/session-parser.ts +0 -94
  372. package/src/env/workflow/utils/session-recovery.test.ts +0 -334
  373. package/src/env/workflow/utils/session-recovery.ts +0 -188
  374. package/src/env/workflow/utils/template-resolver.test.ts +0 -258
  375. package/src/env/workflow/utils/template-resolver.ts +0 -436
  376. package/src/env/workflow/utils/validation-rules.ts +0 -149
  377. package/src/env/workflow/workflow-component.ts +0 -544
  378. package/src/index.ts +0 -422
  379. package/src/utils/id.ts +0 -21
@@ -1,1900 +0,0 @@
1
- /**
2
- * Complex Multi-Node Workflow Integration Tests
3
- *
4
- * Tests for complex workflow patterns including:
5
- * - Parallel execution branches
6
- * - Diamond patterns (merge and fork)
7
- * - Error handling and retry logic
8
- * - Conditional execution
9
- * - Template variable passing
10
- * - Nested workflows
11
- * - Complex dependency graphs
12
- */
13
-
14
- import { describe, it, expect, beforeEach, afterEach } from 'vitest';
15
- import { z } from 'zod';
16
- import type { WorkflowDefinition, NodeDefinition } from './types';
17
-
18
- // ==================== Mock Workflow Executor ====================
19
-
20
- interface MockNodeExecution {
21
- nodeId: string;
22
- status: 'pending' | 'running' | 'completed' | 'failed';
23
- input: any;
24
- output: any;
25
- error?: string;
26
- startTime?: number;
27
- endTime?: number;
28
- retryCount: number;
29
- }
30
-
31
- interface ExecutionTrace {
32
- nodes: Map<string, MockNodeExecution>;
33
- executionOrder: string[];
34
- parallelBatches: string[][];
35
- errors: Array<{ nodeId: string; error: string; attempt: number }>;
36
- }
37
-
38
- /**
39
- * Synchronous Workflow Executor for Testing
40
- */
41
- class SyncWorkflowExecutor {
42
- private trace: ExecutionTrace;
43
- private nodeConfigs: Map<string, any>;
44
-
45
- constructor() {
46
- this.trace = {
47
- nodes: new Map(),
48
- executionOrder: [],
49
- parallelBatches: [],
50
- errors: [],
51
- };
52
- this.nodeConfigs = new Map();
53
- }
54
-
55
- setNodeConfig(nodeId: string, config: any): void {
56
- this.nodeConfigs.set(nodeId, config);
57
- }
58
-
59
- getTrace(): ExecutionTrace {
60
- return this.trace;
61
- }
62
-
63
- clearTrace(): void {
64
- this.trace = {
65
- nodes: new Map(),
66
- executionOrder: [],
67
- parallelBatches: [],
68
- errors: [],
69
- };
70
- }
71
-
72
- executeNode(nodeId: string, input: any, dependencies: Map<string, any>, nodeType?: string): any {
73
- const config = this.nodeConfigs.get(nodeId) || {};
74
-
75
- // Initialize trace entry
76
- const traceEntry: MockNodeExecution = {
77
- nodeId,
78
- status: 'pending',
79
- input,
80
- output: null,
81
- retryCount: 0,
82
- };
83
- this.trace.nodes.set(nodeId, traceEntry);
84
-
85
- // Execute node
86
- traceEntry.status = 'running';
87
- traceEntry.startTime = Date.now();
88
-
89
- // Use provided nodeType or fall back to config.type
90
- const effectiveNodeType = nodeType || config.type || 'unknown';
91
- let output: any;
92
-
93
- switch (effectiveNodeType) {
94
- case 'tool':
95
- output = this.executeToolNode(nodeId, config, input);
96
- break;
97
- case 'skill':
98
- output = this.executeSkillNode(nodeId, config, input, dependencies);
99
- break;
100
- case 'agent':
101
- output = this.executeAgentNode(nodeId, config, input, dependencies);
102
- break;
103
- case 'condition':
104
- output = this.executeConditionNode(nodeId, config, input);
105
- break;
106
- case 'merge':
107
- output = this.executeMergeNode(nodeId, config, dependencies);
108
- break;
109
- case 'nested':
110
- case 'workflow':
111
- output = this.executeNestedWorkflowNode(nodeId, config, input);
112
- break;
113
- default:
114
- output = { result: `${nodeId}_result`, input };
115
- }
116
-
117
- traceEntry.output = output;
118
- traceEntry.status = 'completed';
119
- traceEntry.endTime = Date.now();
120
- this.trace.executionOrder.push(nodeId);
121
-
122
- return output;
123
- }
124
-
125
- private executeToolNode(nodeId: string, config: any, input: any): any {
126
- const toolName = config.tool || 'default-tool';
127
- if (toolName === 'echo') {
128
- return { echo: input, message: config.message };
129
- }
130
- if (toolName === 'calculator') {
131
- return { result: 42, operation: config.operation };
132
- }
133
- if (toolName === 'validator') {
134
- return { valid: true, validated: input, source: config.source };
135
- }
136
- return { tool: toolName, result: `processed_by_${toolName}` };
137
- }
138
-
139
- private executeSkillNode(
140
- nodeId: string,
141
- config: any,
142
- input: any,
143
- dependencies: Map<string, any>
144
- ): any {
145
- const skillName = config.skill || 'default-skill';
146
- return {
147
- skill: skillName,
148
- result: `skill_${skillName}_executed`,
149
- inputsFromDeps: Object.fromEntries(dependencies),
150
- };
151
- }
152
-
153
- private executeAgentNode(
154
- nodeId: string,
155
- config: any,
156
- input: any,
157
- dependencies: Map<string, any>
158
- ): any {
159
- const agentType = config.agent || 'general';
160
- return {
161
- agent: agentType,
162
- reasoning: `Agent ${agentType} processed input`,
163
- input,
164
- };
165
- }
166
-
167
- private executeConditionNode(nodeId: string, config: any, input: any): any {
168
- const condition = config.condition || 'true';
169
- const result = this.evaluateCondition(condition, input);
170
- return { conditionMet: result, evaluatedCondition: condition };
171
- }
172
-
173
- private executeMergeNode(
174
- nodeId: string,
175
- config: any,
176
- dependencies: Map<string, any>
177
- ): any {
178
- const mergedInputs: any = {};
179
- for (const [depId, depOutput] of dependencies) {
180
- mergedInputs[depId] = depOutput;
181
- }
182
- return {
183
- merged: true,
184
- inputs: mergedInputs,
185
- mergeStrategy: config.strategy || 'collect',
186
- valid: true,
187
- };
188
- }
189
-
190
- private executeNestedWorkflowNode(nodeId: string, config: any, input: any): any {
191
- const workflowName = config.workflow || config.workflow_name || 'default-nested';
192
- return {
193
- nestedWorkflow: workflowName,
194
- workflowName,
195
- input,
196
- result: `nested_${workflowName}_executed`,
197
- };
198
- }
199
-
200
- private evaluateCondition(condition: string, context: any): boolean {
201
- // Handle template variable conditions
202
- if (condition === '{{input.skip}}') {
203
- return context?.skip !== false && !!context?.skip;
204
- }
205
- if (condition === '{{input.mode}}') {
206
- return !!context?.mode;
207
- }
208
- if (condition === 'true') return true;
209
- if (condition.includes('valid')) return true;
210
- if (condition.includes('{{process.result}}')) return true;
211
- return condition !== 'false';
212
- }
213
- }
214
-
215
- // ==================== Workflow Execution Engine ====================
216
-
217
- function executeWorkflowSync(
218
- definition: WorkflowDefinition,
219
- input?: any,
220
- executor?: SyncWorkflowExecutor
221
- ): Map<string, any> {
222
- const exec = executor || new SyncWorkflowExecutor();
223
- const results = new Map<string, any>();
224
- const nodeMap = new Map<string, NodeDefinition>();
225
-
226
- // Build node map and config
227
- for (const node of definition.nodes) {
228
- nodeMap.set(node.id, node);
229
- exec.setNodeConfig(node.id, node.config);
230
- }
231
-
232
- // Topological sort for execution order
233
- const order = topologicalSort(definition);
234
-
235
- // Execute in order
236
- const dependencies = new Map<string, any>();
237
-
238
- for (const nodeId of order) {
239
- const node = nodeMap.get(nodeId)!;
240
-
241
- // Get dependency outputs
242
- const depOutputs = new Map<string, any>();
243
- for (const depId of node.depends_on || []) {
244
- depOutputs.set(depId, dependencies.get(depId));
245
- }
246
-
247
- // Execute node with explicit node type
248
- const output = exec.executeNode(nodeId, { ...input }, depOutputs, node.type);
249
- dependencies.set(nodeId, output);
250
- results.set(nodeId, output);
251
- }
252
-
253
- return results;
254
- }
255
-
256
- function topologicalSort(workflow: WorkflowDefinition): string[] {
257
- const nodeMap = new Map(workflow.nodes.map(n => [n.id, n]));
258
- const visited = new Set<string>();
259
- const temp = new Set<string>();
260
- const order: string[] = [];
261
-
262
- function visit(nodeId: string): void {
263
- if (temp.has(nodeId)) {
264
- throw new Error(`Circular dependency detected: ${nodeId}`);
265
- }
266
- if (visited.has(nodeId)) return;
267
-
268
- temp.add(nodeId);
269
-
270
- const node = nodeMap.get(nodeId);
271
- if (node) {
272
- for (const depId of node.depends_on || []) {
273
- visit(depId);
274
- }
275
- }
276
-
277
- temp.delete(nodeId);
278
- visited.add(nodeId);
279
- order.push(nodeId);
280
- }
281
-
282
- // Visit all nodes
283
- for (const node of workflow.nodes) {
284
- if (!visited.has(node.id)) {
285
- visit(node.id);
286
- }
287
- }
288
-
289
- return order;
290
- }
291
-
292
- // ==================== Complex Workflow Definitions ====================
293
-
294
- /**
295
- * Complex Workflow: Data Processing Pipeline
296
- *
297
- * This workflow simulates a complex data processing pipeline with:
298
- * - 12 nodes total
299
- * - Parallel data fetching branches
300
- * - Diamond merge pattern
301
- * - Conditional processing
302
- * - Error handling with retry
303
- */
304
- const createDataProcessingWorkflow = (): WorkflowDefinition => ({
305
- name: 'data-processing-pipeline',
306
- version: '1.0',
307
- description: 'Complex data processing pipeline with parallel branches',
308
- config: {
309
- parallel_limit: 3,
310
- timeout: 300000,
311
- },
312
- nodes: [
313
- // Entry node - Initialize pipeline
314
- {
315
- id: 'init',
316
- type: 'tool',
317
- name: 'Initialize Pipeline',
318
- config: { tool: 'echo', message: 'Starting data pipeline' },
319
- },
320
-
321
- // Parallel fetch branch 1
322
- {
323
- id: 'fetch-users',
324
- type: 'tool',
325
- name: 'Fetch Users',
326
- config: { tool: 'validator', source: 'users' },
327
- depends_on: ['init'],
328
- },
329
-
330
- // Parallel fetch branch 2
331
- {
332
- id: 'fetch-products',
333
- type: 'tool',
334
- name: 'Fetch Products',
335
- config: { tool: 'validator', source: 'products' },
336
- depends_on: ['init'],
337
- },
338
-
339
- // Parallel fetch branch 3
340
- {
341
- id: 'fetch-orders',
342
- type: 'tool',
343
- name: 'Fetch Orders',
344
- config: { tool: 'validator', source: 'orders' },
345
- depends_on: ['init'],
346
- },
347
-
348
- // Diamond pattern - merge point
349
- {
350
- id: 'merge-data',
351
- type: 'merge',
352
- name: 'Merge All Data',
353
- config: { strategy: 'collect' },
354
- depends_on: ['fetch-users', 'fetch-products', 'fetch-orders'],
355
- },
356
-
357
- // Diamond pattern - fork to processing branches
358
- {
359
- id: 'validate-data',
360
- type: 'condition',
361
- name: 'Validate Merged Data',
362
- config: { condition: '{{merge-data.valid}}' },
363
- depends_on: ['merge-data'],
364
- },
365
-
366
- // Processing branch 1 - with retry
367
- {
368
- id: 'process-users',
369
- type: 'tool',
370
- name: 'Process Users',
371
- config: { tool: 'calculator', operation: 'user-scoring' },
372
- depends_on: ['validate-data'],
373
- retry: { max_attempts: 3, backoff: 'exponential', initial_delay: 1000 },
374
- },
375
-
376
- // Processing branch 2
377
- {
378
- id: 'process-products',
379
- type: 'tool',
380
- name: 'Process Products',
381
- config: { tool: 'calculator', operation: 'product-rating' },
382
- depends_on: ['validate-data'],
383
- },
384
-
385
- // Diamond merge - final processing
386
- {
387
- id: 'finalize-results',
388
- type: 'tool',
389
- name: 'Finalize Results',
390
- config: { tool: 'echo', message: 'Finalizing' },
391
- depends_on: ['process-users', 'process-products'],
392
- },
393
-
394
- // Conditional node
395
- {
396
- id: 'generate-report',
397
- type: 'condition',
398
- name: 'Check Report Generation',
399
- config: { condition: '{{input.generateReport}}' },
400
- depends_on: ['finalize-results'],
401
- },
402
-
403
- // Nested workflow call
404
- {
405
- id: 'create-report',
406
- type: 'nested',
407
- name: 'Create Report',
408
- config: { workflow: 'report-generation' },
409
- depends_on: ['generate-report'],
410
- },
411
-
412
- // Final output node
413
- {
414
- id: 'output',
415
- type: 'tool',
416
- name: 'Output Results',
417
- config: { tool: 'echo', message: 'Pipeline complete' },
418
- depends_on: ['create-report'],
419
- },
420
- ],
421
- entry: 'init',
422
- outputs: [
423
- { name: 'final_result', source: 'output', path: 'result' },
424
- { name: 'user_data', source: 'process-users', path: 'result' },
425
- { name: 'product_data', source: 'process-products', path: 'result' },
426
- ],
427
- metadata: {
428
- author: 'Roy Agent',
429
- tags: ['data-processing', 'pipeline', 'complex'],
430
- },
431
- });
432
-
433
- /**
434
- * Complex Workflow: Parallel Processing with Error Handling
435
- *
436
- * Features:
437
- * - Multiple parallel branches
438
- * - Retry logic on failure-prone nodes
439
- * - Error recovery paths
440
- * - Timeout handling
441
- */
442
- const createParallelProcessingWorkflow = (): WorkflowDefinition => ({
443
- name: 'parallel-processing',
444
- version: '1.0',
445
- description: 'Parallel processing with error handling and retry',
446
- nodes: [
447
- { id: 'entry', type: 'tool', config: { tool: 'echo' } },
448
-
449
- // Parallel branch A
450
- { id: 'task-a1', type: 'tool', config: { tool: 'calculator' }, depends_on: ['entry'] },
451
- { id: 'task-a2', type: 'tool', config: { tool: 'calculator' }, depends_on: ['task-a1'] },
452
- { id: 'task-a3', type: 'tool', config: { tool: 'calculator' }, depends_on: ['task-a2'] },
453
-
454
- // Parallel branch B
455
- { id: 'task-b1', type: 'tool', config: { tool: 'calculator' }, depends_on: ['entry'] },
456
- { id: 'task-b2', type: 'tool', config: { tool: 'calculator' }, depends_on: ['task-b1'] },
457
- {
458
- id: 'task-b3',
459
- type: 'tool',
460
- config: { tool: 'calculator' },
461
- depends_on: ['task-b1'],
462
- retry: { max_attempts: 2, backoff: 'fixed', initial_delay: 500 },
463
- },
464
-
465
- // Parallel branch C - with potential failure
466
- { id: 'task-c1', type: 'tool', config: { tool: 'calculator' }, depends_on: ['entry'] },
467
- {
468
- id: 'task-c2',
469
- type: 'tool',
470
- config: { tool: 'calculator' },
471
- depends_on: ['task-c1'],
472
- retry: { max_attempts: 3, backoff: 'exponential', initial_delay: 1000 },
473
- },
474
-
475
- // Merge all branches
476
- {
477
- id: 'merge-ab',
478
- type: 'merge',
479
- config: { strategy: 'collect' },
480
- depends_on: ['task-a3', 'task-b2', 'task-c2'],
481
- },
482
-
483
- // Final processing
484
- { id: 'final', type: 'tool', config: { tool: 'echo' }, depends_on: ['merge-ab'] },
485
- ],
486
- });
487
-
488
- /**
489
- * Complex Workflow: Nested Workflow Composition
490
- *
491
- * Features:
492
- * - Nested workflow calls
493
- * - Multiple levels of abstraction
494
- * - Shared state passing
495
- * - Complex dependency chains
496
- */
497
- const createNestedWorkflow = (): WorkflowDefinition => ({
498
- name: 'nested-workflow-composition',
499
- version: '1.0',
500
- nodes: [
501
- { id: 'start', type: 'tool', config: { tool: 'echo' }, depends_on: [] },
502
-
503
- // First level nested
504
- { id: 'sub-workflow-1', type: 'workflow', config: { workflow_name: 'sub-workflow-a' }, depends_on: ['start'] },
505
- { id: 'sub-workflow-2', type: 'workflow', config: { workflow_name: 'sub-workflow-b' }, depends_on: ['start'] },
506
-
507
- // Second level - parallel execution
508
- { id: 'level-2-a', type: 'tool', config: { tool: 'validator' }, depends_on: ['sub-workflow-1'] },
509
- { id: 'level-2-b', type: 'tool', config: { tool: 'validator' }, depends_on: ['sub-workflow-1'] },
510
- { id: 'level-2-c', type: 'tool', config: { tool: 'validator' }, depends_on: ['sub-workflow-2'] },
511
-
512
- // Merge and continue
513
- { id: 'merge-2', type: 'merge', config: { strategy: 'collect' }, depends_on: ['level-2-a', 'level-2-b', 'level-2-c'] },
514
-
515
- // Third level
516
- { id: 'level-3', type: 'tool', config: { tool: 'calculator' }, depends_on: ['merge-2'] },
517
-
518
- // Final nested call
519
- { id: 'final-nested', type: 'workflow', config: { workflow_name: 'final-workflow' }, depends_on: ['level-3'] },
520
- ],
521
- });
522
-
523
- /**
524
- * Complex Workflow: Conditional Execution Graph
525
- *
526
- * Features:
527
- * - Multiple conditional branches
528
- * - Skip logic
529
- * - Dynamic paths based on runtime conditions
530
- */
531
- const createConditionalWorkflow = (): WorkflowDefinition => ({
532
- name: 'conditional-execution',
533
- version: '1.0',
534
- nodes: [
535
- { id: 'init', type: 'tool', config: { tool: 'echo' } },
536
-
537
- // First condition
538
- {
539
- id: 'check-mode',
540
- type: 'condition',
541
- config: { condition: '{{input.mode}}' },
542
- depends_on: ['init'],
543
- },
544
-
545
- // Mode A path
546
- { id: 'mode-a-1', type: 'tool', config: { tool: 'calculator' }, depends_on: ['check-mode'] },
547
- { id: 'mode-a-2', type: 'tool', config: { tool: 'calculator' }, depends_on: ['mode-a-1'] },
548
-
549
- // Mode B path
550
- { id: 'mode-b-1', type: 'tool', config: { tool: 'validator' }, depends_on: ['check-mode'] },
551
- { id: 'mode-b-2', type: 'tool', config: { tool: 'validator' }, depends_on: ['mode-b-1'] },
552
-
553
- // Merge after conditional paths
554
- { id: 'merge-cond', type: 'merge', config: { strategy: 'collect' }, depends_on: ['mode-a-2', 'mode-b-2'] },
555
-
556
- // Final check
557
- {
558
- id: 'final-check',
559
- type: 'condition',
560
- config: { condition: '{{merge-cond.valid}}' },
561
- depends_on: ['merge-cond'],
562
- },
563
-
564
- // Output
565
- { id: 'output', type: 'tool', config: { tool: 'echo' }, depends_on: ['final-check'] },
566
- ],
567
- });
568
-
569
- /**
570
- * Complex Workflow: Diamond Pattern
571
- *
572
- * A -> B1 -> C -> D
573
- * -> B2 ->
574
- * -> B3 ->
575
- *
576
- * Classic diamond dependency pattern
577
- */
578
- const createDiamondWorkflow = (): WorkflowDefinition => ({
579
- name: 'diamond-pattern',
580
- version: '1.0',
581
- nodes: [
582
- { id: 'A', type: 'tool', config: { tool: 'echo' }, depends_on: [] },
583
- { id: 'B1', type: 'tool', config: { tool: 'calculator' }, depends_on: ['A'] },
584
- { id: 'B2', type: 'tool', config: { tool: 'calculator' }, depends_on: ['A'] },
585
- { id: 'B3', type: 'tool', config: { tool: 'calculator' }, depends_on: ['A'] },
586
- {
587
- id: 'C',
588
- type: 'tool',
589
- config: { tool: 'calculator' },
590
- depends_on: ['B1', 'B2', 'B3'],
591
- retry: { max_attempts: 2, backoff: 'exponential', initial_delay: 500 },
592
- },
593
- { id: 'D', type: 'tool', config: { tool: 'echo' }, depends_on: ['C'] },
594
- ],
595
- });
596
-
597
- /**
598
- * Complex Workflow: Multi-Node Type Orchestration
599
- *
600
- * Demonstrates chaining different node types:
601
- * - ToolNode → AgentNode → SkillNode → WorkflowNode → ToolNode
602
- * - Template variable passing between different node types
603
- * - Real-world AI pipeline pattern
604
- */
605
- const createMultiNodeTypeWorkflow = (): WorkflowDefinition => ({
606
- name: 'multi-node-type-orchestration',
607
- version: '1.0',
608
- description: 'Orchestration across Tool, Agent, Skill, and Workflow nodes',
609
- nodes: [
610
- // ========== Step 1: Tool - Data Preparation ==========
611
- {
612
- id: 'prepare-data',
613
- type: 'tool',
614
- name: 'Prepare Input Data',
615
- config: { tool: 'echo', message: 'Preparing initial data' },
616
- depends_on: [],
617
- },
618
-
619
- // ========== Step 2: Agent - Analysis ==========
620
- {
621
- id: 'analyze-data',
622
- type: 'agent',
623
- name: 'AI Data Analyst',
624
- config: {
625
- agent_type: 'general',
626
- prompt: 'Analyze the input data: {{prepare-data.output}}. Provide insights.',
627
- },
628
- depends_on: ['prepare-data'],
629
- },
630
-
631
- // ========== Step 3: Tool - Validate Analysis ==========
632
- {
633
- id: 'validate-analysis',
634
- type: 'tool',
635
- name: 'Validate Analysis Results',
636
- config: { tool: 'validator', source: 'analysis' },
637
- depends_on: ['analyze-data'],
638
- },
639
-
640
- // ========== Step 4: Agent - Decision Making ==========
641
- {
642
- id: 'make-decision',
643
- type: 'agent',
644
- name: 'Decision Agent',
645
- config: {
646
- agent_type: 'general',
647
- prompt: 'Based on analysis: {{analyze-data.output}}, decide next steps.',
648
- },
649
- depends_on: ['validate-analysis'],
650
- },
651
-
652
- // ========== Step 5: Skill - Transform Data ==========
653
- {
654
- id: 'transform-data',
655
- type: 'skill',
656
- name: 'Data Transformation Skill',
657
- config: {
658
- skill: 'data-transform',
659
- input: {
660
- data: '{{analyze-data.output}}',
661
- format: 'json',
662
- },
663
- },
664
- depends_on: ['make-decision'],
665
- },
666
-
667
- // ========== Step 6: Nested Workflow ==========
668
- {
669
- id: 'process-workflow',
670
- type: 'workflow',
671
- name: 'Sub-Process Workflow',
672
- config: {
673
- workflow_name: 'data-processing-subflow',
674
- input: {
675
- source: '{{transform-data.output}}',
676
- },
677
- },
678
- depends_on: ['transform-data'],
679
- },
680
-
681
- // ========== Step 7: Tool - Final Output ==========
682
- {
683
- id: 'format-output',
684
- type: 'tool',
685
- name: 'Format Final Output',
686
- config: { tool: 'calculator', operation: 'format' },
687
- depends_on: ['process-workflow'],
688
- },
689
- ],
690
- outputs: [
691
- { name: 'analysis_result', source: 'analyze-data', path: 'output' },
692
- { name: 'decision', source: 'make-decision', path: 'output' },
693
- { name: 'transformed_data', source: 'transform-data', path: 'output' },
694
- { name: 'final_result', source: 'format-output', path: 'result' },
695
- ],
696
- metadata: {
697
- author: 'Roy Agent',
698
- tags: ['multi-node', 'orchestration', 'ai-pipeline'],
699
- },
700
- });
701
-
702
- /**
703
- * Complex Workflow: Tool-Skill-Agent Chain
704
- *
705
- * A realistic AI pipeline demonstrating:
706
- * - Tool (data fetch)
707
- * - Agent (reasoning)
708
- * - Skill (processing)
709
- * - Another Agent (verification)
710
- * - Tool (output)
711
- */
712
- const createToolSkillAgentChain = (): WorkflowDefinition => ({
713
- name: 'tool-skill-agent-chain',
714
- version: '1.0',
715
- nodes: [
716
- // Tool: Fetch raw data
717
- {
718
- id: 'fetch-raw-data',
719
- type: 'tool',
720
- name: 'Fetch Raw Data',
721
- config: { tool: 'echo', message: 'Fetching data from source' },
722
- depends_on: [],
723
- },
724
-
725
- // Agent: Initial understanding
726
- {
727
- id: 'understand-context',
728
- type: 'agent',
729
- name: 'Context Understanding Agent',
730
- config: {
731
- agent_type: 'general',
732
- prompt: 'Understand the context of: {{fetch-raw-data.output}}',
733
- },
734
- depends_on: ['fetch-raw-data'],
735
- },
736
-
737
- // Skill: Process with skill
738
- {
739
- id: 'process-with-skill',
740
- type: 'skill',
741
- name: 'Processing Skill',
742
- config: {
743
- skill: 'text-processor',
744
- input: {
745
- text: '{{understand-context.output}}',
746
- options: { lowercase: true },
747
- },
748
- },
749
- depends_on: ['understand-context'],
750
- },
751
-
752
- // Agent: Deep reasoning
753
- {
754
- id: 'deep-reasoning',
755
- type: 'agent',
756
- name: 'Deep Reasoning Agent',
757
- config: {
758
- agent_type: 'general',
759
- prompt: 'Perform deep reasoning on: {{process-with-skill.output}}',
760
- },
761
- depends_on: ['process-with-skill'],
762
- },
763
-
764
- // Skill: Another skill processing
765
- {
766
- id: 'enhance-result',
767
- type: 'skill',
768
- name: 'Enhancement Skill',
769
- config: {
770
- skill: 'enhancer',
771
- input: {
772
- data: '{{deep-reasoning.output}}',
773
- },
774
- },
775
- depends_on: ['deep-reasoning'],
776
- },
777
-
778
- // Tool: Final validation
779
- {
780
- id: 'final-validate',
781
- type: 'tool',
782
- name: 'Final Validation',
783
- config: { tool: 'validator', source: 'final' },
784
- depends_on: ['enhance-result'],
785
- },
786
- ],
787
- });
788
-
789
- /**
790
- * Complex Workflow: Parallel Tool-Agent Branches
791
- *
792
- * Multiple parallel branches, each with Tool → Agent pattern
793
- */
794
- const createParallelToolAgentWorkflow = (): WorkflowDefinition => ({
795
- name: 'parallel-tool-agent-branches',
796
- version: '1.0',
797
- nodes: [
798
- // Entry
799
- { id: 'start', type: 'tool', config: { tool: 'echo' }, depends_on: [] },
800
-
801
- // Branch A: Tool → Agent
802
- { id: 'tool-a', type: 'tool', config: { tool: 'calculator' }, depends_on: ['start'] },
803
- {
804
- id: 'agent-a',
805
- type: 'agent',
806
- config: { agent_type: 'general', prompt: 'Process: {{tool-a.output}}' },
807
- depends_on: ['tool-a'],
808
- },
809
-
810
- // Branch B: Tool → Agent
811
- { id: 'tool-b', type: 'tool', config: { tool: 'validator' }, depends_on: ['start'] },
812
- {
813
- id: 'agent-b',
814
- type: 'agent',
815
- config: { agent_type: 'general', prompt: 'Process: {{tool-b.output}}' },
816
- depends_on: ['tool-b'],
817
- },
818
-
819
- // Branch C: Tool → Agent
820
- { id: 'tool-c', type: 'tool', config: { tool: 'echo' }, depends_on: ['start'] },
821
- {
822
- id: 'agent-c',
823
- type: 'agent',
824
- config: { agent_type: 'general', prompt: 'Process: {{tool-c.output}}' },
825
- depends_on: ['tool-c'],
826
- },
827
-
828
- // Merge all agent outputs
829
- {
830
- id: 'merge-agents',
831
- type: 'merge',
832
- config: { strategy: 'collect' },
833
- depends_on: ['agent-a', 'agent-b', 'agent-c'],
834
- },
835
-
836
- // Final aggregation agent
837
- {
838
- id: 'aggregate-results',
839
- type: 'agent',
840
- name: 'Aggregation Agent',
841
- config: {
842
- agent_type: 'general',
843
- prompt: 'Aggregate and summarize results from all branches.',
844
- },
845
- depends_on: ['merge-agents'],
846
- },
847
-
848
- // Output tool
849
- { id: 'output', type: 'tool', config: { tool: 'echo' }, depends_on: ['aggregate-results'] },
850
- ],
851
- });
852
-
853
- /**
854
- * Complex Workflow: Template Variable Passing
855
- *
856
- * Tests HIL (Human Input Language) template variable passing
857
- */
858
- const createTemplateVariableWorkflow = (): WorkflowDefinition => ({
859
- name: 'template-variables',
860
- version: '1.0',
861
- nodes: [
862
- {
863
- id: 'init',
864
- type: 'tool',
865
- config: { tool: 'echo', message: '{{input.greeting}}' },
866
- },
867
- {
868
- id: 'process',
869
- type: 'tool',
870
- config: { tool: 'calculator', expression: '{{init.result}} + {{input.value}}' },
871
- depends_on: ['init'],
872
- },
873
- {
874
- id: 'validate',
875
- type: 'condition',
876
- config: { condition: '{{process.result}} > {{input.threshold}}' },
877
- depends_on: ['process'],
878
- },
879
- {
880
- id: 'output',
881
- type: 'tool',
882
- config: { tool: 'echo', message: 'Result: {{process.result}}, Valid: {{validate.conditionMet}}' },
883
- depends_on: ['validate'],
884
- },
885
- ],
886
- });
887
-
888
- /**
889
- * Complex Workflow: Complex Dependency Graph
890
- *
891
- * Features:
892
- * - Multiple entry points
893
- * - Complex merge patterns
894
- * - Cross-branch dependencies
895
- */
896
- const createComplexDependencyWorkflow = (): WorkflowDefinition => ({
897
- name: 'complex-dependency-graph',
898
- version: '1.0',
899
- nodes: [
900
- // Two entry points
901
- { id: 'entry-1', type: 'tool', config: { tool: 'echo' }, depends_on: [] },
902
- { id: 'entry-2', type: 'tool', config: { tool: 'echo' }, depends_on: [] },
903
-
904
- // Parallel branches from entry-1
905
- { id: 'branch-1a', type: 'tool', config: { tool: 'calculator' }, depends_on: ['entry-1'] },
906
- { id: 'branch-1b', type: 'tool', config: { tool: 'calculator' }, depends_on: ['entry-1'] },
907
-
908
- // Parallel branches from entry-2
909
- { id: 'branch-2a', type: 'tool', config: { tool: 'validator' }, depends_on: ['entry-2'] },
910
- { id: 'branch-2b', type: 'tool', config: { tool: 'validator' }, depends_on: ['entry-2'] },
911
-
912
- // Cross-branch dependency: branch-1a depends on branch-2a
913
- { id: 'cross-dep', type: 'tool', config: { tool: 'calculator' }, depends_on: ['branch-1a', 'branch-2a'] },
914
-
915
- // Merge all
916
- { id: 'merge-1', type: 'merge', config: { strategy: 'collect' }, depends_on: ['branch-1b', 'branch-2b', 'cross-dep'] },
917
-
918
- // Final processing
919
- { id: 'final', type: 'tool', config: { tool: 'echo' }, depends_on: ['merge-1'] },
920
- ],
921
- });
922
-
923
- // ==================== Test Suites ====================
924
-
925
- describe('Complex Multi-Node Workflow Integration Tests', () => {
926
- let executor: SyncWorkflowExecutor;
927
-
928
- beforeEach(() => {
929
- executor = new SyncWorkflowExecutor();
930
- });
931
-
932
- afterEach(() => {
933
- executor.clearTrace();
934
- });
935
-
936
- // ==================== Workflow Structure Tests ====================
937
-
938
- describe('Data Processing Pipeline Workflow', () => {
939
- const workflow = createDataProcessingWorkflow();
940
-
941
- it('should have 12 nodes', () => {
942
- expect(workflow.nodes).toHaveLength(12);
943
- });
944
-
945
- it('should have correct node IDs', () => {
946
- const nodeIds = workflow.nodes.map(n => n.id);
947
- expect(nodeIds).toContain('init');
948
- expect(nodeIds).toContain('merge-data');
949
- expect(nodeIds).toContain('finalize-results');
950
- expect(nodeIds).toContain('output');
951
- });
952
-
953
- it('should have parallel branches from init', () => {
954
- const fetchNodes = workflow.nodes.filter(n => n.depends_on && n.depends_on.includes('init'));
955
- expect(fetchNodes.length).toBe(3); // fetch-users, fetch-products, fetch-orders
956
- });
957
-
958
- it('should have diamond pattern with merge-data', () => {
959
- const mergeNode = workflow.nodes.find(n => n.id === 'merge-data')!;
960
- expect(mergeNode.depends_on).toBeDefined();
961
- expect(mergeNode.depends_on).toContain('fetch-users');
962
- expect(mergeNode.depends_on).toContain('fetch-products');
963
- expect(mergeNode.depends_on).toContain('fetch-orders');
964
- });
965
-
966
- it('should have nodes with retry configuration', () => {
967
- const retryNode = workflow.nodes.find(n => n.id === 'process-users')!;
968
- expect(retryNode.retry).toBeDefined();
969
- expect(retryNode.retry?.max_attempts).toBe(3);
970
- expect(retryNode.retry?.backoff).toBe('exponential');
971
- });
972
-
973
- it('should have nested workflow node', () => {
974
- const nestedNode = workflow.nodes.find(n => n.id === 'create-report')!;
975
- expect(nestedNode.type).toBe('nested');
976
- expect(nestedNode.config.workflow).toBe('report-generation');
977
- });
978
-
979
- it('should have correct output definitions', () => {
980
- expect(workflow.outputs).toHaveLength(3);
981
- expect(workflow.outputs?.find(o => o.name === 'final_result')?.source).toBe('output');
982
- });
983
-
984
- it('should have metadata with tags', () => {
985
- expect(workflow.metadata?.tags).toContain('data-processing');
986
- expect(workflow.metadata?.author).toBe('Roy Agent');
987
- });
988
-
989
- it('should execute in correct topological order', () => {
990
- const order = topologicalSort(workflow);
991
-
992
- // init should come before fetch-*
993
- expect(order.indexOf('init')).toBeLessThan(order.indexOf('fetch-users'));
994
- expect(order.indexOf('init')).toBeLessThan(order.indexOf('fetch-products'));
995
- expect(order.indexOf('init')).toBeLessThan(order.indexOf('fetch-orders'));
996
-
997
- // merge-data should come after all fetches
998
- expect(order.indexOf('merge-data')).toBeGreaterThan(order.indexOf('fetch-users'));
999
- expect(order.indexOf('merge-data')).toBeGreaterThan(order.indexOf('fetch-products'));
1000
- expect(order.indexOf('merge-data')).toBeGreaterThan(order.indexOf('fetch-orders'));
1001
- });
1002
-
1003
- it('should execute all nodes successfully', () => {
1004
- const results = executeWorkflowSync(workflow, { generateReport: true }, executor);
1005
-
1006
- expect(results.size).toBe(12);
1007
- expect(results.has('init')).toBe(true);
1008
- expect(results.has('merge-data')).toBe(true);
1009
- expect(results.has('output')).toBe(true);
1010
-
1011
- // Verify execution order in trace
1012
- const trace = executor.getTrace();
1013
- expect(trace.executionOrder.length).toBe(12);
1014
- expect(trace.executionOrder[0]).toBe('init');
1015
- expect(trace.executionOrder[11]).toBe('output');
1016
- });
1017
- });
1018
-
1019
- describe('Parallel Processing Workflow', () => {
1020
- const workflow = createParallelProcessingWorkflow();
1021
-
1022
- it('should have parallel branches from entry', () => {
1023
- const parallelNodes = workflow.nodes.filter(n => n.depends_on?.includes('entry'));
1024
- expect(parallelNodes.length).toBe(3); // task-a1, task-b1, task-c1
1025
- });
1026
-
1027
- it('should have correct branch structures', () => {
1028
- // Branch A: entry -> a1 -> a2 -> a3
1029
- expect(workflow.nodes.find(n => n.id === 'task-a1')?.depends_on).toContain('entry');
1030
- expect(workflow.nodes.find(n => n.id === 'task-a2')?.depends_on).toContain('task-a1');
1031
- expect(workflow.nodes.find(n => n.id === 'task-a3')?.depends_on).toContain('task-a2');
1032
-
1033
- // Branch B: entry -> b1 -> b2/b3
1034
- expect(workflow.nodes.find(n => n.id === 'task-b1')?.depends_on).toContain('entry');
1035
- expect(workflow.nodes.find(n => n.id === 'task-b2')?.depends_on).toContain('task-b1');
1036
- expect(workflow.nodes.find(n => n.id === 'task-b3')?.depends_on).toContain('task-b1');
1037
-
1038
- // Branch C: entry -> c1 -> c2
1039
- expect(workflow.nodes.find(n => n.id === 'task-c1')?.depends_on).toContain('entry');
1040
- expect(workflow.nodes.find(n => n.id === 'task-c2')?.depends_on).toContain('task-c1');
1041
- });
1042
-
1043
- it('should have retry configured on specific nodes', () => {
1044
- const retryNode = workflow.nodes.find(n => n.id === 'task-b3')!;
1045
- expect(retryNode.retry?.max_attempts).toBe(2);
1046
- expect(retryNode.retry?.backoff).toBe('fixed');
1047
-
1048
- const retryNode2 = workflow.nodes.find(n => n.id === 'task-c2')!;
1049
- expect(retryNode2.retry?.max_attempts).toBe(3);
1050
- expect(retryNode2.retry?.backoff).toBe('exponential');
1051
- });
1052
-
1053
- it('should merge at merge-ab node', () => {
1054
- const mergeNode = workflow.nodes.find(n => n.id === 'merge-ab')!;
1055
- expect(mergeNode.depends_on).toContain('task-a3');
1056
- expect(mergeNode.depends_on).toContain('task-b2');
1057
- expect(mergeNode.depends_on).toContain('task-c2');
1058
- });
1059
-
1060
- it('should execute all nodes', () => {
1061
- const results = executeWorkflowSync(workflow, {}, executor);
1062
- // entry + 3 branches * 3 nodes each (minus b3 since b2 and b3 both depend on b1) + merge + final
1063
- // 1 + 3 + 3 + 3 + 1 + 1 = 12... wait, let me count again
1064
- // entry, task-a1, task-a2, task-a3, task-b1, task-b2, task-b3, task-c1, task-c2, merge-ab, final = 11 nodes
1065
- expect(results.size).toBe(11);
1066
- });
1067
- });
1068
-
1069
- describe('Nested Workflow Composition', () => {
1070
- const workflow = createNestedWorkflow();
1071
-
1072
- it('should have multiple workflow nodes', () => {
1073
- const workflowNodes = workflow.nodes.filter(n => n.type === 'workflow');
1074
- expect(workflowNodes.length).toBe(3);
1075
- });
1076
-
1077
- it('should have hierarchical structure', () => {
1078
- const startNode = workflow.nodes.find(n => n.id === 'start')!;
1079
- expect(startNode.depends_on).toBeDefined();
1080
- expect(startNode.depends_on!.length).toBe(0);
1081
-
1082
- const subWorkflow1 = workflow.nodes.find(n => n.id === 'sub-workflow-1')!;
1083
- expect(subWorkflow1.depends_on).toBeDefined();
1084
- expect(subWorkflow1.depends_on).toContain('start');
1085
- });
1086
-
1087
- it('should execute workflow nodes', () => {
1088
- const results = executeWorkflowSync(workflow, {}, executor);
1089
- expect(results.has('sub-workflow-1')).toBe(true);
1090
- expect(results.has('final-nested')).toBe(true);
1091
-
1092
- const nestedResult = results.get('sub-workflow-1');
1093
- expect(nestedResult).toBeDefined();
1094
- expect(nestedResult.nestedWorkflow).toBe('sub-workflow-a');
1095
- });
1096
- });
1097
-
1098
- describe('Multi-Node Type Orchestration', () => {
1099
- const workflow = createMultiNodeTypeWorkflow();
1100
-
1101
- it('should have various node types', () => {
1102
- const toolNodes = workflow.nodes.filter(n => n.type === 'tool');
1103
- const agentNodes = workflow.nodes.filter(n => n.type === 'agent');
1104
- const skillNodes = workflow.nodes.filter(n => n.type === 'skill');
1105
- const workflowNodes = workflow.nodes.filter(n => n.type === 'workflow');
1106
-
1107
- expect(toolNodes.length).toBeGreaterThan(0);
1108
- expect(agentNodes.length).toBeGreaterThan(0);
1109
- expect(skillNodes.length).toBeGreaterThan(0);
1110
- expect(workflowNodes.length).toBeGreaterThan(0);
1111
- });
1112
-
1113
- it('should have correct dependency chain', () => {
1114
- const prepareData = workflow.nodes.find(n => n.id === 'prepare-data')!;
1115
- expect(prepareData.depends_on?.length).toBe(0);
1116
-
1117
- const analyzeData = workflow.nodes.find(n => n.id === 'analyze-data')!;
1118
- expect(analyzeData.depends_on).toContain('prepare-data');
1119
-
1120
- const makeDecision = workflow.nodes.find(n => n.id === 'make-decision')!;
1121
- expect(makeDecision.depends_on).toContain('validate-analysis');
1122
- });
1123
-
1124
- it('should have agent nodes with prompts', () => {
1125
- const agentNodes = workflow.nodes.filter(n => n.type === 'agent');
1126
-
1127
- for (const agentNode of agentNodes) {
1128
- expect(agentNode.config).toBeDefined();
1129
- expect((agentNode.config as any).prompt).toBeDefined();
1130
- }
1131
- });
1132
-
1133
- it('should have skill nodes with skill names', () => {
1134
- const skillNodes = workflow.nodes.filter(n => n.type === 'skill');
1135
-
1136
- for (const skillNode of skillNodes) {
1137
- expect(skillNode.config).toBeDefined();
1138
- expect((skillNode.config as any).skill).toBeDefined();
1139
- }
1140
- });
1141
-
1142
- it('should execute all node types', () => {
1143
- const results = executeWorkflowSync(workflow, {}, executor);
1144
-
1145
- // All 7 nodes should be executed
1146
- // prepare-data, analyze-data, validate-analysis, make-decision, transform-data, process-workflow, format-output
1147
- expect(results.size).toBe(7);
1148
-
1149
- // Verify tool nodes executed
1150
- expect(results.has('prepare-data')).toBe(true);
1151
- expect(results.has('format-output')).toBe(true);
1152
-
1153
- // Verify agent nodes executed
1154
- expect(results.has('analyze-data')).toBe(true);
1155
- expect(results.has('make-decision')).toBe(true);
1156
-
1157
- // Verify skill nodes executed
1158
- expect(results.has('transform-data')).toBe(true);
1159
-
1160
- // Verify workflow nodes executed
1161
- expect(results.has('process-workflow')).toBe(true);
1162
- });
1163
- });
1164
-
1165
- describe('Tool-Skill-Agent Chain', () => {
1166
- const workflow = createToolSkillAgentChain();
1167
-
1168
- it('should alternate between tool, agent, and skill nodes', () => {
1169
- const toolNodes = workflow.nodes.filter(n => n.type === 'tool');
1170
- const agentNodes = workflow.nodes.filter(n => n.type === 'agent');
1171
- const skillNodes = workflow.nodes.filter(n => n.type === 'skill');
1172
-
1173
- expect(toolNodes.length).toBe(2); // fetch and validate
1174
- expect(agentNodes.length).toBe(2); // understand and reasoning
1175
- expect(skillNodes.length).toBe(2); // process and enhance
1176
- });
1177
-
1178
- it('should maintain execution order', () => {
1179
- const results = executeWorkflowSync(workflow, {}, executor);
1180
- expect(results.size).toBe(6);
1181
-
1182
- // Verify tool → agent → skill chain
1183
- const toolResult = results.get('fetch-raw-data');
1184
- const agentResult = results.get('understand-context');
1185
- const skillResult = results.get('process-with-skill');
1186
-
1187
- expect(toolResult).toBeDefined();
1188
- expect(agentResult).toBeDefined();
1189
- expect(skillResult).toBeDefined();
1190
- });
1191
- });
1192
-
1193
- describe('Parallel Tool-Agent Branches', () => {
1194
- const workflow = createParallelToolAgentWorkflow();
1195
-
1196
- it('should have parallel tool-agent pairs', () => {
1197
- const toolNodes = workflow.nodes.filter(n => n.type === 'tool');
1198
- const agentNodes = workflow.nodes.filter(n => n.type === 'agent');
1199
-
1200
- expect(toolNodes.length).toBeGreaterThan(3); // start + 3 branch tools + output
1201
- expect(agentNodes.length).toBe(4); // 3 branch agents + 1 aggregator
1202
- });
1203
-
1204
- it('should have merge node after parallel branches', () => {
1205
- const mergeNode = workflow.nodes.find(n => n.id === 'merge-agents')!;
1206
- expect(mergeNode.type).toBe('merge');
1207
- expect(mergeNode.depends_on).toContain('agent-a');
1208
- expect(mergeNode.depends_on).toContain('agent-b');
1209
- expect(mergeNode.depends_on).toContain('agent-c');
1210
- });
1211
-
1212
- it('should execute all parallel branches', () => {
1213
- const results = executeWorkflowSync(workflow, {}, executor);
1214
- expect(results.has('tool-a')).toBe(true);
1215
- expect(results.has('agent-a')).toBe(true);
1216
- expect(results.has('tool-b')).toBe(true);
1217
- expect(results.has('agent-b')).toBe(true);
1218
- expect(results.has('merge-agents')).toBe(true);
1219
- expect(results.has('aggregate-results')).toBe(true);
1220
- });
1221
- });
1222
-
1223
- describe('Diamond Pattern Workflow', () => {
1224
- const workflow = createDiamondWorkflow();
1225
-
1226
- it('should have classic diamond structure', () => {
1227
- // A is the starting point
1228
- const aNode = workflow.nodes.find(n => n.id === 'A')!;
1229
- expect(aNode.depends_on).toBeDefined();
1230
- expect(aNode.depends_on!.length).toBe(0);
1231
-
1232
- // B1, B2, B3 all depend on A
1233
- const b1Node = workflow.nodes.find(n => n.id === 'B1')!;
1234
- const b2Node = workflow.nodes.find(n => n.id === 'B2')!;
1235
- const b3Node = workflow.nodes.find(n => n.id === 'B3')!;
1236
- expect(b1Node.depends_on).toContain('A');
1237
- expect(b2Node.depends_on).toContain('A');
1238
- expect(b3Node.depends_on).toContain('A');
1239
-
1240
- // C depends on all B nodes
1241
- const cNode = workflow.nodes.find(n => n.id === 'C')!;
1242
- expect(cNode.depends_on).toBeDefined();
1243
- expect(cNode.depends_on).toContain('B1');
1244
- expect(cNode.depends_on).toContain('B2');
1245
- expect(cNode.depends_on).toContain('B3');
1246
-
1247
- // D depends on C
1248
- const dNode = workflow.nodes.find(n => n.id === 'D')!;
1249
- expect(dNode.depends_on).toContain('C');
1250
- });
1251
-
1252
- it('should have 6 nodes total', () => {
1253
- expect(workflow.nodes).toHaveLength(6);
1254
- });
1255
-
1256
- it('should execute diamond pattern correctly', () => {
1257
- const results = executeWorkflowSync(workflow, { test: 'data' }, executor);
1258
-
1259
- // All nodes should be executed
1260
- expect(results.has('A')).toBe(true);
1261
- expect(results.has('B1')).toBe(true);
1262
- expect(results.has('B2')).toBe(true);
1263
- expect(results.has('B3')).toBe(true);
1264
- expect(results.has('C')).toBe(true);
1265
- expect(results.has('D')).toBe(true);
1266
- });
1267
-
1268
- it('should merge results at C node', () => {
1269
- const results = executeWorkflowSync(workflow, {}, executor);
1270
- const cResult = results.get('C');
1271
- expect(cResult).toBeDefined();
1272
- });
1273
- });
1274
-
1275
- describe('Conditional Execution Workflow', () => {
1276
- const workflow = createConditionalWorkflow();
1277
-
1278
- it('should have conditional branching', () => {
1279
- const checkNode = workflow.nodes.find(n => n.id === 'check-mode')!;
1280
- expect(checkNode.type).toBe('condition');
1281
-
1282
- const modeA1 = workflow.nodes.find(n => n.id === 'mode-a-1')!;
1283
- const modeB1 = workflow.nodes.find(n => n.id === 'mode-b-1')!;
1284
- expect(modeA1.depends_on).toContain('check-mode');
1285
- expect(modeB1.depends_on).toContain('check-mode');
1286
- });
1287
-
1288
- it('should merge conditional paths', () => {
1289
- const mergeNode = workflow.nodes.find(n => n.id === 'merge-cond')!;
1290
- expect(mergeNode.depends_on).toBeDefined();
1291
- expect(mergeNode.depends_on).toContain('mode-a-2');
1292
- expect(mergeNode.depends_on).toContain('mode-b-2');
1293
- });
1294
-
1295
- it('should execute conditional nodes', () => {
1296
- const results = executeWorkflowSync(workflow, { mode: 'A' }, executor);
1297
-
1298
- expect(results.has('check-mode')).toBe(true);
1299
- const checkResult = results.get('check-mode');
1300
- expect(checkResult).toBeDefined();
1301
- expect(checkResult.conditionMet).toBe(true);
1302
- });
1303
- });
1304
-
1305
- describe('Template Variable Workflow', () => {
1306
- const workflow = createTemplateVariableWorkflow();
1307
-
1308
- it('should have template variables in node configs', () => {
1309
- const initNode = workflow.nodes.find(n => n.id === 'init')!;
1310
- expect(initNode.config.message).toContain('{{input.greeting}}');
1311
-
1312
- const processNode = workflow.nodes.find(n => n.id === 'process')!;
1313
- expect(processNode.config.expression).toContain('{{init.result}}');
1314
- expect(processNode.config.expression).toContain('{{input.value}}');
1315
- });
1316
-
1317
- it('should have condition with template variables', () => {
1318
- const validateNode = workflow.nodes.find(n => n.id === 'validate')!;
1319
- expect(validateNode.config.condition).toContain('{{process.result}}');
1320
- expect(validateNode.config.condition).toContain('{{input.threshold}}');
1321
- });
1322
- });
1323
-
1324
- describe('Complex Dependency Graph Workflow', () => {
1325
- const workflow = createComplexDependencyWorkflow();
1326
-
1327
- it('should have multiple entry points', () => {
1328
- const entry1 = workflow.nodes.find(n => n.id === 'entry-1')!;
1329
- const entry2 = workflow.nodes.find(n => n.id === 'entry-2')!;
1330
- expect(entry1.depends_on).toBeDefined();
1331
- expect(entry1.depends_on!.length).toBe(0);
1332
- expect(entry2.depends_on).toBeDefined();
1333
- expect(entry2.depends_on!.length).toBe(0);
1334
- });
1335
-
1336
- it('should have cross-branch dependencies', () => {
1337
- const crossDep = workflow.nodes.find(n => n.id === 'cross-dep')!;
1338
- expect(crossDep.depends_on).toBeDefined();
1339
- expect(crossDep.depends_on).toContain('branch-1a');
1340
- expect(crossDep.depends_on).toContain('branch-2a');
1341
- });
1342
-
1343
- it('should handle complex merge patterns', () => {
1344
- const mergeNode = workflow.nodes.find(n => n.id === 'merge-1')!;
1345
- expect(mergeNode.depends_on).toBeDefined();
1346
- expect(mergeNode.depends_on).toHaveLength(3);
1347
- });
1348
-
1349
- it('should execute complex workflow correctly', () => {
1350
- const results = executeWorkflowSync(workflow, {}, executor);
1351
- // entry-1, entry-2, branch-1a, branch-1b, branch-2a, branch-2b, cross-dep, merge-1, final = 9 nodes
1352
- expect(results.size).toBe(9);
1353
- });
1354
- });
1355
-
1356
- // ==================== Retry Logic Tests ====================
1357
-
1358
- describe('Retry Configuration', () => {
1359
- it('should configure retry with exponential backoff', () => {
1360
- const workflow = createDataProcessingWorkflow();
1361
- const retryNode = workflow.nodes.find(n => n.id === 'process-users')!;
1362
-
1363
- expect(retryNode.retry).toBeDefined();
1364
- expect(retryNode.retry?.max_attempts).toBe(3);
1365
- expect(retryNode.retry?.backoff).toBe('exponential');
1366
- expect(retryNode.retry?.initial_delay).toBe(1000);
1367
- });
1368
-
1369
- it('should configure retry with fixed backoff', () => {
1370
- const workflow = createParallelProcessingWorkflow();
1371
- const retryNode = workflow.nodes.find(n => n.id === 'task-b3')!;
1372
-
1373
- expect(retryNode.retry).toBeDefined();
1374
- expect(retryNode.retry?.backoff).toBe('fixed');
1375
- expect(retryNode.retry?.initial_delay).toBe(500);
1376
- });
1377
-
1378
- it('should have retry node in diamond workflow', () => {
1379
- const workflow = createDiamondWorkflow();
1380
- const retryNode = workflow.nodes.find(n => n.id === 'C')!;
1381
-
1382
- expect(retryNode.retry).toBeDefined();
1383
- expect(retryNode.retry?.max_attempts).toBe(2);
1384
- });
1385
- });
1386
-
1387
- // ==================== Workflow Validation Tests ====================
1388
-
1389
- describe('Workflow Validation', () => {
1390
- it('should validate workflow with all required fields', () => {
1391
- const workflow = createDataProcessingWorkflow();
1392
-
1393
- // Should have name
1394
- expect(workflow.name).toBeTruthy();
1395
-
1396
- // Should have at least one node
1397
- expect(workflow.nodes.length).toBeGreaterThan(0);
1398
-
1399
- // All nodes should have id and type
1400
- for (const node of workflow.nodes) {
1401
- expect(node.id).toBeTruthy();
1402
- expect(node.type).toBeTruthy();
1403
- }
1404
- });
1405
-
1406
- it('should have valid dependency references', () => {
1407
- const workflow = createDataProcessingWorkflow();
1408
- const nodeIds = new Set(workflow.nodes.map(n => n.id));
1409
-
1410
- for (const node of workflow.nodes) {
1411
- if (node.depends_on) {
1412
- for (const depId of node.depends_on) {
1413
- expect(nodeIds.has(depId)).toBe(true);
1414
- }
1415
- }
1416
- }
1417
- });
1418
-
1419
- it('should detect circular dependencies', () => {
1420
- const circularWorkflow: WorkflowDefinition = {
1421
- name: 'circular',
1422
- nodes: [
1423
- { id: 'a', type: 'tool', config: {}, depends_on: ['c'] },
1424
- { id: 'b', type: 'tool', config: {}, depends_on: ['a'] },
1425
- { id: 'c', type: 'tool', config: {}, depends_on: ['b'] },
1426
- ],
1427
- };
1428
-
1429
- expect(() => topologicalSort(circularWorkflow)).toThrow();
1430
- });
1431
- });
1432
-
1433
- // ==================== Schema Validation Tests ====================
1434
-
1435
- describe('Schema Validation', () => {
1436
- it('should validate NodeDefinition schema', () => {
1437
- const NodeDefinitionSchema = z.object({
1438
- id: z.string().min(1),
1439
- type: z.string().min(1),
1440
- name: z.string().optional(),
1441
- config: z.record(z.string(), z.unknown()).optional(),
1442
- depends_on: z.array(z.string()).optional(),
1443
- condition: z.string().optional(),
1444
- retry: z.object({
1445
- max_attempts: z.number().min(1),
1446
- backoff: z.enum(['fixed', 'exponential']),
1447
- initial_delay: z.number().min(0),
1448
- }).optional(),
1449
- timeout: z.number().optional(),
1450
- });
1451
-
1452
- const validNode = {
1453
- id: 'test-node',
1454
- type: 'tool',
1455
- name: 'Test Node',
1456
- config: { tool: 'test' },
1457
- depends_on: ['dep1'],
1458
- retry: { max_attempts: 3, backoff: 'exponential', initial_delay: 1000 },
1459
- };
1460
-
1461
- const result = NodeDefinitionSchema.safeParse(validNode);
1462
- expect(result.success).toBe(true);
1463
- });
1464
-
1465
- it('should reject invalid retry config', () => {
1466
- const RetryConfigSchema = z.object({
1467
- max_attempts: z.number().min(1),
1468
- backoff: z.enum(['fixed', 'exponential']),
1469
- initial_delay: z.number().min(0),
1470
- });
1471
-
1472
- const invalidConfig = { max_attempts: 0, backoff: 'invalid', initial_delay: -100 };
1473
- const result = RetryConfigSchema.safeParse(invalidConfig);
1474
- expect(result.success).toBe(false);
1475
- });
1476
-
1477
- it('should validate WorkflowDefinition schema', () => {
1478
- const WorkflowDefinitionSchema = z.object({
1479
- name: z.string().min(1),
1480
- version: z.string().default('1.0'),
1481
- description: z.string().optional(),
1482
- nodes: z.array(z.object({
1483
- id: z.string().min(1),
1484
- type: z.string().min(1),
1485
- })).min(1),
1486
- entry: z.union([z.string(), z.array(z.string())]).default('__default_entry__'),
1487
- });
1488
-
1489
- const validWorkflow = {
1490
- name: 'test-workflow',
1491
- nodes: [{ id: 'n1', type: 'tool' }],
1492
- };
1493
-
1494
- const result = WorkflowDefinitionSchema.safeParse(validWorkflow);
1495
- expect(result.success).toBe(true);
1496
- expect(result.data?.version).toBe('1.0');
1497
- });
1498
- });
1499
- });
1500
-
1501
- // ==================== Topological Sort Tests ====================
1502
-
1503
- describe('Topological Sort', () => {
1504
- it('should sort nodes in dependency order', () => {
1505
- const workflow = createDiamondWorkflow();
1506
- const order = topologicalSort(workflow);
1507
-
1508
- // A should come before B1, B2, B3
1509
- expect(order.indexOf('A')).toBeLessThan(order.indexOf('B1'));
1510
- expect(order.indexOf('A')).toBeLessThan(order.indexOf('B2'));
1511
- expect(order.indexOf('A')).toBeLessThan(order.indexOf('B3'));
1512
-
1513
- // B* should come before C
1514
- expect(order.indexOf('B1')).toBeLessThan(order.indexOf('C'));
1515
- expect(order.indexOf('B2')).toBeLessThan(order.indexOf('C'));
1516
- expect(order.indexOf('B3')).toBeLessThan(order.indexOf('C'));
1517
-
1518
- // C should come before D
1519
- expect(order.indexOf('C')).toBeLessThan(order.indexOf('D'));
1520
- });
1521
-
1522
- it('should handle parallel branches', () => {
1523
- const workflow = createParallelProcessingWorkflow();
1524
- const order = topologicalSort(workflow);
1525
-
1526
- // Entry should be first
1527
- expect(order[0]).toBe('entry');
1528
-
1529
- // All task-* should come after entry
1530
- const taskNodes = order.filter(id => id.startsWith('task-'));
1531
- expect(taskNodes.length).toBeGreaterThan(0);
1532
-
1533
- // Verify dependencies are maintained
1534
- for (const node of workflow.nodes) {
1535
- const nodeIndex = order.indexOf(node.id);
1536
- for (const depId of node.depends_on || []) {
1537
- expect(order.indexOf(depId)).toBeLessThan(nodeIndex);
1538
- }
1539
- }
1540
- });
1541
-
1542
- it('should throw on circular dependency', () => {
1543
- const circularWorkflow: WorkflowDefinition = {
1544
- name: 'circular',
1545
- nodes: [
1546
- { id: 'a', type: 'tool', config: {}, depends_on: ['b'] },
1547
- { id: 'b', type: 'tool', config: {}, depends_on: ['a'] },
1548
- ],
1549
- };
1550
-
1551
- expect(() => topologicalSort(circularWorkflow)).toThrow('Circular dependency detected');
1552
- });
1553
- });
1554
-
1555
- // ==================== Performance Tests ====================
1556
-
1557
- describe('Workflow Performance', () => {
1558
- it('should handle large parallel execution', () => {
1559
- const nodes: NodeDefinition[] = [
1560
- { id: 'start', type: 'tool', config: { tool: 'echo' } },
1561
- ];
1562
-
1563
- // Create 20 parallel nodes
1564
- for (let i = 0; i < 20; i++) {
1565
- nodes.push({
1566
- id: `parallel-${i}`,
1567
- type: 'tool',
1568
- config: { tool: 'calculator' },
1569
- depends_on: ['start'],
1570
- });
1571
- }
1572
-
1573
- // Merge all parallel results
1574
- nodes.push({
1575
- id: 'merge',
1576
- type: 'merge',
1577
- config: { strategy: 'collect' },
1578
- depends_on: nodes.slice(1).map(n => n.id),
1579
- });
1580
-
1581
- const workflow: WorkflowDefinition = {
1582
- name: 'large-parallel',
1583
- nodes,
1584
- };
1585
-
1586
- const order = topologicalSort(workflow);
1587
- expect(order.length).toBe(22); // 1 start + 20 parallel + 1 merge
1588
- });
1589
-
1590
- it('should handle deep dependency chains', () => {
1591
- const nodes: NodeDefinition[] = [];
1592
-
1593
- // Create 50-node deep chain
1594
- for (let i = 0; i < 50; i++) {
1595
- nodes.push({
1596
- id: `node-${i}`,
1597
- type: 'tool',
1598
- config: { tool: 'echo' },
1599
- depends_on: i > 0 ? [`node-${i - 1}`] : [],
1600
- });
1601
- }
1602
-
1603
- const workflow: WorkflowDefinition = {
1604
- name: 'deep-chain',
1605
- nodes,
1606
- };
1607
-
1608
- const order = topologicalSort(workflow);
1609
-
1610
- // Should maintain order
1611
- for (let i = 1; i < order.length; i++) {
1612
- expect(order.indexOf(`node-${i - 1}`)).toBeLessThan(order.indexOf(`node-${i}`));
1613
- }
1614
- });
1615
-
1616
- it('should execute large workflow efficiently', () => {
1617
- const nodes: NodeDefinition[] = [
1618
- { id: 'start', type: 'tool', config: { tool: 'echo' } },
1619
- ];
1620
-
1621
- for (let i = 0; i < 10; i++) {
1622
- nodes.push({
1623
- id: `node-${i}`,
1624
- type: 'tool',
1625
- config: { tool: 'calculator' },
1626
- depends_on: i === 0 ? ['start'] : [`node-${i - 1}`],
1627
- });
1628
- }
1629
-
1630
- const workflow: WorkflowDefinition = {
1631
- name: 'large-chain',
1632
- nodes,
1633
- };
1634
-
1635
- const executor = new SyncWorkflowExecutor();
1636
- const start = Date.now();
1637
- const results = executeWorkflowSync(workflow, {}, executor);
1638
- const duration = Date.now() - start;
1639
-
1640
- expect(results.size).toBe(11);
1641
- expect(duration).toBeLessThan(100); // Should complete in under 100ms
1642
- });
1643
- });
1644
-
1645
- // ==================== Edge Cases ====================
1646
-
1647
- describe('Workflow Edge Cases', () => {
1648
- it('should handle single node workflow', () => {
1649
- const workflow: WorkflowDefinition = {
1650
- name: 'single-node',
1651
- nodes: [{ id: 'only', type: 'tool', config: {} }],
1652
- };
1653
-
1654
- const order = topologicalSort(workflow);
1655
- expect(order).toEqual(['only']);
1656
-
1657
- const executor = new SyncWorkflowExecutor();
1658
- const results = executeWorkflowSync(workflow, {}, executor);
1659
- expect(results.size).toBe(1);
1660
- expect(results.get('only')).toBeDefined();
1661
- });
1662
-
1663
- it('should handle nodes with multiple dependencies', () => {
1664
- const workflow: WorkflowDefinition = {
1665
- name: 'multi-dep',
1666
- nodes: [
1667
- { id: 'a', type: 'tool', config: {} },
1668
- { id: 'b', type: 'tool', config: {} },
1669
- { id: 'c', type: 'tool', config: {} },
1670
- { id: 'd', type: 'tool', config: {}, depends_on: ['a', 'b', 'c'] },
1671
- ],
1672
- };
1673
-
1674
- const order = topologicalSort(workflow);
1675
- const dIndex = order.indexOf('d');
1676
- expect(order.indexOf('a')).toBeLessThan(dIndex);
1677
- expect(order.indexOf('b')).toBeLessThan(dIndex);
1678
- expect(order.indexOf('c')).toBeLessThan(dIndex);
1679
- });
1680
-
1681
- it('should handle multiple entry points', () => {
1682
- const workflow: WorkflowDefinition = {
1683
- name: 'multi-entry',
1684
- entry: ['a', 'b'],
1685
- nodes: [
1686
- { id: 'a', type: 'tool', config: {} },
1687
- { id: 'b', type: 'tool', config: {} },
1688
- { id: 'c', type: 'tool', config: {}, depends_on: ['a', 'b'] },
1689
- ],
1690
- };
1691
-
1692
- const order = topologicalSort(workflow);
1693
- expect(order.includes('a')).toBe(true);
1694
- expect(order.includes('b')).toBe(true);
1695
- expect(order.indexOf('c')).toBeGreaterThan(order.indexOf('a'));
1696
- expect(order.indexOf('c')).toBeGreaterThan(order.indexOf('b'));
1697
- });
1698
-
1699
- it('should handle optional fields in nodes', () => {
1700
- const minimalNode = {
1701
- id: 'minimal',
1702
- type: 'tool',
1703
- };
1704
-
1705
- const NodeDefinitionSchema = z.object({
1706
- id: z.string().min(1),
1707
- type: z.string().min(1),
1708
- name: z.string().optional(),
1709
- config: z.record(z.string(), z.unknown()).optional(),
1710
- depends_on: z.array(z.string()).optional(),
1711
- });
1712
-
1713
- const result = NodeDefinitionSchema.safeParse(minimalNode);
1714
- expect(result.success).toBe(true);
1715
- });
1716
-
1717
- it('should handle empty depends_on array', () => {
1718
- const workflow: WorkflowDefinition = {
1719
- name: 'empty-deps',
1720
- nodes: [
1721
- { id: 'a', type: 'tool', config: {}, depends_on: [] },
1722
- { id: 'b', type: 'tool', config: {}, depends_on: ['a'] },
1723
- ],
1724
- };
1725
-
1726
- const order = topologicalSort(workflow);
1727
- expect(order.indexOf('a')).toBeLessThan(order.indexOf('b'));
1728
- });
1729
-
1730
- it('should handle nodes without config', () => {
1731
- const workflow: WorkflowDefinition = {
1732
- name: 'no-config',
1733
- nodes: [
1734
- { id: 'a', type: 'tool' },
1735
- { id: 'b', type: 'tool', depends_on: ['a'] },
1736
- ],
1737
- };
1738
-
1739
- const executor = new SyncWorkflowExecutor();
1740
- const results = executeWorkflowSync(workflow, {}, executor);
1741
- expect(results.get('a')).toBeDefined();
1742
- expect(results.get('b')).toBeDefined();
1743
- });
1744
- });
1745
-
1746
- // ==================== Execution Trace Tests ====================
1747
-
1748
- describe('Execution Trace', () => {
1749
- it('should track execution order', () => {
1750
- const workflow = createDiamondWorkflow();
1751
- const executor = new SyncWorkflowExecutor();
1752
-
1753
- executeWorkflowSync(workflow, {}, executor);
1754
-
1755
- const trace = executor.getTrace();
1756
- expect(trace.executionOrder).toEqual(['A', 'B1', 'B2', 'B3', 'C', 'D']);
1757
- });
1758
-
1759
- it('should track node execution status', () => {
1760
- const workflow = createDiamondWorkflow();
1761
- const executor = new SyncWorkflowExecutor();
1762
-
1763
- executeWorkflowSync(workflow, {}, executor);
1764
-
1765
- const trace = executor.getTrace();
1766
-
1767
- for (const entry of trace.nodes.values()) {
1768
- expect(entry.status).toBe('completed');
1769
- expect(entry.startTime).toBeDefined();
1770
- expect(entry.endTime).toBeDefined();
1771
- expect(entry.endTime).toBeGreaterThanOrEqual(entry.startTime);
1772
- }
1773
- });
1774
-
1775
- it('should record input for each node', () => {
1776
- const workflow = createDataProcessingWorkflow();
1777
- const executor = new SyncWorkflowExecutor();
1778
- const input = { generateReport: true, test: 'value' };
1779
-
1780
- executeWorkflowSync(workflow, input, executor);
1781
-
1782
- const trace = executor.getTrace();
1783
- const initEntry = trace.nodes.get('init');
1784
- expect(initEntry?.input).toEqual(input);
1785
- });
1786
-
1787
- it('should record output for each node', () => {
1788
- const workflow = createDiamondWorkflow();
1789
- const executor = new SyncWorkflowExecutor();
1790
-
1791
- executeWorkflowSync(workflow, {}, executor);
1792
-
1793
- const trace = executor.getTrace();
1794
-
1795
- for (const [nodeId, entry] of trace.nodes) {
1796
- expect(entry.output).toBeDefined();
1797
- }
1798
- });
1799
- });
1800
-
1801
- // ==================== Merge Node Tests ====================
1802
-
1803
- describe('Merge Node Behavior', () => {
1804
- it('should collect all dependency outputs', () => {
1805
- const workflow: WorkflowDefinition = {
1806
- name: 'merge-test',
1807
- nodes: [
1808
- { id: 'a', type: 'tool', config: { tool: 'calculator' } },
1809
- { id: 'b', type: 'tool', config: { tool: 'calculator' } },
1810
- { id: 'c', type: 'tool', config: { tool: 'calculator' } },
1811
- {
1812
- id: 'merge',
1813
- type: 'merge',
1814
- config: { strategy: 'collect' },
1815
- depends_on: ['a', 'b', 'c'],
1816
- },
1817
- ],
1818
- };
1819
-
1820
- const executor = new SyncWorkflowExecutor();
1821
- const results = executeWorkflowSync(workflow, {}, executor);
1822
-
1823
- const mergeResult = results.get('merge');
1824
- expect(mergeResult).toBeDefined();
1825
- expect(mergeResult.merged).toBe(true);
1826
- expect(mergeResult.inputs).toBeDefined();
1827
- expect(mergeResult.inputs.a).toBeDefined();
1828
- expect(mergeResult.inputs.b).toBeDefined();
1829
- expect(mergeResult.inputs.c).toBeDefined();
1830
- });
1831
-
1832
- it('should handle merge with different strategies', () => {
1833
- const mergeStrategies = ['collect', 'merge', 'concat', 'zip'];
1834
-
1835
- for (const strategy of mergeStrategies) {
1836
- const workflow: WorkflowDefinition = {
1837
- name: `merge-${strategy}`,
1838
- nodes: [
1839
- { id: 'a', type: 'tool', config: { tool: 'calculator' } },
1840
- { id: 'b', type: 'tool', config: { tool: 'calculator' } },
1841
- {
1842
- id: 'merge',
1843
- type: 'merge',
1844
- config: { strategy },
1845
- depends_on: ['a', 'b'],
1846
- },
1847
- ],
1848
- };
1849
-
1850
- const executor = new SyncWorkflowExecutor();
1851
- const results = executeWorkflowSync(workflow, {}, executor);
1852
- const mergeResult = results.get('merge');
1853
- expect(mergeResult).toBeDefined();
1854
- expect(mergeResult.mergeStrategy).toBe(strategy);
1855
- }
1856
- });
1857
- });
1858
-
1859
- // ==================== Condition Node Tests ====================
1860
-
1861
- describe('Condition Node Behavior', () => {
1862
- it('should evaluate true condition', () => {
1863
- const workflow: WorkflowDefinition = {
1864
- name: 'condition-test',
1865
- nodes: [
1866
- { id: 'check', type: 'condition', config: { condition: 'true' } },
1867
- { id: 'output', type: 'tool', config: { tool: 'echo' }, depends_on: ['check'] },
1868
- ],
1869
- };
1870
-
1871
- const executor = new SyncWorkflowExecutor();
1872
- const results = executeWorkflowSync(workflow, {}, executor);
1873
-
1874
- const checkResult = results.get('check');
1875
- expect(checkResult).toBeDefined();
1876
- expect(checkResult.conditionMet).toBe(true);
1877
- });
1878
-
1879
- it('should evaluate template condition', () => {
1880
- const workflow: WorkflowDefinition = {
1881
- name: 'condition-template',
1882
- nodes: [
1883
- {
1884
- id: 'check',
1885
- type: 'condition',
1886
- config: { condition: '{{merge-data.valid}}' },
1887
- },
1888
- { id: 'output', type: 'tool', config: { tool: 'echo' }, depends_on: ['check'] },
1889
- ],
1890
- };
1891
-
1892
- const executor = new SyncWorkflowExecutor();
1893
- const results = executeWorkflowSync(workflow, {}, executor);
1894
-
1895
- const checkResult = results.get('check');
1896
- expect(checkResult).toBeDefined();
1897
- expect(checkResult.conditionMet).toBe(true); // Template conditions default to true
1898
- expect(checkResult.evaluatedCondition).toBe('{{merge-data.valid}}');
1899
- });
1900
- });