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

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,1422 +0,0 @@
1
- /**
2
- * @fileoverview WorkflowEngine - Session-based workflow execution engine
3
- *
4
- * 基于 Session 实现 run/resume 机制:
5
- * - Session 直接作为 workflow run 的实例
6
- * - 使用 workflow.node.* 消息记录节点执行状态
7
- * - resume 时从 Session 消息恢复状态
8
- *
9
- * @version v2.0
10
- */
11
-
12
- import { EventEmitter } from 'events';
13
- import type {
14
- WorkflowDefinition,
15
- Workflow,
16
- RunOptions,
17
- RunResult,
18
- RunStatus,
19
- NodeExecutionContext,
20
- } from '../types/index';
21
- import { createWorkflowEvent, WorkflowEvent } from '../types/event';
22
- import { createNodeInterruptEvent, AskUserError } from '../types/workflow-hil';
23
- import { DAGManager } from './dag-manager';
24
- import { EventBus } from './event-bus';
25
- import { Scheduler } from './scheduler';
26
- import { Executor, ExecutorOptions } from './executor';
27
- import { NodeRegistry } from './node-registry';
28
- import type { SessionComponent } from '../../session/session-component';
29
- import type { WorkflowSessionMetadata, AgentSessionRef } from '../types/workflow-message';
30
- import type { Environment } from '../../interface';
31
- import { createLogger } from '../../log-trace/logger';
32
- import { TracedAs } from '../../log-trace/decorator';
33
-
34
- // Logger
35
- const logger = createLogger("workflow:engine");
36
-
37
- // ============================================================================
38
- // Type Definitions
39
- // ============================================================================
40
-
41
- /**
42
- * Agent Session Recovery Info
43
- */
44
- export interface AgentSessionRecoveryInfo {
45
- nodeId: string;
46
- agentSessionId: string;
47
- }
48
-
49
- /**
50
- * Options for creating WorkflowEngine via static factory
51
- */
52
- export interface WorkflowEngineCreateOptions {
53
- env?: {
54
- getComponent<T>(name: string): T | undefined;
55
- };
56
- toolRegistry?: any;
57
- skillRegistry?: any;
58
- workflowRunner?: any;
59
- sessionComponent?: SessionComponent;
60
- /** Repository for loading workflow definitions during resume */
61
- workflowRepository?: WorkflowRepository;
62
- }
63
-
64
- /**
65
- * Workflow Repository interface (abstraction)
66
- */
67
- export interface WorkflowRepository {
68
- getById(id: string): Workflow | null;
69
- getByName(name: string): Workflow | null;
70
- }
71
-
72
- /**
73
- * Active Session State
74
- * 内存中追踪正在运行的 workflow session
75
- */
76
- interface ActiveSessionState {
77
- sessionId: string;
78
- workflowId: string;
79
- workflowName: string;
80
- status: RunStatus;
81
- startedAt: Date;
82
- eventBus: EventBus;
83
- dagManager: DAGManager;
84
- scheduler: Scheduler;
85
- executor: Executor;
86
- nodeRegistry: NodeRegistry;
87
- nodeOutputs: Map<string, any>;
88
- config: WorkflowEngineConfig;
89
- abortController: AbortController;
90
- completedPromise: Promise<RunResult>;
91
- resolveCompleted: (result: RunResult) => void;
92
- rejectCompleted: (error: Error) => void;
93
- // LLM history for agent nodes
94
- workflowHistory: Array<{
95
- role: 'user' | 'assistant' | 'tool';
96
- content: string;
97
- toolCallId?: string;
98
- toolName?: string;
99
- nodeId?: string;
100
- }>;
101
- // Agent sessions reference
102
- agentSessions: Map<string, string>; // nodeId -> agentSessionId
103
- }
104
-
105
- /**
106
- * WorkflowEngine Configuration
107
- */
108
- export interface WorkflowEngineConfig {
109
- parallelLimit: number | null;
110
- timeout: number | null;
111
- debug: boolean;
112
- }
113
-
114
- /**
115
- * Runtime State from Session Messages
116
- * 从 Session 消息恢复的状态
117
- */
118
- interface RuntimeState {
119
- nodeOutputs: Map<string, unknown>;
120
- pendingNodeId: string | null;
121
- waitingForUser: boolean;
122
- agentSessionId?: string;
123
- lastInterruptMessage?: any;
124
- }
125
-
126
- // ============================================================================
127
- // WorkflowEngine Class
128
- // ============================================================================
129
-
130
- /**
131
- * WorkflowEngine
132
- *
133
- * Session-based workflow execution engine.
134
- * Uses Session to manage workflow run state and node execution history.
135
- */
136
- export class WorkflowEngine extends EventEmitter {
137
- /** 活跃的 session 状态,key 为 sessionId */
138
- private activeSessions: Map<string, ActiveSessionState> = new Map();
139
-
140
- /** Session ID 计数器 */
141
- private sessionIdCounter = 0;
142
-
143
- /** Session component */
144
- private sessionComponent?: SessionComponent;
145
-
146
- /** Node registry */
147
- private nodeRegistry: NodeRegistry;
148
-
149
- /** Workflow repository for loading definitions during resume */
150
- private workflowRepository?: WorkflowRepository;
151
-
152
- /**
153
- * Create a new WorkflowEngine
154
- */
155
- constructor(
156
- nodeRegistry: NodeRegistry,
157
- sessionComponent?: SessionComponent,
158
- workflowRepository?: WorkflowRepository
159
- ) {
160
- super();
161
- this.nodeRegistry = nodeRegistry;
162
- this.sessionComponent = sessionComponent;
163
- this.workflowRepository = workflowRepository;
164
- }
165
-
166
- /**
167
- * Create a WorkflowEngine with automatic AgentComponent integration
168
- */
169
- static async create(options: WorkflowEngineCreateOptions): Promise<WorkflowEngine> {
170
- const agentComponent = options.env?.getComponent<any>('agent');
171
- const sessionComponent = options.sessionComponent;
172
-
173
- const nodeRegistry = new NodeRegistry({
174
- toolRegistry: options.toolRegistry,
175
- skillRegistry: options.skillRegistry,
176
- agentComponent,
177
- workflowRunner: options.workflowRunner,
178
- sessionComponent,
179
- });
180
-
181
- return new WorkflowEngine(nodeRegistry, sessionComponent, options.workflowRepository);
182
- }
183
-
184
- // =========================================================================
185
- // Session ID Utilities
186
- // =========================================================================
187
-
188
- /**
189
- * Generate a unique run ID
190
- */
191
- private generateRunId(): string {
192
- const random = Math.random().toString(36).substring(2, 8);
193
- return `run_${Date.now()}_${++this.sessionIdCounter}_${random}`;
194
- }
195
-
196
- /**
197
- * Extract runId from sessionId
198
- * sessionId format: workflow_{runId}
199
- */
200
- private getRunIdFromSessionId(sessionId: string): string {
201
- return sessionId.replace(/^workflow_/, '');
202
- }
203
-
204
- /**
205
- * Get session ID for a run
206
- */
207
- getSessionId(runId: string): string {
208
- return `workflow_${runId}`;
209
- }
210
-
211
- // =========================================================================
212
- // Public API: Session Management
213
- // =========================================================================
214
-
215
- /**
216
- * Create a new workflow session
217
- *
218
- * @param workflow - Workflow definition or entity
219
- * @param options - Optional configuration
220
- * @returns Promise resolving to sessionId (format: workflow_{runId})
221
- */
222
- @TracedAs("workflow.engine.createSession", { recordParams: true, recordResult: true, log: true })
223
- async createSession(
224
- workflow: Workflow | WorkflowDefinition,
225
- options?: RunOptions
226
- ): Promise<string> {
227
- const definition = 'definition' in workflow
228
- ? workflow.definition
229
- : workflow;
230
-
231
- const workflowId = 'id' in workflow
232
- ? workflow.id
233
- : `inline_${definition.name}`;
234
-
235
- const workflowName = definition.name;
236
-
237
- // Generate run ID and session ID
238
- const runId = this.generateRunId();
239
- const sessionId = this.getSessionId(runId);
240
-
241
- // Create Session
242
- const metadata: WorkflowSessionMetadata = {
243
- type: 'workflow',
244
- workflowId,
245
- workflowName,
246
- workflowVersion: definition.version,
247
- status: 'running',
248
- input: options?.input,
249
- agentSessions: [],
250
- };
251
-
252
- if (this.sessionComponent) {
253
- await this.sessionComponent.create({
254
- id: sessionId,
255
- title: `Workflow: ${workflowName} (${runId})`,
256
- metadata,
257
- });
258
- }
259
-
260
- logger.info(`[WorkflowEngine] Created session: ${sessionId}`);
261
-
262
- return sessionId;
263
- }
264
-
265
- /**
266
- * Run a workflow by sessionId (unified entry point)
267
- *
268
- * This method supports both:
269
- * 1. First run: session doesn't exist → create session, start from entry node
270
- * 2. Resume: session exists with messages → infer next node from messages
271
- *
272
- * @param sessionId - Session ID (format: workflow_{runId} or any string)
273
- * @param options - Optional configuration
274
- * @returns Promise resolving to RunResult
275
- */
276
- @TracedAs("workflow.engine.run", { recordParams: true, recordResult: true, log: true })
277
- async run(
278
- sessionId: string,
279
- options?: RunOptions & { workflowId?: string }
280
- ): Promise<RunResult> {
281
- // 1. Try to get existing session
282
- let session = this.sessionComponent
283
- ? await this.sessionComponent.get(sessionId)
284
- : null;
285
-
286
- let workflowDef: WorkflowDefinition | null = null;
287
- let workflowId: string = '';
288
- let workflowName: string = '';
289
-
290
- if (session) {
291
- // Session exists - load workflow definition
292
- const metadata = session.metadata as WorkflowSessionMetadata;
293
- if (metadata.type === 'workflow' && metadata.workflowId) {
294
- workflowDef = this.findDefinitionForWorkflow(metadata.workflowId);
295
- workflowId = metadata.workflowId;
296
- workflowName = metadata.workflowName;
297
- }
298
- if (!workflowDef && metadata.workflowName) {
299
- workflowDef = this.findDefinitionForWorkflow(metadata.workflowName);
300
- workflowId = metadata.workflowName;
301
- workflowName = metadata.workflowName;
302
- }
303
- } else {
304
- // No session - this is a new run
305
- if (!options?.workflowId) {
306
- throw new Error('workflowId required for new session');
307
- }
308
-
309
- workflowDef = this.findDefinitionForWorkflow(options.workflowId);
310
- if (!workflowDef) {
311
- throw new Error(`Workflow not found: ${options.workflowId}`);
312
- }
313
-
314
- workflowId = options.workflowId;
315
- workflowName = workflowDef.name;
316
-
317
- // Create session
318
- if (this.sessionComponent) {
319
- const sessionMetadata: WorkflowSessionMetadata = {
320
- type: 'workflow',
321
- workflowId,
322
- workflowName,
323
- workflowVersion: workflowDef.version,
324
- status: 'running',
325
- };
326
-
327
- await this.sessionComponent.create({
328
- id: sessionId,
329
- title: `Workflow: ${workflowName}`,
330
- metadata: sessionMetadata,
331
- });
332
-
333
- session = await this.sessionComponent.get(sessionId);
334
- }
335
- }
336
-
337
- if (!session || !workflowDef) {
338
- throw new Error(`Cannot start workflow: session or workflow not found`);
339
- }
340
-
341
- // 2. Check session status
342
- const metadata = session.metadata as WorkflowSessionMetadata;
343
- if (metadata.status === 'completed') {
344
- return { runId: this.getRunIdFromSessionId(sessionId), status: 'completed' };
345
- }
346
- if (metadata.status === 'failed') {
347
- return { runId: this.getRunIdFromSessionId(sessionId), status: 'failed' };
348
- }
349
-
350
- // 3. Load messages and infer next node
351
- const messages = this.sessionComponent
352
- ? await this.sessionComponent.getMessages(sessionId)
353
- : [];
354
-
355
- // 4. Import and use session-recovery utilities
356
- const { inferNextNode } = await import('../utils/session-recovery');
357
-
358
- // Get entry node from workflow definition
359
- const entry = workflowDef.entry;
360
- const entryNode = Array.isArray(entry) ? entry[0] : entry;
361
-
362
- const resumePoint = inferNextNode(messages, {
363
- entryNode: entryNode !== '__default_entry__' ? entryNode : undefined,
364
- edges: undefined, // edges not available in WorkflowDefinition, derived at runtime
365
- });
366
-
367
- // 5. Initialize session state and execute
368
- const sessionState = await this.initializeSessionState(
369
- sessionId,
370
- workflowDef,
371
- workflowId,
372
- workflowName,
373
- options
374
- );
375
-
376
- this.activeSessions.set(sessionId, sessionState);
377
-
378
- // 6. Run with resume point
379
- return this.runWithResume(sessionId, resumePoint, options);
380
- }
381
-
382
- /**
383
- * Initialize session state
384
- */
385
- private async initializeSessionState(
386
- sessionId: string,
387
- definition: WorkflowDefinition,
388
- workflowId: string,
389
- workflowName: string,
390
- options?: RunOptions
391
- ): Promise<ActiveSessionState> {
392
- // Create DAG manager
393
- const dagManager = new DAGManager(definition);
394
-
395
- // Validate DAG
396
- const validation = dagManager.validate();
397
- if (!validation.valid) {
398
- throw new Error(`Invalid workflow: ${validation.errors.join(', ')}`);
399
- }
400
-
401
- // Get parallel limit
402
- const parallelLimit = options?.parallelLimit
403
- ?? definition.config?.parallel_limit
404
- ?? null;
405
-
406
- // Create scheduler
407
- const scheduler = new Scheduler(dagManager, { parallelLimit });
408
-
409
- // Create event bus
410
- const eventBus = new EventBus();
411
-
412
- // Create executor
413
- const executorOptions: ExecutorOptions = {
414
- globalTimeout: options?.timeout ?? definition.config?.timeout ?? null,
415
- globalRetry: definition.config?.retry ?? null,
416
- debug: options?.debug ?? definition.config?.debug ?? false,
417
- };
418
- const executor = new Executor(this.nodeRegistry, eventBus, executorOptions, this.sessionComponent);
419
-
420
- // Create engine config
421
- const config: WorkflowEngineConfig = {
422
- parallelLimit,
423
- timeout: options?.timeout ?? definition.config?.timeout ?? null,
424
- debug: options?.debug ?? definition.config?.debug ?? false,
425
- };
426
-
427
- // Create abort controller
428
- const abortController = new AbortController();
429
-
430
- // Create completion promise
431
- let resolveCompleted!: (result: RunResult) => void;
432
- let rejectCompleted!: (error: Error) => void;
433
- const completedPromise = new Promise<RunResult>((resolve, reject) => {
434
- resolveCompleted = resolve;
435
- rejectCompleted = reject;
436
- });
437
-
438
- // Create session state
439
- const sessionState: ActiveSessionState = {
440
- sessionId,
441
- workflowId,
442
- workflowName,
443
- status: 'running',
444
- startedAt: new Date(),
445
- eventBus,
446
- dagManager,
447
- scheduler,
448
- executor,
449
- nodeRegistry: this.nodeRegistry,
450
- nodeOutputs: new Map(),
451
- config,
452
- abortController,
453
- completedPromise,
454
- resolveCompleted,
455
- rejectCompleted,
456
- workflowHistory: [],
457
- agentSessions: new Map(),
458
- };
459
-
460
- // Setup event handlers
461
- this.setupEventHandlers(sessionState);
462
-
463
- return sessionState;
464
- }
465
-
466
- /**
467
- * Run with resume point
468
- */
469
- private async runWithResume(
470
- sessionId: string,
471
- resumePoint: { type: string; nodeId?: string; nodeIds?: string[]; agentSessionId?: string },
472
- options?: RunOptions & { input?: unknown }
473
- ): Promise<RunResult> {
474
- const sessionState = this.activeSessions.get(sessionId);
475
- if (!sessionState) {
476
- throw new Error(`Session not found: ${sessionId}`);
477
- }
478
-
479
- const runId = this.getRunIdFromSessionId(sessionId);
480
-
481
- // Publish workflow started event
482
- await sessionState.eventBus.publish(
483
- createWorkflowEvent('workflow.started', runId, {
484
- workflow_name: sessionState.workflowName,
485
- input: options?.input,
486
- })
487
- );
488
-
489
- // Determine what to execute based on resume point
490
- let pendingNodeId: string | undefined;
491
- let agentSessionId: string | undefined;
492
-
493
- switch (resumePoint.type) {
494
- case 'entry_node':
495
- case 'next_nodes':
496
- // Normal execution - scheduler will handle it
497
- pendingNodeId = undefined;
498
- break;
499
- case 'resume_node':
500
- case 'ask_user':
501
- // Resume specific node
502
- pendingNodeId = resumePoint.nodeId;
503
- agentSessionId = resumePoint.agentSessionId;
504
- break;
505
- }
506
-
507
- // Start scheduling
508
- // Note: userResponse is extracted from input if it's a string (for resume)
509
- const userResponse = typeof options?.input === 'string' ? options.input as string : undefined;
510
- this.scheduleAndExecute(sessionState, {
511
- input: options?.input as Record<string, any>,
512
- pendingNodeId,
513
- agentSessionId,
514
- userResponse,
515
- }).catch((error) => {
516
- logger.error(`Workflow ${sessionId} scheduling error:`, error);
517
- this.failWorkflow(sessionState, error);
518
- });
519
-
520
- // If sync mode, wait for completion
521
- if (options?.sync !== false) {
522
- return this.waitForCompletion(sessionId, options?.timeout);
523
- }
524
-
525
- return { runId, status: 'running' };
526
- }
527
-
528
- /**
529
- * Convenience method to run a workflow directly
530
- */
531
- @TracedAs("workflow.engine.runWorkflow", { recordParams: true, recordResult: true, log: true })
532
- async runWorkflow(
533
- workflow: Workflow | WorkflowDefinition,
534
- options?: RunOptions
535
- ): Promise<RunResult> {
536
- const definition = 'definition' in workflow
537
- ? workflow.definition
538
- : workflow;
539
-
540
- const workflowId = 'id' in workflow
541
- ? workflow.id
542
- : `inline_${definition.name}`;
543
-
544
- const workflowName = definition.name;
545
-
546
- // Create session
547
- const sessionId = await this.createSession(workflow, options);
548
-
549
- // Initialize session state
550
- const sessionState = await this.initializeSessionState(
551
- sessionId,
552
- definition,
553
- workflowId,
554
- workflowName,
555
- options
556
- );
557
-
558
- this.activeSessions.set(sessionId, sessionState);
559
-
560
- // Run with default entry node
561
- return this.runWithResume(sessionId, { type: 'entry_node' }, options);
562
- }
563
-
564
- /**
565
- * Pause a running workflow
566
- */
567
- @TracedAs("workflow.engine.pause", { recordParams: true, recordResult: true, log: true })
568
- async pause(sessionId: string): Promise<void> {
569
- const sessionState = this.activeSessions.get(sessionId);
570
-
571
- if (!sessionState) {
572
- throw new Error(`Session not found: ${sessionId}`);
573
- }
574
-
575
- if (sessionState.status !== 'running') {
576
- throw new Error(`Session ${sessionId} is not running (current: ${sessionState.status})`);
577
- }
578
-
579
- sessionState.status = 'paused';
580
- sessionState.abortController.abort();
581
- sessionState.executor.cancelAll();
582
-
583
- // Update session metadata
584
- if (this.sessionComponent) {
585
- const metadata = await this.getSessionMetadata(sessionId);
586
- await this.sessionComponent.update(sessionId, {
587
- metadata: { ...metadata, status: 'paused' },
588
- });
589
- }
590
-
591
- // Publish pause event
592
- await sessionState.eventBus.publish(
593
- createWorkflowEvent('workflow.paused', this.getRunIdFromSessionId(sessionId), {})
594
- );
595
-
596
- logger.info(`[WorkflowEngine] Workflow paused: ${sessionId}`);
597
- }
598
-
599
- /**
600
- * Resume a paused workflow
601
- */
602
- @TracedAs("workflow.engine.resume", { recordParams: true, recordResult: true, log: true })
603
- async resume(
604
- sessionId: string,
605
- options?: { response?: string }
606
- ): Promise<RunResult> {
607
- const sessionState = this.activeSessions.get(sessionId);
608
-
609
- if (!sessionState) {
610
- // Session not in memory, try to restore from database
611
- logger.info(`[WorkflowEngine] Session not in memory, attempting to restore from DB: ${sessionId}`);
612
- return this.resumeFromDatabase(sessionId, options);
613
- }
614
-
615
- // Write resume message
616
- if (options?.response) {
617
- await this.writeNodeResume(sessionId, options.response);
618
- }
619
-
620
- if (sessionState.status === 'running') {
621
- // Already running
622
- return { runId: this.getRunIdFromSessionId(sessionId), status: 'running' };
623
- }
624
-
625
- if (sessionState.status !== 'paused') {
626
- throw new Error(`Session ${sessionId} is not paused (current: ${sessionState.status})`);
627
- }
628
-
629
- // Resume state
630
- sessionState.status = 'running';
631
- sessionState.abortController = new AbortController();
632
-
633
- // Update session metadata
634
- if (this.sessionComponent) {
635
- const metadata = await this.getSessionMetadata(sessionId);
636
- await this.sessionComponent.update(sessionId, {
637
- metadata: { ...metadata, status: 'running' },
638
- });
639
- }
640
-
641
- // Publish resume event
642
- await sessionState.eventBus.publish(
643
- createWorkflowEvent('workflow.resumed', this.getRunIdFromSessionId(sessionId), {
644
- userResponse: options?.response,
645
- })
646
- );
647
-
648
- // Restore runtime state from messages and continue execution
649
- const runtimeState = await this.restoreRuntimeState(sessionId);
650
-
651
- this.scheduleAndExecute(sessionState, {
652
- pendingNodeId: runtimeState.pendingNodeId || undefined,
653
- agentSessionId: runtimeState.agentSessionId,
654
- userResponse: options?.response,
655
- restoredOutputs: runtimeState.nodeOutputs,
656
- });
657
-
658
- return {
659
- runId: this.getRunIdFromSessionId(sessionId),
660
- status: 'running',
661
- };
662
- }
663
-
664
- /**
665
- * Resume from database when CLI restarts
666
- */
667
- @TracedAs("workflow.engine.resumeFromDatabase", { recordParams: true, recordResult: true, log: true })
668
- private async resumeFromDatabase(
669
- sessionId: string,
670
- options?: { response?: string }
671
- ): Promise<RunResult> {
672
- // Get session
673
- const session = await this.sessionComponent?.get(sessionId);
674
- if (!session) {
675
- throw new Error(`Session not found: ${sessionId}`);
676
- }
677
-
678
- const metadata = session.metadata as WorkflowSessionMetadata;
679
- if (metadata.type !== 'workflow') {
680
- throw new Error(`Session is not a workflow session: ${sessionId}`);
681
- }
682
-
683
- if (metadata.status !== 'paused') {
684
- throw new Error(`Workflow is not paused: ${sessionId}, status: ${metadata.status}`);
685
- }
686
-
687
- // Restore runtime state from messages
688
- const runtimeState = await this.restoreRuntimeState(sessionId);
689
-
690
- if (!runtimeState.pendingNodeId) {
691
- throw new Error(`No pending node found in session: ${sessionId}`);
692
- }
693
-
694
- // Write resume message
695
- if (options?.response) {
696
- await this.writeNodeResume(sessionId, options.response);
697
- }
698
-
699
- // Update session metadata to running
700
- if (this.sessionComponent) {
701
- await this.sessionComponent.update(sessionId, {
702
- metadata: { ...metadata, status: 'running' },
703
- });
704
- }
705
-
706
- logger.info(`[WorkflowEngine] Resumed from database: ${sessionId}, pendingNode: ${runtimeState.pendingNodeId}`);
707
-
708
- // Note: Cannot fully restore without workflow definition
709
- // This is a current design limitation
710
- throw new Error(
711
- `Cannot resume from database without workflow definition. ` +
712
- `Session ${sessionId} is paused at node "${runtimeState.pendingNodeId}". ` +
713
- `Please ensure the workflow is registered before resuming.`
714
- );
715
- }
716
-
717
- /**
718
- * Restore runtime state from Session messages
719
- */
720
- @TracedAs("workflow.engine.restoreRuntimeState", { recordParams: true, recordResult: true, log: true })
721
- private async restoreRuntimeState(sessionId: string): Promise<RuntimeState> {
722
- const messages = this.sessionComponent
723
- ? await this.sessionComponent.getMessages(sessionId)
724
- : [];
725
-
726
- const nodeOutputs = new Map<string, unknown>();
727
- let lastCallNodeId: string | null = null;
728
- let lastInterruptMessage: any = null;
729
- let agentSessionId: string | undefined;
730
-
731
- for (const msg of messages) {
732
- const msgMetadata = msg.metadata as any;
733
-
734
- if (msgMetadata?.type === 'workflow.node.call') {
735
- lastCallNodeId = msgMetadata.workflowNodeId;
736
- } else if (msgMetadata?.type === 'workflow.node.result') {
737
- if (lastCallNodeId) {
738
- try {
739
- nodeOutputs.set(lastCallNodeId, JSON.parse(msg.content));
740
- } catch {
741
- nodeOutputs.set(lastCallNodeId, msg.content);
742
- }
743
- }
744
- lastCallNodeId = null;
745
- } else if (msgMetadata?.type === 'workflow.node.interrupt') {
746
- lastInterruptMessage = msg;
747
- lastCallNodeId = msgMetadata.workflowNodeId;
748
- agentSessionId = msgMetadata.agentSessionId;
749
- }
750
- }
751
-
752
- return {
753
- nodeOutputs,
754
- pendingNodeId: lastCallNodeId,
755
- waitingForUser: !!lastInterruptMessage,
756
- agentSessionId,
757
- lastInterruptMessage,
758
- };
759
- }
760
-
761
- /**
762
- * Write node resume message
763
- */
764
- @TracedAs("workflow.engine.writeNodeResume", { recordParams: true, recordResult: true, log: true })
765
- private async writeNodeResume(sessionId: string, response: string): Promise<void> {
766
- if (this.sessionComponent) {
767
- await this.sessionComponent.addMessage(sessionId, {
768
- role: 'workflow.node.resume',
769
- content: response,
770
- metadata: {
771
- type: 'workflow.node.resume',
772
- response,
773
- timestamp: Date.now(),
774
- },
775
- });
776
- }
777
- }
778
-
779
- /**
780
- * Stop a running or paused workflow
781
- */
782
- @TracedAs("workflow.engine.stop", { recordParams: true, recordResult: true, log: true })
783
- async stop(sessionId: string, reason?: string): Promise<void> {
784
- const sessionState = this.activeSessions.get(sessionId);
785
-
786
- if (!sessionState) {
787
- // Not in memory, try to update database
788
- if (this.sessionComponent) {
789
- await this.sessionComponent.update(sessionId, {
790
- metadata: { status: 'stopped' } as any,
791
- });
792
- }
793
- return;
794
- }
795
-
796
- sessionState.status = 'stopped';
797
- sessionState.abortController.abort();
798
- sessionState.executor.cancelAll();
799
-
800
- // Update session metadata
801
- if (this.sessionComponent) {
802
- const metadata = await this.getSessionMetadata(sessionId);
803
- await this.sessionComponent.update(sessionId, {
804
- metadata: { ...metadata, status: 'stopped' },
805
- });
806
- }
807
-
808
- // Publish stop event
809
- await sessionState.eventBus.publish(
810
- createWorkflowEvent('workflow.stopped', this.getRunIdFromSessionId(sessionId), {
811
- reason: reason ?? 'User requested stop',
812
- })
813
- );
814
-
815
- // Cleanup
816
- this.cleanupSession(sessionState);
817
- }
818
-
819
- /**
820
- * Get session status
821
- */
822
- getSessionStatus(sessionId: string): RunStatus | null {
823
- const sessionState = this.activeSessions.get(sessionId);
824
- return sessionState?.status ?? null;
825
- }
826
-
827
- /**
828
- * Check if session is active
829
- */
830
- isSessionActive(sessionId: string): boolean {
831
- return this.activeSessions.has(sessionId);
832
- }
833
-
834
- // =========================================================================
835
- // Private Methods
836
- // =========================================================================
837
-
838
- /**
839
- * Setup event handlers for a run
840
- */
841
- private setupEventHandlers(sessionState: ActiveSessionState): void {
842
- const { sessionId, eventBus, scheduler } = sessionState;
843
- const runId = this.getRunIdFromSessionId(sessionId);
844
-
845
- // Handle node completed
846
- eventBus.on('node.completed', async (event: WorkflowEvent) => {
847
- if (event.type !== 'node.completed') return;
848
-
849
- sessionState.nodeOutputs.set(event.node_id, event.output);
850
- scheduler.markCompleted(event.node_id);
851
-
852
- // Accumulate workflow history
853
- if (event.output && typeof event.output === 'object' && 'workflowHistory' in event.output) {
854
- const messages = (event.output as any).workflowHistory;
855
- if (Array.isArray(messages)) {
856
- sessionState.workflowHistory.push(...messages);
857
- }
858
- }
859
-
860
- // Check if workflow is complete
861
- this.checkAndFinalize(sessionState);
862
- });
863
-
864
- // Handle node failed
865
- eventBus.on('node.failed', async (event: WorkflowEvent) => {
866
- if (event.type !== 'node.failed') return;
867
-
868
- scheduler.markFailed(event.node_id);
869
- await this.failWorkflow(sessionState, new Error(event.error.message));
870
- });
871
-
872
- // Handle node interrupt (ask_user pause)
873
- eventBus.on('node.interrupt', async (event: WorkflowEvent) => {
874
- if (event.type !== 'node.interrupt') return;
875
-
876
- logger.info(`Workflow paused at node "${event.node_id}" - ask_user pending`);
877
-
878
- sessionState.status = 'paused';
879
- sessionState.abortController.abort();
880
-
881
- // Update session metadata
882
- if (this.sessionComponent) {
883
- const metadata = await this.getSessionMetadata(sessionId);
884
- await this.sessionComponent.update(sessionId, {
885
- metadata: { ...metadata, status: 'paused' },
886
- });
887
- }
888
-
889
- // Publish workflow.paused event
890
- await eventBus.publish(
891
- createWorkflowEvent('workflow.paused', runId, {
892
- pendingNodeId: event.node_id,
893
- query: (event as any).query,
894
- })
895
- );
896
-
897
- // Write interrupt message to session
898
- await this.writeNodeInterrupt(sessionId, event.node_id, event.node_type, (event as any).query, (event as any).agent_session_id);
899
-
900
- // Resolve with paused status
901
- sessionState.resolveCompleted({
902
- runId,
903
- status: 'paused',
904
- pendingNodeId: event.node_id,
905
- query: (event as any).query,
906
- agentSessionId: (event as any).agent_session_id,
907
- });
908
- });
909
- }
910
-
911
- /**
912
- * Write node interrupt message
913
- */
914
- @TracedAs("workflow.engine.writeNodeInterrupt", { recordParams: true, recordResult: true, log: true })
915
- @TracedAs("workflow.engine.writeNodeInterrupt", { recordParams: true, recordResult: true, log: true })
916
- private async writeNodeInterrupt(
917
- sessionId: string,
918
- nodeId: string,
919
- nodeType: string,
920
- query: string,
921
- agentSessionId?: string
922
- ): Promise<void> {
923
- if (this.sessionComponent) {
924
- await this.sessionComponent.addMessage(sessionId, {
925
- role: 'workflow.node.interrupt',
926
- content: query,
927
- metadata: {
928
- type: 'workflow.node.interrupt',
929
- workflowNodeId: nodeId,
930
- workflowNodeType: nodeType,
931
- query,
932
- agentSessionId,
933
- timestamp: Date.now(),
934
- },
935
- });
936
- }
937
- }
938
-
939
- /**
940
- * Main scheduling loop
941
- */
942
- @TracedAs("workflow.engine.scheduleAndExecute", { recordParams: true, recordResult: true, log: true })
943
- @TracedAs("workflow.engine.scheduleAndExecute", { recordParams: true, recordResult: true, log: true })
944
- private async scheduleAndExecute(
945
- sessionState: ActiveSessionState,
946
- options?: {
947
- input?: Record<string, any>;
948
- pendingNodeId?: string;
949
- agentSessionId?: string;
950
- userResponse?: string;
951
- restoredOutputs?: Map<string, unknown>;
952
- }
953
- ): Promise<void> {
954
- const { scheduler, executor, eventBus, abortController, nodeOutputs } = sessionState;
955
- const { input: globalInput, pendingNodeId, agentSessionId, userResponse, restoredOutputs } = options || {};
956
-
957
- // Merge restored outputs if any
958
- if (restoredOutputs) {
959
- for (const [nodeId, output] of restoredOutputs) {
960
- nodeOutputs.set(nodeId, output);
961
- }
962
- }
963
-
964
- // Helper to schedule available nodes
965
- const scheduleAvailableNodes = async (): Promise<boolean> => {
966
- const state = scheduler.getState();
967
- const completedNodes = new Set(state.completed);
968
-
969
- const readyNodes = scheduler.getReadyNodes(completedNodes);
970
- const canStart = scheduler.canStartMore();
971
-
972
- if (readyNodes.length === 0 || !canStart) {
973
- return false;
974
- }
975
-
976
- let scheduled = 0;
977
- for (const nodeId of readyNodes) {
978
- if (!scheduler.canStartMore()) break;
979
- if (scheduled >= (sessionState.config.parallelLimit ?? Infinity)) break;
980
-
981
- await this.startNode(sessionState, nodeId, globalInput);
982
- scheduled++;
983
- }
984
-
985
- return true;
986
- };
987
-
988
- // Initial scheduling
989
- if (pendingNodeId) {
990
- // Resume mode: only schedule the pending node
991
- await this.resumeNode(sessionState, pendingNodeId, {
992
- agentSessionId,
993
- userResponse,
994
- });
995
- } else {
996
- await scheduleAvailableNodes();
997
- }
998
-
999
- // Event-driven loop
1000
- while (sessionState.status === 'running' && !abortController.signal.aborted) {
1001
- const state = scheduler.getState();
1002
- if (state.pending.length === 0 && state.running.length === 0) {
1003
- break;
1004
- }
1005
-
1006
- try {
1007
- await this.waitForNextNodeEvent(sessionState, abortController.signal);
1008
- } catch {
1009
- break;
1010
- }
1011
-
1012
- await scheduleAvailableNodes();
1013
- }
1014
- }
1015
-
1016
- /**
1017
- * Wait for next node event
1018
- */
1019
- @TracedAs("workflow.engine.waitForNextNodeEvent", { recordParams: true, recordResult: true, log: true })
1020
- private waitForNextNodeEvent(
1021
- sessionState: ActiveSessionState,
1022
- signal: AbortSignal
1023
- ): Promise<void> {
1024
- return new Promise((resolve) => {
1025
- const timeoutId = setTimeout(() => {
1026
- cleanup();
1027
- resolve();
1028
- }, 100);
1029
-
1030
- const cleanup = () => {
1031
- clearTimeout(timeoutId);
1032
- sessionState.eventBus.off('node.completed', onCompleted);
1033
- sessionState.eventBus.off('node.failed', onFailed);
1034
- };
1035
-
1036
- const onCompleted = (event: WorkflowEvent) => {
1037
- if (event.type === 'node.completed') {
1038
- cleanup();
1039
- resolve();
1040
- }
1041
- };
1042
-
1043
- const onFailed = (event: WorkflowEvent) => {
1044
- if (event.type === 'node.failed') {
1045
- cleanup();
1046
- resolve();
1047
- }
1048
- };
1049
-
1050
- sessionState.eventBus.on('node.completed', onCompleted);
1051
- sessionState.eventBus.on('node.failed', onFailed);
1052
-
1053
- if (signal.aborted) {
1054
- cleanup();
1055
- resolve();
1056
- }
1057
- });
1058
- }
1059
-
1060
- /**
1061
- * Start execution of a single node
1062
- */
1063
- @TracedAs("workflow.engine.startNode", { recordParams: true, recordResult: true, log: true })
1064
- @TracedAs("workflow.engine.startNode", { recordParams: true, recordResult: true, log: true })
1065
- private async startNode(
1066
- sessionState: ActiveSessionState,
1067
- nodeId: string,
1068
- input?: Record<string, any>
1069
- ): Promise<void> {
1070
- const { scheduler, executor, eventBus, dagManager } = sessionState;
1071
- const sessionId = sessionState.sessionId;
1072
- const runId = this.getRunIdFromSessionId(sessionId);
1073
-
1074
- scheduler.markStarted(nodeId);
1075
-
1076
- // Publish scheduled event
1077
- await eventBus.publish(
1078
- createWorkflowEvent('node.scheduled', runId, {
1079
- node_id: nodeId,
1080
- })
1081
- );
1082
-
1083
- // Get node definition
1084
- const nodeDef = dagManager.getNode(nodeId);
1085
- if (!nodeDef) {
1086
- scheduler.markFailed(nodeId);
1087
- return;
1088
- }
1089
-
1090
- // Write workflow.node.call message
1091
- await this.writeNodeCall(sessionId, nodeId, nodeDef.type, input);
1092
-
1093
- // Create execution context
1094
- const context = this.createExecutionContext(sessionState, nodeId, input);
1095
-
1096
- // Execute node
1097
- executor.executeNode(nodeDef, context).catch(async (error) => {
1098
- if (error instanceof AskUserError) {
1099
- await eventBus.publish(createNodeInterruptEvent(
1100
- runId,
1101
- nodeId,
1102
- nodeDef.type,
1103
- error.query,
1104
- error.agentSessionId
1105
- ));
1106
- return;
1107
- }
1108
-
1109
- scheduler.markFailed(nodeId);
1110
- });
1111
- }
1112
-
1113
- /**
1114
- * Write node call message
1115
- */
1116
- @TracedAs("workflow.engine.writeNodeCall", { recordParams: true, recordResult: true, log: true })
1117
- @TracedAs("workflow.engine.writeNodeCall", { recordParams: true, recordResult: true, log: true })
1118
- private async writeNodeCall(
1119
- sessionId: string,
1120
- nodeId: string,
1121
- nodeType: string,
1122
- input?: any
1123
- ): Promise<void> {
1124
- if (this.sessionComponent) {
1125
- await this.sessionComponent.addMessage(sessionId, {
1126
- role: 'workflow.node.call',
1127
- content: JSON.stringify({ input }),
1128
- metadata: {
1129
- type: 'workflow.node.call',
1130
- workflowNodeId: nodeId,
1131
- workflowNodeType: nodeType,
1132
- timestamp: Date.now(),
1133
- },
1134
- });
1135
- }
1136
- }
1137
-
1138
- /**
1139
- * Resume execution of a paused node
1140
- */
1141
- @TracedAs("workflow.engine.resumeNode", { recordParams: true, recordResult: true, log: true })
1142
- @TracedAs("workflow.engine.resumeNode", { recordParams: true, recordResult: true, log: true })
1143
- private async resumeNode(
1144
- sessionState: ActiveSessionState,
1145
- nodeId: string,
1146
- options?: {
1147
- agentSessionId?: string;
1148
- userResponse?: string;
1149
- }
1150
- ): Promise<void> {
1151
- const { scheduler, executor, eventBus, dagManager } = sessionState;
1152
- const sessionId = sessionState.sessionId;
1153
- const runId = this.getRunIdFromSessionId(sessionId);
1154
-
1155
- scheduler.markStarted(nodeId);
1156
-
1157
- // Publish resume event
1158
- await eventBus.publish(
1159
- createWorkflowEvent('node.started', runId, {
1160
- node_id: nodeId,
1161
- agentSessionId: options?.agentSessionId,
1162
- userResponse: options?.userResponse,
1163
- })
1164
- );
1165
-
1166
- // Get node definition
1167
- const nodeDef = dagManager.getNode(nodeId);
1168
- if (!nodeDef) {
1169
- scheduler.markFailed(nodeId);
1170
- return;
1171
- }
1172
-
1173
- // Create execution context
1174
- const context = this.createExecutionContext(sessionState, nodeId, undefined);
1175
-
1176
- // Add resume options to context
1177
- if (options?.agentSessionId || options?.userResponse) {
1178
- (context as any).agentSessionId = options.agentSessionId;
1179
- (context as any).userResponse = options.userResponse;
1180
- }
1181
-
1182
- // Execute node
1183
- executor.executeNode(nodeDef, context).catch((error) => {
1184
- if (error instanceof AskUserError) {
1185
- const event = createNodeInterruptEvent(
1186
- runId,
1187
- nodeId,
1188
- nodeDef.type,
1189
- error.query,
1190
- error.agentSessionId
1191
- );
1192
- eventBus.publish(event);
1193
- return;
1194
- }
1195
- scheduler.markFailed(nodeId);
1196
- });
1197
- }
1198
-
1199
- /**
1200
- * Create execution context for a node
1201
- */
1202
- private createExecutionContext(
1203
- sessionState: ActiveSessionState,
1204
- nodeId: string,
1205
- globalInput?: Record<string, any>
1206
- ): NodeExecutionContext {
1207
- const nodeDef = sessionState.dagManager.getNode(nodeId)!;
1208
- const runId = this.getRunIdFromSessionId(sessionState.sessionId);
1209
-
1210
- // Collect inputs from dependencies
1211
- const deps = nodeDef.depends_on || [];
1212
- const input: Record<string, any> = {};
1213
-
1214
- for (const depId of deps) {
1215
- const depOutput = sessionState.nodeOutputs.get(depId);
1216
- if (depOutput !== undefined) {
1217
- input[depId] = depOutput;
1218
- }
1219
- }
1220
-
1221
- // Merge global input if this is an entry node
1222
- const analysis = sessionState.dagManager.analyze();
1223
- if (analysis.entryNodes.includes(nodeId) && globalInput) {
1224
- Object.assign(input, globalInput);
1225
- }
1226
-
1227
- // Ask user function
1228
- const askUser = (query: string): never => {
1229
- throw new AskUserError(
1230
- runId,
1231
- sessionState.sessionId,
1232
- nodeId,
1233
- nodeDef.type,
1234
- query
1235
- );
1236
- };
1237
-
1238
- return {
1239
- runId,
1240
- sessionId: sessionState.sessionId,
1241
- workflowName: sessionState.workflowName,
1242
- nodeId,
1243
- input,
1244
- previousOutputs: sessionState.nodeOutputs,
1245
- config: nodeDef.config ?? {},
1246
- debug: sessionState.config.debug,
1247
- eventBus: sessionState.eventBus,
1248
- nodeOutputs: sessionState.nodeOutputs,
1249
- workflowHistory: sessionState.workflowHistory,
1250
- sessionComponent: this.sessionComponent,
1251
- askUser,
1252
- };
1253
- }
1254
-
1255
- /**
1256
- * Check if workflow is complete and finalize
1257
- */
1258
- @TracedAs("workflow.engine.checkAndFinalize", { recordParams: true, recordResult: true, log: true })
1259
- private checkAndFinalize(sessionState: ActiveSessionState): void {
1260
- const state = sessionState.scheduler.getState();
1261
-
1262
- if (state.pending.length === 0 &&
1263
- state.running.length === 0 &&
1264
- state.ready.length === 0) {
1265
-
1266
- if (state.failed.length > 0) {
1267
- this.failWorkflow(sessionState, new Error(`Nodes failed: ${state.failed.join(', ')}`));
1268
- } else {
1269
- this.completeWorkflow(sessionState);
1270
- }
1271
- }
1272
- }
1273
-
1274
- /**
1275
- * Complete a workflow successfully
1276
- */
1277
- @TracedAs("workflow.engine.completeWorkflow", { recordParams: true, recordResult: true, log: true })
1278
- private async completeWorkflow(sessionState: ActiveSessionState): Promise<void> {
1279
- const durationMs = Date.now() - sessionState.startedAt.getTime();
1280
- const sessionId = sessionState.sessionId;
1281
- const runId = this.getRunIdFromSessionId(sessionId);
1282
-
1283
- // Collect output
1284
- const output: Record<string, any> = {};
1285
- for (const [nodeId, nodeOutput] of sessionState.nodeOutputs) {
1286
- output[nodeId] = nodeOutput;
1287
- }
1288
-
1289
- // Update session metadata
1290
- if (this.sessionComponent) {
1291
- const metadata = await this.getSessionMetadata(sessionId);
1292
- await this.sessionComponent.update(sessionId, {
1293
- metadata: { ...metadata, status: 'completed' },
1294
- });
1295
- }
1296
-
1297
- // Publish completed event
1298
- await sessionState.eventBus.publish(
1299
- createWorkflowEvent('workflow.completed', runId, {
1300
- result: output,
1301
- duration_ms: durationMs,
1302
- }),
1303
- true
1304
- );
1305
-
1306
- // Resolve completion
1307
- sessionState.resolveCompleted({
1308
- runId,
1309
- status: 'completed',
1310
- output,
1311
- durationMs,
1312
- });
1313
-
1314
- // Cleanup
1315
- this.cleanupSession(sessionState);
1316
- }
1317
-
1318
- /**
1319
- * Fail a workflow
1320
- */
1321
- @TracedAs("workflow.engine.failWorkflow", { recordParams: true, recordResult: true, log: true })
1322
- private async failWorkflow(sessionState: ActiveSessionState, error: Error): Promise<void> {
1323
- const durationMs = Date.now() - sessionState.startedAt.getTime();
1324
- const sessionId = sessionState.sessionId;
1325
- const runId = this.getRunIdFromSessionId(sessionId);
1326
-
1327
- sessionState.executor.cancelAll();
1328
-
1329
- // Update session metadata
1330
- if (this.sessionComponent) {
1331
- const metadata = await this.getSessionMetadata(sessionId);
1332
- await this.sessionComponent.update(sessionId, {
1333
- metadata: { ...metadata, status: 'failed' },
1334
- });
1335
- }
1336
-
1337
- // Publish failed event
1338
- await sessionState.eventBus.publish(
1339
- createWorkflowEvent('workflow.failed', runId, {
1340
- error: { message: error.message, stack: error.stack },
1341
- failed_at: new Date().toISOString(),
1342
- }),
1343
- true
1344
- );
1345
-
1346
- // Resolve with error
1347
- sessionState.resolveCompleted({
1348
- runId,
1349
- status: 'failed',
1350
- error: error.message,
1351
- durationMs,
1352
- });
1353
-
1354
- // Cleanup
1355
- this.cleanupSession(sessionState);
1356
- }
1357
-
1358
- /**
1359
- * Cleanup session resources
1360
- */
1361
- private cleanupSession(sessionState: ActiveSessionState): void {
1362
- sessionState.eventBus.clear();
1363
- this.activeSessions.delete(sessionState.sessionId);
1364
- }
1365
-
1366
- /**
1367
- * Wait for a run to complete
1368
- */
1369
- private async waitForCompletion(
1370
- sessionId: string,
1371
- timeout?: number | null
1372
- ): Promise<RunResult> {
1373
- const sessionState = this.activeSessions.get(sessionId);
1374
-
1375
- if (!sessionState) {
1376
- throw new Error(`Session not found: ${sessionId}`);
1377
- }
1378
-
1379
- const timeoutMs = timeout ?? 300000;
1380
-
1381
- const timeoutPromise = new Promise<RunResult>((_, reject) => {
1382
- setTimeout(() => {
1383
- reject(new Error(`Workflow execution timeout: ${timeoutMs}ms`));
1384
- }, timeoutMs);
1385
- });
1386
-
1387
- return Promise.race([
1388
- sessionState.completedPromise,
1389
- timeoutPromise,
1390
- ]);
1391
- }
1392
-
1393
- /**
1394
- * Get session metadata
1395
- */
1396
- private async getSessionMetadata(sessionId: string): Promise<WorkflowSessionMetadata> {
1397
- if (!this.sessionComponent) {
1398
- return { type: 'workflow', workflowId: '', workflowName: '', status: 'running' };
1399
- }
1400
- const session = await this.sessionComponent.get(sessionId);
1401
- return (session?.metadata as WorkflowSessionMetadata) || { type: 'workflow', workflowId: '', workflowName: '', status: 'running' };
1402
- }
1403
-
1404
- /**
1405
- * Find workflow definition from repository
1406
- */
1407
- private findDefinitionForWorkflow(workflowIdOrName: string): WorkflowDefinition | null {
1408
- if (!this.workflowRepository) {
1409
- return null;
1410
- }
1411
-
1412
- // Try by ID first
1413
- let workflow = this.workflowRepository.getById(workflowIdOrName);
1414
- if (workflow) return workflow.definition;
1415
-
1416
- // Try by name
1417
- workflow = this.workflowRepository.getByName(workflowIdOrName);
1418
- if (workflow) return workflow.definition;
1419
-
1420
- return null;
1421
- }
1422
- }