@ai-setting/roy-agent-core 1.0.0

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 (378) hide show
  1. package/dist/index.js +99145 -0
  2. package/package.json +114 -0
  3. package/src/config/config-component.test.ts +627 -0
  4. package/src/config/config-component.ts +906 -0
  5. package/src/config/config-parser.test.ts +319 -0
  6. package/src/config/config-parser.ts +203 -0
  7. package/src/config/decentralized-config.test.ts +740 -0
  8. package/src/config/env-key.ts +210 -0
  9. package/src/config/env-source.test.ts +252 -0
  10. package/src/config/env-source.ts +301 -0
  11. package/src/config/file-source.test.ts +357 -0
  12. package/src/config/file-source.ts +421 -0
  13. package/src/config/index.ts +24 -0
  14. package/src/config/protocol-resolver.test.ts +217 -0
  15. package/src/config/protocol-resolver.ts +228 -0
  16. package/src/env/agent/agent-component.abort.test.ts +511 -0
  17. package/src/env/agent/agent-component.record-session.test.ts +349 -0
  18. package/src/env/agent/agent-component.test.ts +1389 -0
  19. package/src/env/agent/agent-component.tool-error.test.ts +327 -0
  20. package/src/env/agent/agent-component.ts +1711 -0
  21. package/src/env/agent/agent-config-registration.test.ts +226 -0
  22. package/src/env/agent/agent-config-registration.ts +46 -0
  23. package/src/env/agent/agent-reminder-plugin.integration.test.ts +243 -0
  24. package/src/env/agent/index.ts +10 -0
  25. package/src/env/agent/summary-agent.parse-hint.test.ts +360 -0
  26. package/src/env/agent/summary-agent.ts +508 -0
  27. package/src/env/agent/types.ts +536 -0
  28. package/src/env/commands/commands-component.test.ts +364 -0
  29. package/src/env/commands/commands-component.ts +604 -0
  30. package/src/env/commands/commands-config-registration.test.ts +198 -0
  31. package/src/env/commands/commands-config-registration.ts +38 -0
  32. package/src/env/commands/index.ts +21 -0
  33. package/src/env/commands/parser.test.ts +203 -0
  34. package/src/env/commands/parser.ts +115 -0
  35. package/src/env/commands/types.ts +184 -0
  36. package/src/env/commands-prompt-integration.test.ts +243 -0
  37. package/src/env/component-env.test.ts +119 -0
  38. package/src/env/component.ts +335 -0
  39. package/src/env/constants.test.ts +72 -0
  40. package/src/env/constants.ts +123 -0
  41. package/src/env/debug/debug-component.test.ts +114 -0
  42. package/src/env/debug/debug-component.ts +547 -0
  43. package/src/env/debug/formatters/index.ts +9 -0
  44. package/src/env/debug/formatters/repl-formatter.test.ts +139 -0
  45. package/src/env/debug/formatters/repl-formatter.ts +358 -0
  46. package/src/env/debug/formatters/trace-formatter.test.ts +119 -0
  47. package/src/env/debug/formatters/trace-formatter.ts +191 -0
  48. package/src/env/debug/formatters/tree-formatter.test.ts +107 -0
  49. package/src/env/debug/formatters/tree-formatter.ts +325 -0
  50. package/src/env/debug/index.ts +38 -0
  51. package/src/env/debug/parser/regex-parser.test.ts +201 -0
  52. package/src/env/debug/parser/regex-parser.ts +196 -0
  53. package/src/env/debug/parser/span-builder.test.ts +241 -0
  54. package/src/env/debug/parser/span-builder.ts +386 -0
  55. package/src/env/debug/reader/log-reader.test.ts +170 -0
  56. package/src/env/debug/reader/log-reader.ts +186 -0
  57. package/src/env/debug/reader/span-db-reader.test.ts +118 -0
  58. package/src/env/debug/reader/span-db-reader.ts +201 -0
  59. package/src/env/debug/types.test.ts +187 -0
  60. package/src/env/debug/types.ts +171 -0
  61. package/src/env/environment-init.test.ts +183 -0
  62. package/src/env/environment-lifecycle.test.ts +516 -0
  63. package/src/env/environment-service.test.ts +332 -0
  64. package/src/env/environment.handle-query.test.ts +96 -0
  65. package/src/env/environment.test.ts +232 -0
  66. package/src/env/environment.ts +708 -0
  67. package/src/env/errors.test.ts +165 -0
  68. package/src/env/errors.ts +157 -0
  69. package/src/env/event-source/event-source-agent-handler.test.ts +193 -0
  70. package/src/env/event-source/event-source-agent-handler.ts +111 -0
  71. package/src/env/event-source/event-source-component.process-cleanup.test.ts +236 -0
  72. package/src/env/event-source/event-source-component.stop.test.ts +346 -0
  73. package/src/env/event-source/event-source-component.test.ts +1207 -0
  74. package/src/env/event-source/event-source-component.ts +1379 -0
  75. package/src/env/event-source/event-source-config-registration.test.ts +242 -0
  76. package/src/env/event-source/event-source-config-registration.ts +37 -0
  77. package/src/env/event-source/event-source-integration.test.ts +320 -0
  78. package/src/env/event-source/event-source-platform.test.ts +630 -0
  79. package/src/env/event-source/types.ts +298 -0
  80. package/src/env/hook/global-hook-manager.ts +162 -0
  81. package/src/env/hook/hook-manager.test.ts +374 -0
  82. package/src/env/hook/hook-manager.ts +309 -0
  83. package/src/env/hook/index.ts +38 -0
  84. package/src/env/hook/types.ts +138 -0
  85. package/src/env/index.ts +144 -0
  86. package/src/env/interface.ts +203 -0
  87. package/src/env/llm/hooks.test.ts +293 -0
  88. package/src/env/llm/hooks.ts +316 -0
  89. package/src/env/llm/index.ts +61 -0
  90. package/src/env/llm/invoke-threshold-check.test.ts +88 -0
  91. package/src/env/llm/invoke-timeout.test.ts +54 -0
  92. package/src/env/llm/invoke.test.ts +71 -0
  93. package/src/env/llm/invoke.ts +1039 -0
  94. package/src/env/llm/llm-config.test.ts +523 -0
  95. package/src/env/llm/llm.test.ts +233 -0
  96. package/src/env/llm/llm.ts +568 -0
  97. package/src/env/llm/provider.test.ts +182 -0
  98. package/src/env/llm/provider.ts +108 -0
  99. package/src/env/llm/transform.test.ts +251 -0
  100. package/src/env/llm/transform.ts +286 -0
  101. package/src/env/llm/types.test.ts +580 -0
  102. package/src/env/llm/types.ts +424 -0
  103. package/src/env/log-trace/decorator-otel.test.ts +182 -0
  104. package/src/env/log-trace/decorator.ts +230 -0
  105. package/src/env/log-trace/index.ts +79 -0
  106. package/src/env/log-trace/log-trace-component.test.ts +242 -0
  107. package/src/env/log-trace/log-trace-component.ts +497 -0
  108. package/src/env/log-trace/log-trace-config-registration.test.ts +348 -0
  109. package/src/env/log-trace/log-trace-config-registration.ts +45 -0
  110. package/src/env/log-trace/logger.test.ts +149 -0
  111. package/src/env/log-trace/logger.ts +522 -0
  112. package/src/env/log-trace/opentelemetry/cli-propagation.test.ts +147 -0
  113. package/src/env/log-trace/opentelemetry/cli-propagation.ts +194 -0
  114. package/src/env/log-trace/opentelemetry/integration.test.ts +668 -0
  115. package/src/env/log-trace/opentelemetry/mod.ts +25 -0
  116. package/src/env/log-trace/opentelemetry/propagation-env.test.ts +181 -0
  117. package/src/env/log-trace/opentelemetry/propagation-env.ts +136 -0
  118. package/src/env/log-trace/opentelemetry/propagation.test.ts +259 -0
  119. package/src/env/log-trace/opentelemetry/propagation.ts +215 -0
  120. package/src/env/log-trace/opentelemetry/tracer-provider-context.test.ts +166 -0
  121. package/src/env/log-trace/opentelemetry/tracer-provider.test.ts +379 -0
  122. package/src/env/log-trace/opentelemetry/tracer-provider.ts +612 -0
  123. package/src/env/log-trace/span-storage.test.ts +145 -0
  124. package/src/env/log-trace/span-storage.ts +230 -0
  125. package/src/env/log-trace/trace-context.test.ts +187 -0
  126. package/src/env/log-trace/trace-context.ts +162 -0
  127. package/src/env/log-trace/types.test.ts +63 -0
  128. package/src/env/log-trace/types.ts +172 -0
  129. package/src/env/mcp/README.md +244 -0
  130. package/src/env/mcp/__integration__/mcp-component.integration.test.ts +373 -0
  131. package/src/env/mcp/config.test.ts +74 -0
  132. package/src/env/mcp/config.ts +116 -0
  133. package/src/env/mcp/index.ts +41 -0
  134. package/src/env/mcp/loader.test.ts +161 -0
  135. package/src/env/mcp/loader.ts +209 -0
  136. package/src/env/mcp/mcp-component.test.ts +111 -0
  137. package/src/env/mcp/mcp-component.ts +358 -0
  138. package/src/env/mcp/mcp-config-registration.test.ts +304 -0
  139. package/src/env/mcp/mcp-config-registration.ts +50 -0
  140. package/src/env/mcp/scanner.test.ts +170 -0
  141. package/src/env/mcp/scanner.ts +246 -0
  142. package/src/env/mcp/tool/adapter.test.ts +520 -0
  143. package/src/env/mcp/tool/adapter.ts +521 -0
  144. package/src/env/mcp/tool/index.ts +5 -0
  145. package/src/env/mcp/types.test.ts +171 -0
  146. package/src/env/mcp/types.ts +79 -0
  147. package/src/env/memory/README.md +177 -0
  148. package/src/env/memory/built-in/index.ts +59 -0
  149. package/src/env/memory/built-in/recall-memory.ts +103 -0
  150. package/src/env/memory/built-in/record-memory.ts +148 -0
  151. package/src/env/memory/index.ts +20 -0
  152. package/src/env/memory/memory-component.test.ts +239 -0
  153. package/src/env/memory/memory-component.ts +503 -0
  154. package/src/env/memory/memory-config-registration.test.ts +67 -0
  155. package/src/env/memory/memory-config-registration.ts +48 -0
  156. package/src/env/memory/memory-config.ts +45 -0
  157. package/src/env/memory/memory-file.test.ts +268 -0
  158. package/src/env/memory/plugin/index.ts +48 -0
  159. package/src/env/memory/plugin/memory-agent.test.ts +249 -0
  160. package/src/env/memory/plugin/memory-agent.ts +365 -0
  161. package/src/env/memory/plugin/memory-manager.ts +198 -0
  162. package/src/env/memory/plugin/memory-plugin-agent.test.ts +145 -0
  163. package/src/env/memory/plugin/memory-plugin.ts +210 -0
  164. package/src/env/memory/plugin/plugin-simplified.test.ts +51 -0
  165. package/src/env/memory/plugin/recall-memory.test.ts +106 -0
  166. package/src/env/memory/plugin/recall-memory.ts +53 -0
  167. package/src/env/memory/plugin/types.ts +101 -0
  168. package/src/env/memory/tools/memory-agent-tools.ts +228 -0
  169. package/src/env/memory/types.ts +85 -0
  170. package/src/env/paths.ts +118 -0
  171. package/src/env/prompt/index.ts +18 -0
  172. package/src/env/prompt/memory-prompts.test.ts +91 -0
  173. package/src/env/prompt/prompt-component.test.ts +491 -0
  174. package/src/env/prompt/prompt-component.ts +619 -0
  175. package/src/env/prompt/prompt-config-registration.test.ts +213 -0
  176. package/src/env/prompt/prompt-config-registration.ts +39 -0
  177. package/src/env/prompt/prompts-index.ts +504 -0
  178. package/src/env/prompt/renderer.ts +67 -0
  179. package/src/env/prompt/types.ts +136 -0
  180. package/src/env/session/hooks.ts +18 -0
  181. package/src/env/session/index.ts +37 -0
  182. package/src/env/session/search-query-parser.test.ts +425 -0
  183. package/src/env/session/search-query-parser.ts +171 -0
  184. package/src/env/session/session-checkpoint.test.ts +523 -0
  185. package/src/env/session/session-component.extract-recent-messages.test.ts +209 -0
  186. package/src/env/session/session-component.test.ts +132 -0
  187. package/src/env/session/session-component.ts +1249 -0
  188. package/src/env/session/session-config-registration.test.ts +138 -0
  189. package/src/env/session/session-config-registration.ts +52 -0
  190. package/src/env/session/session-message-converter.test.ts +763 -0
  191. package/src/env/session/session-message-converter.ts +415 -0
  192. package/src/env/session/session-message-e2e.test.ts +448 -0
  193. package/src/env/session/session-search.test.ts +391 -0
  194. package/src/env/session/session-store.test.ts +362 -0
  195. package/src/env/session/session-store.ts +141 -0
  196. package/src/env/session/storage/index.ts +6 -0
  197. package/src/env/session/storage/memory.ts +502 -0
  198. package/src/env/session/storage/sqlite.ts +794 -0
  199. package/src/env/session/types.ts +742 -0
  200. package/src/env/skill/config.ts +39 -0
  201. package/src/env/skill/index.ts +6 -0
  202. package/src/env/skill/parser.test.ts +116 -0
  203. package/src/env/skill/parser.ts +77 -0
  204. package/src/env/skill/scanner.test.ts +211 -0
  205. package/src/env/skill/scanner.ts +119 -0
  206. package/src/env/skill/skill-component.test.ts +234 -0
  207. package/src/env/skill/skill-component.ts +352 -0
  208. package/src/env/skill/skill-config-registration.test.ts +60 -0
  209. package/src/env/skill/skill-config-registration.ts +43 -0
  210. package/src/env/skill/tool/index.ts +1 -0
  211. package/src/env/skill/tool/skill-tool.test.ts +100 -0
  212. package/src/env/skill/tool/skill-tool.ts +72 -0
  213. package/src/env/skill/types.ts +64 -0
  214. package/src/env/task/delegate/delegate-tool.test.ts +498 -0
  215. package/src/env/task/delegate/delegate-tool.ts +1014 -0
  216. package/src/env/task/delegate/index.ts +18 -0
  217. package/src/env/task/delegate/stop-tool.test.ts +140 -0
  218. package/src/env/task/delegate/stop-tool.ts +119 -0
  219. package/src/env/task/delegate/task-events.test.ts +178 -0
  220. package/src/env/task/delegate/task-events.ts +143 -0
  221. package/src/env/task/hooks/contexts.test.ts +92 -0
  222. package/src/env/task/hooks/contexts.ts +192 -0
  223. package/src/env/task/hooks/index.ts +23 -0
  224. package/src/env/task/hooks/task-hook-points.test.ts +32 -0
  225. package/src/env/task/hooks/task-hook-points.ts +54 -0
  226. package/src/env/task/index.ts +7 -0
  227. package/src/env/task/plugins/index.ts +13 -0
  228. package/src/env/task/plugins/task-plugin.test.ts +74 -0
  229. package/src/env/task/plugins/task-plugin.ts +89 -0
  230. package/src/env/task/plugins/task-tag-plugin.test.ts +377 -0
  231. package/src/env/task/plugins/task-tag-plugin.ts +319 -0
  232. package/src/env/task/plugins/task-workflow-extractor.integration.test.ts +226 -0
  233. package/src/env/task/plugins/workflow-extractor-agent.test.ts +107 -0
  234. package/src/env/task/plugins/workflow-extractor-agent.ts +225 -0
  235. package/src/env/task/storage/index.ts +6 -0
  236. package/src/env/task/storage/sqlite-task-store.test.ts +283 -0
  237. package/src/env/task/storage/sqlite-task-store.ts +903 -0
  238. package/src/env/task/storage/task-search.test.ts +291 -0
  239. package/src/env/task/tag-service.test.ts +198 -0
  240. package/src/env/task/tag-service.ts +264 -0
  241. package/src/env/task/task-component.test.ts +193 -0
  242. package/src/env/task/task-component.ts +658 -0
  243. package/src/env/task/task-config-registration.test.ts +57 -0
  244. package/src/env/task/task-config-registration.ts +37 -0
  245. package/src/env/task/task-types.test.ts +137 -0
  246. package/src/env/task/tools/complete-tool.ts +44 -0
  247. package/src/env/task/tools/create-tool.ts +49 -0
  248. package/src/env/task/tools/delete-tool.ts +43 -0
  249. package/src/env/task/tools/get-tool.ts +59 -0
  250. package/src/env/task/tools/index.ts +10 -0
  251. package/src/env/task/tools/list-tool.ts +40 -0
  252. package/src/env/task/tools/operation/create-tool.ts +48 -0
  253. package/src/env/task/tools/operation/delete-tool.ts +43 -0
  254. package/src/env/task/tools/operation/get-tool.ts +43 -0
  255. package/src/env/task/tools/operation/index.ts +9 -0
  256. package/src/env/task/tools/operation/list-tool.ts +40 -0
  257. package/src/env/task/tools/operation/operation-tools.test.ts +274 -0
  258. package/src/env/task/tools/operation/operation-types.ts +75 -0
  259. package/src/env/task/tools/operation/update-tool.ts +47 -0
  260. package/src/env/task/tools/task-tools.test.ts +203 -0
  261. package/src/env/task/tools/task-types.test.ts +75 -0
  262. package/src/env/task/tools/task-types.ts +68 -0
  263. package/src/env/task/tools/update-tool.ts +70 -0
  264. package/src/env/task/types.ts +160 -0
  265. package/src/env/tool/built-in/bash.ts +201 -0
  266. package/src/env/tool/built-in/echo.ts +29 -0
  267. package/src/env/tool/built-in/edit-file.test.ts +136 -0
  268. package/src/env/tool/built-in/edit-file.ts +92 -0
  269. package/src/env/tool/built-in/glob.test.ts +94 -0
  270. package/src/env/tool/built-in/glob.ts +65 -0
  271. package/src/env/tool/built-in/grep.test.ts +122 -0
  272. package/src/env/tool/built-in/grep.ts +108 -0
  273. package/src/env/tool/built-in/index.ts +44 -0
  274. package/src/env/tool/built-in/read-file.test.ts +84 -0
  275. package/src/env/tool/built-in/read-file.ts +75 -0
  276. package/src/env/tool/built-in/write-file.test.ts +119 -0
  277. package/src/env/tool/built-in/write-file.ts +68 -0
  278. package/src/env/tool/index.ts +24 -0
  279. package/src/env/tool/registry.test.ts +257 -0
  280. package/src/env/tool/registry.ts +167 -0
  281. package/src/env/tool/tool-component.test.ts +559 -0
  282. package/src/env/tool/tool-component.ts +563 -0
  283. package/src/env/tool/tool-config-registration.test.ts +249 -0
  284. package/src/env/tool/tool-config-registration.ts +46 -0
  285. package/src/env/tool/types.ts +267 -0
  286. package/src/env/tool/validator.test.ts +143 -0
  287. package/src/env/tool/validator.ts +44 -0
  288. package/src/env/types.ts +180 -0
  289. package/src/env/workflow/ask-user-tool-registration.test.ts +216 -0
  290. package/src/env/workflow/complex-workflow.integration.test.ts +1900 -0
  291. package/src/env/workflow/decorators/decorator-node.ts +229 -0
  292. package/src/env/workflow/decorators/decorator.test.ts +196 -0
  293. package/src/env/workflow/decorators/edge.ts +82 -0
  294. package/src/env/workflow/decorators/index.ts +31 -0
  295. package/src/env/workflow/decorators/node-as.ts +98 -0
  296. package/src/env/workflow/decorators/workflow.ts +54 -0
  297. package/src/env/workflow/engine/dag-manager.test.ts +570 -0
  298. package/src/env/workflow/engine/dag-manager.ts +594 -0
  299. package/src/env/workflow/engine/engine.ts +1422 -0
  300. package/src/env/workflow/engine/event-bus.test.ts +359 -0
  301. package/src/env/workflow/engine/event-bus.ts +156 -0
  302. package/src/env/workflow/engine/executor-agent-session.test.ts +84 -0
  303. package/src/env/workflow/engine/executor.test.ts +619 -0
  304. package/src/env/workflow/engine/executor.ts +593 -0
  305. package/src/env/workflow/engine/index.ts +24 -0
  306. package/src/env/workflow/engine/node-registry.test.ts +560 -0
  307. package/src/env/workflow/engine/node-registry.ts +289 -0
  308. package/src/env/workflow/engine/resume-removed.test.ts +22 -0
  309. package/src/env/workflow/engine/scheduler.test.ts +715 -0
  310. package/src/env/workflow/engine/scheduler.ts +318 -0
  311. package/src/env/workflow/engine/workflow-engine.test.ts +815 -0
  312. package/src/env/workflow/extractor/workflow-converter.ts +306 -0
  313. package/src/env/workflow/fixtures.ts +380 -0
  314. package/src/env/workflow/index.ts +38 -0
  315. package/src/env/workflow/integration/run-resume-unified.test.ts +186 -0
  316. package/src/env/workflow/integration/service-integration.test.ts +267 -0
  317. package/src/env/workflow/metadata/keys.ts +12 -0
  318. package/src/env/workflow/nodes/agent-component-adapter.test.ts +318 -0
  319. package/src/env/workflow/nodes/agent-component-adapter.ts +448 -0
  320. package/src/env/workflow/nodes/agent-node.test.ts +371 -0
  321. package/src/env/workflow/nodes/agent-node.ts +598 -0
  322. package/src/env/workflow/nodes/ask-user-node.ts +113 -0
  323. package/src/env/workflow/nodes/condition-node.ts +200 -0
  324. package/src/env/workflow/nodes/index.ts +9 -0
  325. package/src/env/workflow/nodes/merge-node.ts +141 -0
  326. package/src/env/workflow/nodes/skill-node.test.ts +253 -0
  327. package/src/env/workflow/nodes/skill-node.ts +393 -0
  328. package/src/env/workflow/nodes/tool-node.test.ts +251 -0
  329. package/src/env/workflow/nodes/tool-node.ts +493 -0
  330. package/src/env/workflow/nodes/workflow-llm-history.test.ts +455 -0
  331. package/src/env/workflow/nodes/workflow-node.test.ts +315 -0
  332. package/src/env/workflow/nodes/workflow-node.ts +311 -0
  333. package/src/env/workflow/service/index.ts +27 -0
  334. package/src/env/workflow/service/registry.test.ts +133 -0
  335. package/src/env/workflow/service/registry.ts +71 -0
  336. package/src/env/workflow/service/workflow-service.test.ts +310 -0
  337. package/src/env/workflow/service/workflow-service.ts +393 -0
  338. package/src/env/workflow/storage/index.ts +28 -0
  339. package/src/env/workflow/storage/mock-repositories.ts +385 -0
  340. package/src/env/workflow/storage/sqlite.test.ts +179 -0
  341. package/src/env/workflow/storage/sqlite.ts +163 -0
  342. package/src/env/workflow/storage/workflow-repo.test.ts +780 -0
  343. package/src/env/workflow/storage/workflow-repo.ts +342 -0
  344. package/src/env/workflow/tools/ask-user-tool.ts +82 -0
  345. package/src/env/workflow/tools/index.ts +26 -0
  346. package/src/env/workflow/tools/run-workflow.test.ts +352 -0
  347. package/src/env/workflow/tools/run-workflow.ts +214 -0
  348. package/src/env/workflow/types/context.ts +18 -0
  349. package/src/env/workflow/types/decorators-types.ts +198 -0
  350. package/src/env/workflow/types/event.test.ts +515 -0
  351. package/src/env/workflow/types/event.ts +193 -0
  352. package/src/env/workflow/types/index.ts +49 -0
  353. package/src/env/workflow/types/run.test.ts +437 -0
  354. package/src/env/workflow/types/run.ts +173 -0
  355. package/src/env/workflow/types/workflow-hil.ts +114 -0
  356. package/src/env/workflow/types/workflow-message.test.ts +138 -0
  357. package/src/env/workflow/types/workflow-message.ts +196 -0
  358. package/src/env/workflow/types/workflow-session.test.ts +95 -0
  359. package/src/env/workflow/types/workflow-session.ts +59 -0
  360. package/src/env/workflow/types/workflow.test.ts +495 -0
  361. package/src/env/workflow/types/workflow.ts +195 -0
  362. package/src/env/workflow/types_compat.ts +51 -0
  363. package/src/env/workflow/utils/create-workflow.ts +47 -0
  364. package/src/env/workflow/utils/execution-state.ts +245 -0
  365. package/src/env/workflow/utils/index.ts +18 -0
  366. package/src/env/workflow/utils/node-registry-helper.ts +58 -0
  367. package/src/env/workflow/utils/recovery-validator.test.ts +460 -0
  368. package/src/env/workflow/utils/recovery-validator.ts +377 -0
  369. package/src/env/workflow/utils/session-parser.test.ts +111 -0
  370. package/src/env/workflow/utils/session-parser.ts +94 -0
  371. package/src/env/workflow/utils/session-recovery.test.ts +334 -0
  372. package/src/env/workflow/utils/session-recovery.ts +188 -0
  373. package/src/env/workflow/utils/template-resolver.test.ts +258 -0
  374. package/src/env/workflow/utils/template-resolver.ts +436 -0
  375. package/src/env/workflow/utils/validation-rules.ts +149 -0
  376. package/src/env/workflow/workflow-component.ts +544 -0
  377. package/src/index.ts +422 -0
  378. package/src/utils/id.ts +21 -0
@@ -0,0 +1,780 @@
1
+ import { describe, it, expect, beforeEach } from 'bun:test';
2
+ import { randomUUID } from 'crypto';
3
+ import type { Workflow, WorkflowDefinition } from '../types';
4
+
5
+ /**
6
+ * WorkflowRepository Tests
7
+ *
8
+ * Tests CRUD operations for workflows in SQLite.
9
+ */
10
+
11
+ // Helper to create in-memory database
12
+ function createTestDatabase() {
13
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
14
+ const { Database } = require("bun:sqlite");
15
+ const db = new Database(":memory:");
16
+
17
+ // Create tables
18
+ db.exec(`
19
+ CREATE TABLE IF NOT EXISTS workflows (
20
+ id TEXT PRIMARY KEY,
21
+ name TEXT NOT NULL,
22
+ version TEXT NOT NULL DEFAULT '1.0',
23
+ description TEXT,
24
+ definition TEXT NOT NULL,
25
+ config TEXT NOT NULL DEFAULT '{}',
26
+ metadata TEXT NOT NULL DEFAULT '{}',
27
+ tags TEXT NOT NULL DEFAULT '[]',
28
+ task_id INTEGER,
29
+ created_at TEXT NOT NULL,
30
+ updated_at TEXT NOT NULL
31
+ )
32
+ `);
33
+
34
+ db.exec(`CREATE INDEX IF NOT EXISTS idx_workflows_name ON workflows(name)`);
35
+ db.exec(`CREATE INDEX IF NOT EXISTS idx_workflows_tags ON workflows(tags)`);
36
+ db.exec(`CREATE INDEX IF NOT EXISTS idx_workflows_task_id ON workflows(task_id)`);
37
+
38
+ return db;
39
+ }
40
+
41
+ interface WorkflowRow {
42
+ id: string;
43
+ name: string;
44
+ version: string;
45
+ description: string | null;
46
+ definition: string;
47
+ config: string;
48
+ metadata: string;
49
+ tags: string;
50
+ task_id: number | null;
51
+ created_at: string;
52
+ updated_at: string;
53
+ }
54
+
55
+ /**
56
+ * Row to Workflow conversion helper
57
+ */
58
+ function rowToWorkflow(row: WorkflowRow): Workflow {
59
+ const metadata = JSON.parse(row.metadata);
60
+ // Merge task_id from column into metadata
61
+ if (row.task_id !== null) {
62
+ metadata.taskId = row.task_id;
63
+ }
64
+ return {
65
+ id: row.id,
66
+ name: row.name,
67
+ version: row.version,
68
+ description: row.description || undefined,
69
+ definition: JSON.parse(row.definition),
70
+ config: JSON.parse(row.config),
71
+ metadata,
72
+ tags: JSON.parse(row.tags),
73
+ createdAt: new Date(row.created_at),
74
+ updatedAt: new Date(row.updated_at),
75
+ };
76
+ }
77
+
78
+ describe('WorkflowRepository', () => {
79
+ let db: any;
80
+
81
+ beforeEach(() => {
82
+ db = createTestDatabase();
83
+ });
84
+
85
+ describe('create', () => {
86
+ it('should create a new workflow', () => {
87
+ const id = randomUUID();
88
+ const now = new Date().toISOString();
89
+ const definition: WorkflowDefinition = {
90
+ name: 'TestWorkflow',
91
+ version: '1.0',
92
+ description: 'A test workflow',
93
+ nodes: [
94
+ { id: 'node1', type: 'llm' }
95
+ ],
96
+ };
97
+
98
+ const stmt = db.prepare(`
99
+ INSERT INTO workflows (id, name, version, description, definition, config, metadata, tags, created_at, updated_at)
100
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
101
+ `);
102
+
103
+ stmt.run(
104
+ id,
105
+ definition.name,
106
+ definition.version,
107
+ definition.description || null,
108
+ JSON.stringify(definition),
109
+ JSON.stringify({}),
110
+ JSON.stringify({}),
111
+ JSON.stringify([]),
112
+ now,
113
+ now
114
+ );
115
+
116
+ const row = db.prepare("SELECT * FROM workflows WHERE id = ?").get(id) as WorkflowRow;
117
+ const workflow = rowToWorkflow(row);
118
+
119
+ expect(workflow.id).toBe(id);
120
+ expect(workflow.name).toBe('TestWorkflow');
121
+ expect(workflow.version).toBe('1.0');
122
+ expect(workflow.definition.nodes).toHaveLength(1);
123
+ });
124
+
125
+ it('should set timestamps correctly', () => {
126
+ const id = randomUUID();
127
+ const now = new Date().toISOString();
128
+ const definition: WorkflowDefinition = {
129
+ name: 'TestWorkflow',
130
+ version: '1.0',
131
+ nodes: [{ id: 'node1', type: 'llm' }],
132
+ };
133
+
134
+ db.prepare(`
135
+ INSERT INTO workflows (id, name, version, definition, config, metadata, tags, created_at, updated_at)
136
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
137
+ `).run(
138
+ id,
139
+ definition.name,
140
+ definition.version,
141
+ JSON.stringify(definition),
142
+ JSON.stringify({}),
143
+ JSON.stringify({}),
144
+ JSON.stringify([]),
145
+ now,
146
+ now
147
+ );
148
+
149
+ const row = db.prepare("SELECT * FROM workflows WHERE id = ?").get(id) as WorkflowRow;
150
+
151
+ expect(row.created_at).toBe(now);
152
+ expect(row.updated_at).toBe(now);
153
+ });
154
+ });
155
+
156
+ describe('getById', () => {
157
+ it('should retrieve a workflow by id', () => {
158
+ const id = randomUUID();
159
+ const now = new Date().toISOString();
160
+ const definition: WorkflowDefinition = {
161
+ name: 'TestWorkflow',
162
+ version: '1.0',
163
+ nodes: [{ id: 'node1', type: 'llm' }],
164
+ };
165
+
166
+ db.prepare(`
167
+ INSERT INTO workflows (id, name, version, definition, config, metadata, tags, created_at, updated_at)
168
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
169
+ `).run(
170
+ id,
171
+ definition.name,
172
+ definition.version,
173
+ JSON.stringify(definition),
174
+ JSON.stringify({}),
175
+ JSON.stringify({}),
176
+ JSON.stringify([]),
177
+ now,
178
+ now
179
+ );
180
+
181
+ const row = db.prepare("SELECT * FROM workflows WHERE id = ?").get(id) as WorkflowRow | undefined;
182
+ expect(row).toBeDefined();
183
+
184
+ const workflow = rowToWorkflow(row!);
185
+ expect(workflow.id).toBe(id);
186
+ expect(workflow.name).toBe('TestWorkflow');
187
+ });
188
+
189
+ it('should return undefined for non-existent id', () => {
190
+ const row = db.prepare("SELECT * FROM workflows WHERE id = ?").get('non-existent') as WorkflowRow | null | undefined;
191
+ expect(row).toBeNull();
192
+ });
193
+ });
194
+
195
+ describe('getByName', () => {
196
+ it('should retrieve a workflow by name', () => {
197
+ const now = new Date().toISOString();
198
+ const definition: WorkflowDefinition = {
199
+ name: 'MyWorkflow',
200
+ version: '1.0',
201
+ nodes: [{ id: 'node1', type: 'llm' }],
202
+ };
203
+
204
+ db.prepare(`
205
+ INSERT INTO workflows (id, name, version, definition, config, metadata, tags, created_at, updated_at)
206
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
207
+ `).run(
208
+ randomUUID(),
209
+ definition.name,
210
+ definition.version,
211
+ JSON.stringify(definition),
212
+ JSON.stringify({}),
213
+ JSON.stringify({}),
214
+ JSON.stringify([]),
215
+ now,
216
+ now
217
+ );
218
+
219
+ const row = db.prepare("SELECT * FROM workflows WHERE name = ? ORDER BY updated_at DESC LIMIT 1").get('MyWorkflow') as WorkflowRow | undefined;
220
+ expect(row).toBeDefined();
221
+
222
+ const workflow = rowToWorkflow(row!);
223
+ expect(workflow.name).toBe('MyWorkflow');
224
+ });
225
+
226
+ it('should return latest version when multiple versions exist', () => {
227
+ const now = new Date().toISOString();
228
+ const definition1: WorkflowDefinition = {
229
+ name: 'MultiVersion',
230
+ version: '1.0',
231
+ nodes: [{ id: 'node1', type: 'llm' }],
232
+ };
233
+ const definition2: WorkflowDefinition = {
234
+ name: 'MultiVersion',
235
+ version: '2.0',
236
+ nodes: [{ id: 'node1', type: 'llm' }],
237
+ };
238
+
239
+ // Insert version 1.0
240
+ db.prepare(`
241
+ INSERT INTO workflows (id, name, version, definition, config, metadata, tags, created_at, updated_at)
242
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
243
+ `).run(
244
+ randomUUID(),
245
+ definition1.name,
246
+ definition1.version,
247
+ JSON.stringify(definition1),
248
+ JSON.stringify({}),
249
+ JSON.stringify({}),
250
+ JSON.stringify([]),
251
+ now,
252
+ now
253
+ );
254
+
255
+ // Insert version 2.0 (updated later)
256
+ db.prepare(`
257
+ INSERT INTO workflows (id, name, version, definition, config, metadata, tags, created_at, updated_at)
258
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
259
+ `).run(
260
+ randomUUID(),
261
+ definition2.name,
262
+ definition2.version,
263
+ JSON.stringify(definition2),
264
+ JSON.stringify({}),
265
+ JSON.stringify({}),
266
+ JSON.stringify([]),
267
+ now,
268
+ new Date(Date.now() + 1000).toISOString() // Updated later
269
+ );
270
+
271
+ const row = db.prepare("SELECT * FROM workflows WHERE name = ? ORDER BY updated_at DESC LIMIT 1").get('MultiVersion') as WorkflowRow | undefined;
272
+ expect(row).toBeDefined();
273
+
274
+ const workflow = rowToWorkflow(row!);
275
+ expect(workflow.version).toBe('2.0');
276
+ });
277
+ });
278
+
279
+ describe('list', () => {
280
+ it('should list all workflows', () => {
281
+ const now = new Date().toISOString();
282
+
283
+ // Create multiple workflows
284
+ for (let i = 0; i < 3; i++) {
285
+ const definition: WorkflowDefinition = {
286
+ name: `Workflow${i}`,
287
+ version: '1.0',
288
+ nodes: [{ id: 'node1', type: 'llm' }],
289
+ };
290
+
291
+ db.prepare(`
292
+ INSERT INTO workflows (id, name, version, definition, config, metadata, tags, created_at, updated_at)
293
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
294
+ `).run(
295
+ randomUUID(),
296
+ definition.name,
297
+ definition.version,
298
+ JSON.stringify(definition),
299
+ JSON.stringify({}),
300
+ JSON.stringify({}),
301
+ JSON.stringify([]),
302
+ now,
303
+ now
304
+ );
305
+ }
306
+
307
+ const rows = db.prepare("SELECT * FROM workflows ORDER BY updated_at DESC").all() as WorkflowRow[];
308
+ expect(rows).toHaveLength(3);
309
+ });
310
+
311
+ it('should support pagination', () => {
312
+ const now = new Date().toISOString();
313
+
314
+ // Create 5 workflows
315
+ for (let i = 0; i < 5; i++) {
316
+ const definition: WorkflowDefinition = {
317
+ name: `Workflow${i}`,
318
+ version: '1.0',
319
+ nodes: [{ id: 'node1', type: 'llm' }],
320
+ };
321
+
322
+ db.prepare(`
323
+ INSERT INTO workflows (id, name, version, definition, config, metadata, tags, created_at, updated_at)
324
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
325
+ `).run(
326
+ randomUUID(),
327
+ definition.name,
328
+ definition.version,
329
+ JSON.stringify(definition),
330
+ JSON.stringify({}),
331
+ JSON.stringify({}),
332
+ JSON.stringify([]),
333
+ now,
334
+ now
335
+ );
336
+ }
337
+
338
+ const rows = db.prepare("SELECT * FROM workflows ORDER BY updated_at DESC LIMIT 2 OFFSET 1").all() as WorkflowRow[];
339
+ expect(rows).toHaveLength(2);
340
+ });
341
+
342
+ it('should filter by tags', () => {
343
+ const now = new Date().toISOString();
344
+
345
+ // Create workflow with tag
346
+ const definition: WorkflowDefinition = {
347
+ name: 'TaggedWorkflow',
348
+ version: '1.0',
349
+ nodes: [{ id: 'node1', type: 'llm' }],
350
+ };
351
+
352
+ db.prepare(`
353
+ INSERT INTO workflows (id, name, version, definition, config, metadata, tags, created_at, updated_at)
354
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
355
+ `).run(
356
+ randomUUID(),
357
+ definition.name,
358
+ definition.version,
359
+ JSON.stringify(definition),
360
+ JSON.stringify({}),
361
+ JSON.stringify({}),
362
+ JSON.stringify(['test', 'workflow']),
363
+ now,
364
+ now
365
+ );
366
+
367
+ // Query using LIKE for tag search (stored as JSON array)
368
+ const rows = db.prepare("SELECT * FROM workflows WHERE tags LIKE ?").all('%test%') as WorkflowRow[];
369
+ expect(rows).toHaveLength(1);
370
+ expect(rowToWorkflow(rows[0]).tags).toContain('test');
371
+ });
372
+ });
373
+
374
+ describe('update', () => {
375
+ it('should update a workflow', () => {
376
+ const id = randomUUID();
377
+ const now = new Date().toISOString();
378
+ const definition: WorkflowDefinition = {
379
+ name: 'Original',
380
+ version: '1.0',
381
+ nodes: [{ id: 'node1', type: 'llm' }],
382
+ };
383
+
384
+ db.prepare(`
385
+ INSERT INTO workflows (id, name, version, definition, config, metadata, tags, created_at, updated_at)
386
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
387
+ `).run(
388
+ id,
389
+ definition.name,
390
+ definition.version,
391
+ JSON.stringify(definition),
392
+ JSON.stringify({}),
393
+ JSON.stringify({}),
394
+ JSON.stringify([]),
395
+ now,
396
+ now
397
+ );
398
+
399
+ // Update the workflow
400
+ const updatedNow = new Date().toISOString();
401
+ const updatedDefinition: WorkflowDefinition = {
402
+ ...definition,
403
+ name: 'Updated',
404
+ };
405
+
406
+ db.prepare(`
407
+ UPDATE workflows
408
+ SET name = ?, definition = ?, updated_at = ?
409
+ WHERE id = ?
410
+ `).run(
411
+ updatedDefinition.name,
412
+ JSON.stringify(updatedDefinition),
413
+ updatedNow,
414
+ id
415
+ );
416
+
417
+ const row = db.prepare("SELECT * FROM workflows WHERE id = ?").get(id) as WorkflowRow;
418
+ const workflow = rowToWorkflow(row);
419
+
420
+ expect(workflow.name).toBe('Updated');
421
+ expect(workflow.updatedAt).toEqual(new Date(updatedNow));
422
+ });
423
+
424
+ it('should update only specific fields', () => {
425
+ const id = randomUUID();
426
+ const now = new Date().toISOString();
427
+ const definition: WorkflowDefinition = {
428
+ name: 'TestWorkflow',
429
+ version: '1.0',
430
+ nodes: [{ id: 'node1', type: 'llm' }],
431
+ };
432
+
433
+ db.prepare(`
434
+ INSERT INTO workflows (id, name, version, definition, config, metadata, tags, created_at, updated_at)
435
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
436
+ `).run(
437
+ id,
438
+ definition.name,
439
+ definition.version,
440
+ JSON.stringify(definition),
441
+ JSON.stringify({}),
442
+ JSON.stringify({}),
443
+ JSON.stringify([]),
444
+ now,
445
+ now
446
+ );
447
+
448
+ // Update only tags
449
+ db.prepare(`
450
+ UPDATE workflows
451
+ SET tags = ?, updated_at = ?
452
+ WHERE id = ?
453
+ `).run(
454
+ JSON.stringify(['new-tag']),
455
+ new Date().toISOString(),
456
+ id
457
+ );
458
+
459
+ const row = db.prepare("SELECT * FROM workflows WHERE id = ?").get(id) as WorkflowRow;
460
+ const workflow = rowToWorkflow(row);
461
+
462
+ expect(workflow.name).toBe('TestWorkflow'); // Unchanged
463
+ expect(workflow.tags).toContain('new-tag');
464
+ });
465
+ });
466
+
467
+ describe('delete', () => {
468
+ it('should delete a workflow', () => {
469
+ const id = randomUUID();
470
+ const now = new Date().toISOString();
471
+ const definition: WorkflowDefinition = {
472
+ name: 'ToDelete',
473
+ version: '1.0',
474
+ nodes: [{ id: 'node1', type: 'llm' }],
475
+ };
476
+
477
+ db.prepare(`
478
+ INSERT INTO workflows (id, name, version, definition, config, metadata, tags, created_at, updated_at)
479
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
480
+ `).run(
481
+ id,
482
+ definition.name,
483
+ definition.version,
484
+ JSON.stringify(definition),
485
+ JSON.stringify({}),
486
+ JSON.stringify({}),
487
+ JSON.stringify([]),
488
+ now,
489
+ now
490
+ );
491
+
492
+ // Delete the workflow
493
+ db.prepare("DELETE FROM workflows WHERE id = ?").run(id);
494
+
495
+ const row = db.prepare("SELECT * FROM workflows WHERE id = ?").get(id);
496
+ expect(row).toBeNull();
497
+ });
498
+
499
+ it('should not affect other workflows when deleting one', () => {
500
+ const now = new Date().toISOString();
501
+ const definition: WorkflowDefinition = {
502
+ name: 'Test',
503
+ version: '1.0',
504
+ nodes: [{ id: 'node1', type: 'llm' }],
505
+ };
506
+
507
+ const id1 = randomUUID();
508
+ const id2 = randomUUID();
509
+
510
+ db.prepare(`
511
+ INSERT INTO workflows (id, name, version, definition, config, metadata, tags, created_at, updated_at)
512
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
513
+ `).run(id1, definition.name, definition.version, JSON.stringify(definition), JSON.stringify({}), JSON.stringify({}), JSON.stringify([]), now, now);
514
+
515
+ db.prepare(`
516
+ INSERT INTO workflows (id, name, version, definition, config, metadata, tags, created_at, updated_at)
517
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
518
+ `).run(id2, definition.name, definition.version, JSON.stringify(definition), JSON.stringify({}), JSON.stringify({}), JSON.stringify([]), now, now);
519
+
520
+ // Delete one
521
+ db.prepare("DELETE FROM workflows WHERE id = ?").run(id1);
522
+
523
+ const rows = db.prepare("SELECT * FROM workflows").all();
524
+ expect(rows).toHaveLength(1);
525
+ expect((rows[0] as WorkflowRow).id).toBe(id2);
526
+ });
527
+ });
528
+
529
+ describe('exists', () => {
530
+ it('should return true for existing workflow', () => {
531
+ const id = randomUUID();
532
+ const now = new Date().toISOString();
533
+ const definition: WorkflowDefinition = {
534
+ name: 'Exists',
535
+ version: '1.0',
536
+ nodes: [{ id: 'node1', type: 'llm' }],
537
+ };
538
+
539
+ db.prepare(`
540
+ INSERT INTO workflows (id, name, version, definition, config, metadata, tags, created_at, updated_at)
541
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
542
+ `).run(
543
+ id,
544
+ definition.name,
545
+ definition.version,
546
+ JSON.stringify(definition),
547
+ JSON.stringify({}),
548
+ JSON.stringify({}),
549
+ JSON.stringify([]),
550
+ now,
551
+ now
552
+ );
553
+
554
+ const count = db.prepare("SELECT COUNT(*) as count FROM workflows WHERE id = ?").get(id) as { count: number };
555
+ expect(count.count).toBeGreaterThan(0);
556
+ });
557
+
558
+ it('should return false for non-existing workflow', () => {
559
+ const count = db.prepare("SELECT COUNT(*) as count FROM workflows WHERE id = ?").get('non-existent') as { count: number };
560
+ expect(count.count).toBe(0);
561
+ });
562
+ });
563
+
564
+ describe('list() with taskId filter', () => {
565
+ it('should filter workflows by taskId', () => {
566
+ const now = new Date().toISOString();
567
+
568
+ // Create workflows with different taskIds
569
+ const definition: WorkflowDefinition = {
570
+ name: 'Workflow1',
571
+ version: '1.0',
572
+ nodes: [{ id: 'node1', type: 'llm' }],
573
+ };
574
+
575
+ // Insert with task_id = 1
576
+ db.prepare(`
577
+ INSERT INTO workflows (id, name, version, definition, config, metadata, tags, task_id, created_at, updated_at)
578
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
579
+ `).run(
580
+ randomUUID(),
581
+ definition.name,
582
+ definition.version,
583
+ JSON.stringify(definition),
584
+ JSON.stringify({}),
585
+ JSON.stringify({}),
586
+ JSON.stringify([]),
587
+ 1,
588
+ now,
589
+ now
590
+ );
591
+
592
+ // Insert with task_id = 2
593
+ db.prepare(`
594
+ INSERT INTO workflows (id, name, version, definition, config, metadata, tags, task_id, created_at, updated_at)
595
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
596
+ `).run(
597
+ randomUUID(),
598
+ 'Workflow2',
599
+ definition.version,
600
+ JSON.stringify(definition),
601
+ JSON.stringify({}),
602
+ JSON.stringify({}),
603
+ JSON.stringify([]),
604
+ 2,
605
+ now,
606
+ now
607
+ );
608
+
609
+ // Query with task_id = 1
610
+ const rows = db.prepare("SELECT * FROM workflows WHERE task_id = ?").all(1) as WorkflowRow[];
611
+ expect(rows).toHaveLength(1);
612
+ expect(rows[0].name).toBe('Workflow1');
613
+ });
614
+ });
615
+
616
+ describe('list() with search', () => {
617
+ it('should search in description', () => {
618
+ const now = new Date().toISOString();
619
+ const definition: WorkflowDefinition = {
620
+ name: 'Workflow1',
621
+ version: '1.0',
622
+ nodes: [{ id: 'node1', type: 'llm' }],
623
+ };
624
+
625
+ // Create workflow with CI description
626
+ db.prepare(`
627
+ INSERT INTO workflows (id, name, version, description, definition, config, metadata, tags, created_at, updated_at)
628
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
629
+ `).run(
630
+ randomUUID(),
631
+ definition.name,
632
+ definition.version,
633
+ 'CI pipeline workflow',
634
+ JSON.stringify(definition),
635
+ JSON.stringify({}),
636
+ JSON.stringify({}),
637
+ JSON.stringify([]),
638
+ now,
639
+ now
640
+ );
641
+
642
+ // Create workflow with Data description
643
+ db.prepare(`
644
+ INSERT INTO workflows (id, name, version, description, definition, config, metadata, tags, created_at, updated_at)
645
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
646
+ `).run(
647
+ randomUUID(),
648
+ 'Workflow2',
649
+ definition.version,
650
+ 'Data processing workflow',
651
+ JSON.stringify(definition),
652
+ JSON.stringify({}),
653
+ JSON.stringify({}),
654
+ JSON.stringify([]),
655
+ now,
656
+ now
657
+ );
658
+
659
+ // Search in-memory for CI
660
+ const allRows = db.prepare("SELECT * FROM workflows").all() as WorkflowRow[];
661
+ const workflows = allRows.map(rowToWorkflow);
662
+ const ciWorkflows = workflows.filter(w => w.description?.toLowerCase().includes('ci'));
663
+
664
+ expect(ciWorkflows).toHaveLength(1);
665
+ expect(ciWorkflows[0].description).toContain('CI');
666
+ });
667
+
668
+ it('should search in tags', () => {
669
+ const now = new Date().toISOString();
670
+ const definition: WorkflowDefinition = {
671
+ name: 'TaggedWorkflow',
672
+ version: '1.0',
673
+ nodes: [{ id: 'node1', type: 'llm' }],
674
+ };
675
+
676
+ db.prepare(`
677
+ INSERT INTO workflows (id, name, version, definition, config, metadata, tags, created_at, updated_at)
678
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
679
+ `).run(
680
+ randomUUID(),
681
+ definition.name,
682
+ definition.version,
683
+ JSON.stringify(definition),
684
+ JSON.stringify({}),
685
+ JSON.stringify({}),
686
+ JSON.stringify(['ci', 'deploy']),
687
+ now,
688
+ now
689
+ );
690
+
691
+ // Search in-memory for 'ci' tag
692
+ const allRows = db.prepare("SELECT * FROM workflows").all() as WorkflowRow[];
693
+ const workflows = allRows.map(rowToWorkflow);
694
+ const ciWorkflows = workflows.filter(w => w.tags.some(tag => tag.toLowerCase().includes('ci')));
695
+
696
+ expect(ciWorkflows).toHaveLength(1);
697
+ expect(ciWorkflows[0].tags).toContain('ci');
698
+ });
699
+ });
700
+
701
+ describe('list() with combined filters', () => {
702
+ it('should combine taskId + tag + search', () => {
703
+ const now = new Date().toISOString();
704
+ const definition: WorkflowDefinition = {
705
+ name: 'Workflow1',
706
+ version: '1.0',
707
+ nodes: [{ id: 'node1', type: 'llm' }],
708
+ };
709
+
710
+ // Create workflow 1: taskId=1, tag=ci, description=Build
711
+ db.prepare(`
712
+ INSERT INTO workflows (id, name, version, description, definition, config, metadata, tags, task_id, created_at, updated_at)
713
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
714
+ `).run(
715
+ randomUUID(),
716
+ definition.name,
717
+ definition.version,
718
+ 'Build workflow',
719
+ JSON.stringify(definition),
720
+ JSON.stringify({}),
721
+ JSON.stringify({}),
722
+ JSON.stringify(['ci']),
723
+ 1,
724
+ now,
725
+ now
726
+ );
727
+
728
+ // Create workflow 2: taskId=1, tag=ci, description=Deploy
729
+ db.prepare(`
730
+ INSERT INTO workflows (id, name, version, description, definition, config, metadata, tags, task_id, created_at, updated_at)
731
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
732
+ `).run(
733
+ randomUUID(),
734
+ 'Workflow2',
735
+ definition.version,
736
+ 'Deploy workflow',
737
+ JSON.stringify(definition),
738
+ JSON.stringify({}),
739
+ JSON.stringify({}),
740
+ JSON.stringify(['ci']),
741
+ 1,
742
+ now,
743
+ now
744
+ );
745
+
746
+ // Create workflow 3: taskId=2, tag=ci, description=Test
747
+ db.prepare(`
748
+ INSERT INTO workflows (id, name, version, description, definition, config, metadata, tags, task_id, created_at, updated_at)
749
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
750
+ `).run(
751
+ randomUUID(),
752
+ 'Workflow3',
753
+ definition.version,
754
+ 'Test workflow',
755
+ JSON.stringify(definition),
756
+ JSON.stringify({}),
757
+ JSON.stringify({}),
758
+ JSON.stringify(['ci']),
759
+ 2,
760
+ now,
761
+ now
762
+ );
763
+
764
+ // Query: taskId=1 AND tag=ci AND search=build
765
+ let rows = db.prepare("SELECT * FROM workflows WHERE task_id = ?").all(1) as WorkflowRow[];
766
+ let workflows = rows.map(rowToWorkflow);
767
+
768
+ // Filter by tag
769
+ workflows = workflows.filter(w => w.tags.includes('ci'));
770
+
771
+ // Filter by search
772
+ workflows = workflows.filter(w => w.description?.toLowerCase().includes('build'));
773
+
774
+ expect(workflows).toHaveLength(1);
775
+ expect(workflows[0].metadata.taskId).toBe(1);
776
+ expect(workflows[0].tags).toContain('ci');
777
+ expect(workflows[0].description).toContain('Build');
778
+ });
779
+ });
780
+ });