@pikku/core 0.11.2 → 0.12.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 (344) hide show
  1. package/CHANGELOG.md +33 -2
  2. package/dist/crypto-utils.d.ts +2 -0
  3. package/dist/crypto-utils.js +78 -0
  4. package/dist/errors/errors.d.ts +8 -0
  5. package/dist/errors/errors.js +16 -0
  6. package/dist/errors/index.d.ts +0 -1
  7. package/dist/errors/index.js +0 -1
  8. package/dist/function/function-runner.d.ts +7 -1
  9. package/dist/function/function-runner.js +82 -23
  10. package/dist/function/functions.types.d.ts +31 -87
  11. package/dist/function/functions.types.js +16 -46
  12. package/dist/function/index.d.ts +2 -2
  13. package/dist/function/index.js +1 -2
  14. package/dist/handle-error.js +1 -10
  15. package/dist/index.d.ts +28 -26
  16. package/dist/index.js +18 -29
  17. package/dist/middleware/auth-apikey.d.ts +2 -2
  18. package/dist/middleware/auth-apikey.js +3 -3
  19. package/dist/middleware/auth-bearer.d.ts +2 -2
  20. package/dist/middleware/auth-bearer.js +3 -6
  21. package/dist/middleware/auth-cookie.d.ts +2 -2
  22. package/dist/middleware/auth-cookie.js +6 -8
  23. package/dist/middleware/cors.d.ts +46 -0
  24. package/dist/middleware/cors.js +73 -0
  25. package/dist/middleware/index.d.ts +5 -3
  26. package/dist/middleware/index.js +5 -3
  27. package/dist/middleware/remote-auth.d.ts +5 -0
  28. package/dist/middleware/remote-auth.js +55 -0
  29. package/dist/middleware/timeout.d.ts +2 -2
  30. package/dist/middleware-runner.js +8 -10
  31. package/dist/permissions.js +11 -15
  32. package/dist/pikku-state.d.ts +1 -0
  33. package/dist/pikku-state.js +18 -8
  34. package/dist/schema.js +12 -11
  35. package/dist/services/ai-agent-runner-service.d.ts +24 -0
  36. package/dist/services/ai-agent-runner-service.js +1 -0
  37. package/dist/services/ai-run-state-service.d.ts +13 -0
  38. package/dist/services/ai-run-state-service.js +1 -0
  39. package/dist/services/ai-storage-service.d.ts +18 -0
  40. package/dist/services/ai-storage-service.js +1 -0
  41. package/dist/services/deployment-service.d.ts +19 -0
  42. package/dist/services/deployment-service.js +1 -0
  43. package/dist/services/gopass-secrets.d.ts +47 -0
  44. package/dist/services/gopass-secrets.js +94 -0
  45. package/dist/services/in-memory-trigger-service.d.ts +23 -0
  46. package/dist/services/in-memory-trigger-service.js +50 -0
  47. package/dist/services/in-memory-workflow-service.d.ts +59 -0
  48. package/dist/services/in-memory-workflow-service.js +287 -0
  49. package/dist/services/index.d.ts +25 -11
  50. package/dist/services/index.js +10 -13
  51. package/dist/services/local-secrets.d.ts +23 -0
  52. package/dist/services/local-secrets.js +45 -0
  53. package/dist/services/local-variables.d.ts +5 -0
  54. package/dist/services/local-variables.js +18 -0
  55. package/dist/services/scheduler-service.d.ts +21 -1
  56. package/dist/services/scheduler-service.js +14 -0
  57. package/dist/services/schema-service.d.ts +6 -0
  58. package/dist/services/scoped-secret-service.d.ts +16 -0
  59. package/dist/services/scoped-secret-service.js +35 -0
  60. package/dist/services/secret-service.d.ts +25 -4
  61. package/dist/services/trigger-service.d.ts +23 -0
  62. package/dist/services/trigger-service.js +1 -0
  63. package/dist/services/typed-secret-service.d.ts +30 -0
  64. package/dist/services/typed-secret-service.js +40 -0
  65. package/dist/services/typed-variables-service.d.ts +27 -0
  66. package/dist/services/typed-variables-service.js +46 -0
  67. package/dist/services/user-session-service.d.ts +13 -0
  68. package/dist/services/user-session-service.js +17 -0
  69. package/dist/services/variables-service.d.ts +5 -0
  70. package/dist/services/workflow-service.d.ts +9 -4
  71. package/dist/types/core.types.d.ts +48 -12
  72. package/dist/types/core.types.js +8 -1
  73. package/dist/types/state.types.d.ts +43 -51
  74. package/dist/utils.d.ts +3 -2
  75. package/dist/utils.js +15 -3
  76. package/dist/version.d.ts +6 -0
  77. package/dist/version.js +19 -0
  78. package/dist/wirings/ai-agent/ai-agent-memory.d.ts +29 -0
  79. package/dist/wirings/ai-agent/ai-agent-memory.js +199 -0
  80. package/dist/wirings/ai-agent/ai-agent-model-config.d.ts +9 -0
  81. package/dist/wirings/ai-agent/ai-agent-model-config.js +36 -0
  82. package/dist/wirings/ai-agent/ai-agent-prepare.d.ts +60 -0
  83. package/dist/wirings/ai-agent/ai-agent-prepare.js +312 -0
  84. package/dist/wirings/ai-agent/ai-agent-registry.d.ts +15 -0
  85. package/dist/wirings/ai-agent/ai-agent-registry.js +47 -0
  86. package/dist/wirings/ai-agent/ai-agent-runner.d.ts +3 -0
  87. package/dist/wirings/ai-agent/ai-agent-runner.js +96 -0
  88. package/dist/wirings/ai-agent/ai-agent-stream.d.ts +11 -0
  89. package/dist/wirings/ai-agent/ai-agent-stream.js +503 -0
  90. package/dist/wirings/ai-agent/ai-agent.types.d.ts +254 -0
  91. package/dist/wirings/ai-agent/ai-agent.types.js +1 -0
  92. package/dist/wirings/ai-agent/index.d.ts +5 -0
  93. package/dist/wirings/ai-agent/index.js +4 -0
  94. package/dist/wirings/channel/channel-common.d.ts +7 -4
  95. package/dist/wirings/channel/channel-common.js +15 -8
  96. package/dist/wirings/channel/channel-handler.js +12 -3
  97. package/dist/wirings/channel/channel-middleware-runner.d.ts +9 -0
  98. package/dist/wirings/channel/channel-middleware-runner.js +73 -0
  99. package/dist/wirings/channel/channel-runner.js +7 -7
  100. package/dist/wirings/channel/channel.types.d.ts +5 -1
  101. package/dist/wirings/channel/define-channel-routes.d.ts +21 -0
  102. package/dist/wirings/channel/define-channel-routes.js +23 -0
  103. package/dist/wirings/channel/index.d.ts +10 -8
  104. package/dist/wirings/channel/index.js +7 -8
  105. package/dist/wirings/channel/local/index.d.ts +3 -3
  106. package/dist/wirings/channel/local/index.js +3 -3
  107. package/dist/wirings/channel/local/local-channel-runner.js +18 -6
  108. package/dist/wirings/channel/serverless/index.d.ts +1 -1
  109. package/dist/wirings/channel/serverless/index.js +1 -1
  110. package/dist/wirings/channel/serverless/serverless-channel-runner.js +15 -4
  111. package/dist/wirings/cli/channel/index.d.ts +1 -1
  112. package/dist/wirings/cli/channel/index.js +1 -1
  113. package/dist/wirings/cli/cli-runner.d.ts +1 -1
  114. package/dist/wirings/cli/cli-runner.js +12 -8
  115. package/dist/wirings/cli/cli.types.d.ts +6 -6
  116. package/dist/wirings/cli/define-cli-commands.d.ts +19 -0
  117. package/dist/wirings/cli/define-cli-commands.js +20 -0
  118. package/dist/wirings/cli/index.d.ts +2 -3
  119. package/dist/wirings/cli/index.js +1 -3
  120. package/dist/wirings/http/http-routes.d.ts +69 -0
  121. package/dist/wirings/http/http-routes.js +139 -0
  122. package/dist/wirings/http/http-runner.js +49 -23
  123. package/dist/wirings/http/http.types.d.ts +78 -38
  124. package/dist/wirings/http/index.d.ts +5 -4
  125. package/dist/wirings/http/index.js +4 -3
  126. package/dist/wirings/mcp/index.d.ts +4 -3
  127. package/dist/wirings/mcp/index.js +3 -3
  128. package/dist/wirings/mcp/mcp-runner.d.ts +1 -3
  129. package/dist/wirings/mcp/mcp-runner.js +25 -39
  130. package/dist/wirings/mcp/mcp.types.d.ts +3 -3
  131. package/dist/wirings/node/index.d.ts +2 -0
  132. package/dist/wirings/node/index.js +1 -0
  133. package/dist/wirings/node/node.types.d.ts +20 -0
  134. package/dist/wirings/node/node.types.js +1 -0
  135. package/dist/wirings/oauth2/index.d.ts +3 -0
  136. package/dist/wirings/oauth2/index.js +2 -0
  137. package/dist/wirings/oauth2/oauth2-client.d.ts +47 -0
  138. package/dist/wirings/oauth2/oauth2-client.js +267 -0
  139. package/dist/wirings/oauth2/oauth2.types.d.ts +65 -0
  140. package/dist/wirings/oauth2/oauth2.types.js +4 -0
  141. package/dist/wirings/oauth2/wire-oauth2-credential.d.ts +20 -0
  142. package/dist/wirings/oauth2/wire-oauth2-credential.js +21 -0
  143. package/dist/wirings/queue/index.d.ts +2 -2
  144. package/dist/wirings/queue/index.js +1 -1
  145. package/dist/wirings/queue/queue-runner.d.ts +7 -0
  146. package/dist/wirings/queue/queue-runner.js +17 -6
  147. package/dist/wirings/queue/queue.types.d.ts +8 -2
  148. package/dist/wirings/rpc/index.d.ts +1 -1
  149. package/dist/wirings/rpc/index.js +1 -1
  150. package/dist/wirings/rpc/rpc-runner.d.ts +33 -5
  151. package/dist/wirings/rpc/rpc-runner.js +134 -16
  152. package/dist/wirings/rpc/rpc-types.d.ts +8 -4
  153. package/dist/wirings/scheduler/index.d.ts +3 -3
  154. package/dist/wirings/scheduler/index.js +2 -3
  155. package/dist/wirings/scheduler/scheduler-runner.d.ts +5 -0
  156. package/dist/wirings/scheduler/scheduler-runner.js +23 -5
  157. package/dist/wirings/secret/index.d.ts +3 -0
  158. package/dist/wirings/secret/index.js +2 -0
  159. package/dist/wirings/secret/secret.types.d.ts +27 -0
  160. package/dist/wirings/secret/secret.types.js +1 -0
  161. package/dist/wirings/secret/validate-secret-definitions.d.ts +6 -0
  162. package/dist/wirings/secret/validate-secret-definitions.js +55 -0
  163. package/dist/wirings/trigger/index.d.ts +3 -2
  164. package/dist/wirings/trigger/index.js +2 -2
  165. package/dist/wirings/trigger/pikku-trigger-service.d.ts +26 -0
  166. package/dist/wirings/trigger/pikku-trigger-service.js +75 -0
  167. package/dist/wirings/trigger/trigger-runner.d.ts +21 -8
  168. package/dist/wirings/trigger/trigger-runner.js +46 -18
  169. package/dist/wirings/trigger/trigger.types.d.ts +99 -11
  170. package/dist/wirings/trigger/trigger.types.js +40 -1
  171. package/dist/wirings/variable/index.d.ts +3 -0
  172. package/dist/wirings/variable/index.js +2 -0
  173. package/dist/wirings/variable/validate-variable-definitions.d.ts +6 -0
  174. package/dist/wirings/variable/validate-variable-definitions.js +45 -0
  175. package/dist/wirings/variable/variable.types.d.ts +18 -0
  176. package/dist/wirings/variable/variable.types.js +1 -0
  177. package/dist/wirings/workflow/dsl/index.d.ts +1 -1
  178. package/dist/wirings/workflow/dsl/workflow-dsl.types.d.ts +7 -1
  179. package/dist/wirings/workflow/dsl/workflow-runner.js +3 -2
  180. package/dist/wirings/workflow/graph/graph-node.d.ts +1 -6
  181. package/dist/wirings/workflow/graph/graph-runner.d.ts +5 -31
  182. package/dist/wirings/workflow/graph/graph-runner.js +384 -296
  183. package/dist/wirings/workflow/graph/index.d.ts +4 -3
  184. package/dist/wirings/workflow/graph/index.js +3 -3
  185. package/dist/wirings/workflow/graph/template.d.ts +15 -0
  186. package/dist/wirings/workflow/graph/template.js +27 -0
  187. package/dist/wirings/workflow/graph/wire-workflow-graph.d.ts +15 -0
  188. package/dist/wirings/workflow/graph/wire-workflow-graph.js +8 -0
  189. package/dist/wirings/workflow/graph/workflow-graph.types.d.ts +0 -13
  190. package/dist/wirings/workflow/index.d.ts +6 -6
  191. package/dist/wirings/workflow/index.js +6 -4
  192. package/dist/wirings/workflow/pikku-workflow-service.d.ts +28 -14
  193. package/dist/wirings/workflow/pikku-workflow-service.js +200 -56
  194. package/dist/wirings/workflow/workflow-helpers.d.ts +36 -0
  195. package/dist/wirings/workflow/workflow-helpers.js +38 -0
  196. package/dist/wirings/workflow/workflow-utils.d.ts +1 -23
  197. package/dist/wirings/workflow/workflow-utils.js +1 -66
  198. package/dist/wirings/workflow/workflow.types.d.ts +40 -93
  199. package/package.json +13 -15
  200. package/run-tests.sh +4 -1
  201. package/src/crypto-utils.ts +99 -0
  202. package/src/errors/errors.ts +17 -0
  203. package/src/errors/index.ts +0 -1
  204. package/src/factory-functions.test.ts +42 -2
  205. package/src/function/function-runner.test.ts +4 -11
  206. package/src/function/function-runner.ts +114 -29
  207. package/src/function/functions.types.ts +62 -132
  208. package/src/function/index.ts +5 -2
  209. package/src/handle-error.ts +2 -10
  210. package/src/index.ts +99 -25
  211. package/src/middleware/auth-apikey.test.ts +15 -12
  212. package/src/middleware/auth-apikey.ts +3 -3
  213. package/src/middleware/auth-bearer.test.ts +17 -14
  214. package/src/middleware/auth-bearer.ts +25 -27
  215. package/src/middleware/auth-cookie.test.ts +16 -13
  216. package/src/middleware/auth-cookie.ts +6 -8
  217. package/src/middleware/cors.ts +96 -0
  218. package/src/middleware/index.ts +5 -3
  219. package/src/middleware/remote-auth.ts +68 -0
  220. package/src/middleware-runner.test.ts +122 -0
  221. package/src/middleware-runner.ts +12 -12
  222. package/src/permissions.test.ts +81 -7
  223. package/src/permissions.ts +15 -17
  224. package/src/pikku-state.ts +20 -8
  225. package/src/run-tests-script.test.ts +49 -0
  226. package/src/schema.ts +18 -15
  227. package/src/services/ai-agent-runner-service.ts +29 -0
  228. package/src/services/ai-run-state-service.ts +20 -0
  229. package/src/services/ai-storage-service.ts +39 -0
  230. package/src/services/deployment-service.ts +22 -0
  231. package/src/services/gopass-secrets.ts +95 -0
  232. package/src/services/in-memory-trigger-service.ts +68 -0
  233. package/src/services/in-memory-workflow-service.ts +395 -0
  234. package/src/services/index.ts +49 -14
  235. package/src/services/local-secrets.ts +51 -0
  236. package/src/services/local-variables.ts +22 -0
  237. package/src/services/scheduler-service.ts +29 -1
  238. package/src/services/schema-service.ts +7 -0
  239. package/src/services/scoped-secret-service.ts +41 -0
  240. package/src/services/secret-service.ts +25 -4
  241. package/src/services/trigger-service.ts +38 -0
  242. package/src/services/typed-secret-service.ts +70 -0
  243. package/src/services/typed-variables-service.ts +81 -0
  244. package/src/services/user-session-service.ts +23 -0
  245. package/src/services/variables-service.ts +5 -0
  246. package/src/services/workflow-service.ts +22 -2
  247. package/src/types/core.types.ts +76 -11
  248. package/src/types/state.types.ts +37 -63
  249. package/src/utils.ts +20 -6
  250. package/src/version.test.ts +80 -0
  251. package/src/version.ts +25 -0
  252. package/src/wirings/ai-agent/ai-agent-memory.ts +300 -0
  253. package/src/wirings/ai-agent/ai-agent-model-config.ts +43 -0
  254. package/src/wirings/ai-agent/ai-agent-prepare.ts +470 -0
  255. package/src/wirings/ai-agent/ai-agent-registry.test.ts +37 -0
  256. package/src/wirings/ai-agent/ai-agent-registry.ts +82 -0
  257. package/src/wirings/ai-agent/ai-agent-runner.test.ts +75 -0
  258. package/src/wirings/ai-agent/ai-agent-runner.ts +137 -0
  259. package/src/wirings/ai-agent/ai-agent-stream.test.ts +246 -0
  260. package/src/wirings/ai-agent/ai-agent-stream.ts +774 -0
  261. package/src/wirings/ai-agent/ai-agent.types.ts +273 -0
  262. package/src/wirings/ai-agent/index.ts +28 -0
  263. package/src/wirings/channel/channel-common.ts +32 -19
  264. package/src/wirings/channel/channel-handler.ts +17 -4
  265. package/src/wirings/channel/channel-middleware-runner.ts +119 -0
  266. package/src/wirings/channel/channel-runner.ts +9 -10
  267. package/src/wirings/channel/channel.types.ts +17 -1
  268. package/src/wirings/channel/define-channel-routes.ts +25 -0
  269. package/src/wirings/channel/index.ts +23 -8
  270. package/src/wirings/channel/local/index.ts +3 -3
  271. package/src/wirings/channel/local/local-channel-runner.test.ts +44 -1
  272. package/src/wirings/channel/local/local-channel-runner.ts +22 -6
  273. package/src/wirings/channel/serverless/index.ts +5 -1
  274. package/src/wirings/channel/serverless/serverless-channel-runner.ts +20 -4
  275. package/src/wirings/cli/channel/index.ts +1 -1
  276. package/src/wirings/cli/cli-runner.test.ts +9 -9
  277. package/src/wirings/cli/cli-runner.ts +17 -10
  278. package/src/wirings/cli/cli.types.ts +11 -11
  279. package/src/wirings/cli/command-parser.test.ts +8 -8
  280. package/src/wirings/cli/define-cli-commands.ts +24 -0
  281. package/src/wirings/cli/index.ts +10 -4
  282. package/src/wirings/http/http-routes.test.ts +322 -0
  283. package/src/wirings/http/http-routes.ts +198 -0
  284. package/src/wirings/http/http-runner.test.ts +3 -3
  285. package/src/wirings/http/http-runner.ts +63 -29
  286. package/src/wirings/http/http.types.ts +121 -37
  287. package/src/wirings/http/index.ts +16 -4
  288. package/src/wirings/mcp/index.ts +27 -3
  289. package/src/wirings/mcp/mcp-endpoint-registry.test.ts +3 -3
  290. package/src/wirings/mcp/mcp-runner.ts +39 -50
  291. package/src/wirings/mcp/mcp.types.ts +3 -3
  292. package/src/wirings/node/index.ts +2 -0
  293. package/src/wirings/node/node.types.ts +23 -0
  294. package/src/wirings/oauth2/index.ts +3 -0
  295. package/src/wirings/oauth2/oauth2-client.test.ts +929 -0
  296. package/src/wirings/oauth2/oauth2-client.ts +335 -0
  297. package/src/wirings/oauth2/oauth2.types.ts +69 -0
  298. package/src/wirings/oauth2/wire-oauth2-credential.ts +23 -0
  299. package/src/wirings/queue/index.ts +14 -2
  300. package/src/wirings/queue/queue-runner.test.ts +97 -55
  301. package/src/wirings/queue/queue-runner.ts +33 -7
  302. package/src/wirings/queue/queue.types.ts +12 -2
  303. package/src/wirings/rpc/index.ts +1 -1
  304. package/src/wirings/rpc/rpc-runner.ts +187 -17
  305. package/src/wirings/rpc/rpc-types.ts +13 -4
  306. package/src/wirings/scheduler/index.ts +7 -4
  307. package/src/wirings/scheduler/scheduler-runner.test.ts +70 -26
  308. package/src/wirings/scheduler/scheduler-runner.ts +45 -5
  309. package/src/wirings/secret/index.ts +9 -0
  310. package/src/wirings/secret/secret.types.ts +32 -0
  311. package/src/wirings/secret/validate-secret-definitions.ts +82 -0
  312. package/src/wirings/trigger/index.ts +10 -2
  313. package/src/wirings/trigger/pikku-trigger-service.ts +134 -0
  314. package/src/wirings/trigger/trigger-runner.ts +80 -38
  315. package/src/wirings/trigger/trigger.types.ts +144 -22
  316. package/src/wirings/variable/index.ts +8 -0
  317. package/src/wirings/variable/validate-variable-definitions.ts +69 -0
  318. package/src/wirings/variable/variable.types.ts +22 -0
  319. package/src/wirings/workflow/dsl/index.ts +1 -0
  320. package/src/wirings/workflow/dsl/workflow-dsl.types.ts +9 -1
  321. package/src/wirings/workflow/dsl/workflow-runner.ts +4 -3
  322. package/src/wirings/workflow/graph/graph-node.ts +1 -6
  323. package/src/wirings/workflow/graph/graph-runner.test.ts +495 -0
  324. package/src/wirings/workflow/graph/graph-runner.ts +496 -374
  325. package/src/wirings/workflow/graph/index.ts +14 -3
  326. package/src/wirings/workflow/graph/template.ts +42 -0
  327. package/src/wirings/workflow/graph/wire-workflow-graph.ts +29 -0
  328. package/src/wirings/workflow/graph/workflow-graph.types.ts +0 -22
  329. package/src/wirings/workflow/index.ts +21 -10
  330. package/src/wirings/workflow/pikku-workflow-service.test.ts +212 -0
  331. package/src/wirings/workflow/pikku-workflow-service.ts +285 -66
  332. package/src/wirings/workflow/workflow-helpers.ts +79 -0
  333. package/src/wirings/workflow/workflow-utils.ts +0 -120
  334. package/src/wirings/workflow/workflow.types.ts +45 -90
  335. package/tsconfig.tsbuildinfo +1 -1
  336. package/dist/wirings/forge-node/forge-node.types.d.ts +0 -120
  337. package/dist/wirings/forge-node/forge-node.types.js +0 -38
  338. package/dist/wirings/forge-node/index.d.ts +0 -1
  339. package/dist/wirings/forge-node/index.js +0 -1
  340. package/dist/wirings/workflow/wire-workflow.d.ts +0 -42
  341. package/dist/wirings/workflow/wire-workflow.js +0 -53
  342. package/src/wirings/forge-node/forge-node.types.ts +0 -135
  343. package/src/wirings/forge-node/index.ts +0 -1
  344. package/src/wirings/workflow/wire-workflow.ts +0 -94
@@ -13,13 +13,20 @@ import type {
13
13
  PikkuWorkflowWire,
14
14
  StepState,
15
15
  WorkflowRun,
16
+ WorkflowRunWire,
16
17
  WorkflowStatus,
17
18
  WorkflowServiceConfig,
18
19
  WorkflowStepOptions,
19
20
  } from './workflow.types.js'
20
- import { executeGraphStep, runWorkflowGraph } from './graph/graph-runner.js'
21
+ import {
22
+ continueGraph,
23
+ executeGraphStep,
24
+ runWorkflowGraph,
25
+ runFromMeta,
26
+ } from './graph/graph-runner.js'
21
27
  import { WorkflowService } from '../../services/workflow-service.js'
22
- import { PikkuError } from '../../errors/error-handler.js'
28
+ import { PikkuError, addError } from '../../errors/error-handler.js'
29
+ import { RPCNotFoundError } from '../rpc/rpc-runner.js'
23
30
 
24
31
  /**
25
32
  * Exception thrown when workflow needs to pause for async step
@@ -47,6 +54,19 @@ export class WorkflowCancelledException extends Error {
47
54
  }
48
55
  }
49
56
 
57
+ /**
58
+ * Exception thrown when workflow is suspended
59
+ */
60
+ export class WorkflowSuspendedException extends Error {
61
+ constructor(
62
+ public readonly runId: string,
63
+ public readonly reason: string
64
+ ) {
65
+ super(reason || 'Workflow suspended')
66
+ this.name = 'WorkflowSuspendedException'
67
+ }
68
+ }
69
+
50
70
  /**
51
71
  * Error class for workflow not found
52
72
  */
@@ -55,15 +75,20 @@ export class WorkflowNotFoundError extends PikkuError {
55
75
  super(`Workflow not found: ${name}`)
56
76
  }
57
77
  }
78
+ addError(WorkflowNotFoundError, {
79
+ status: 404,
80
+ message: 'Workflow not found.',
81
+ })
58
82
 
59
- /**
60
- * Error class for workflow not found
61
- */
62
- export class WorkflowRunNotFound extends PikkuError {
83
+ export class WorkflowRunNotFoundError extends PikkuError {
63
84
  constructor(runId: string) {
64
85
  super(`Workflow run not found: ${runId}`)
65
86
  }
66
87
  }
88
+ addError(WorkflowRunNotFoundError, {
89
+ status: 404,
90
+ message: 'Workflow run not found.',
91
+ })
67
92
 
68
93
  export class WorkflowServiceNotInitialized extends Error {}
69
94
  export class WorkflowStepNameNotString extends Error {
@@ -72,6 +97,13 @@ export class WorkflowStepNameNotString extends Error {
72
97
  }
73
98
  }
74
99
 
100
+ const WORKFLOW_END_STATES: ReadonlySet<string> = new Set([
101
+ 'completed',
102
+ 'failed',
103
+ 'cancelled',
104
+ 'suspended',
105
+ ])
106
+
75
107
  /**
76
108
  * Abstract workflow state service
77
109
  * Implementations provide pluggable storage backends (SQLite, PostgreSQL, etc.)
@@ -108,7 +140,7 @@ export abstract class PikkuWorkflowService implements WorkflowService {
108
140
 
109
141
  public setServices(
110
142
  singletonServices: CoreSingletonServices,
111
- createWireServices: CreateWireServices,
143
+ createWireServices: CreateWireServices | undefined,
112
144
  { workflow }: CoreConfig
113
145
  ) {
114
146
  this.singletonServices = singletonServices
@@ -124,16 +156,20 @@ export abstract class PikkuWorkflowService implements WorkflowService {
124
156
  }
125
157
  }
126
158
 
127
- /**
128
- * Create a new workflow run
129
- * @param name - Workflow name
130
- * @param input - Input data for the workflow
131
- * @returns Run ID
132
- */
159
+ public async registerWorkflowVersions(): Promise<void> {
160
+ const allMeta = pikkuState(null, 'workflows', 'meta')
161
+ for (const [name, meta] of Object.entries(allMeta)) {
162
+ if (!meta.graphHash) continue
163
+ await this.upsertWorkflowVersion(name, meta.graphHash, meta, meta.source)
164
+ }
165
+ }
166
+
133
167
  abstract createRun(
134
168
  workflowName: string,
135
169
  input: any,
136
- inline?: boolean
170
+ inline: boolean,
171
+ graphHash: string,
172
+ wire: WorkflowRunWire
137
173
  ): Promise<string>
138
174
 
139
175
  /**
@@ -270,6 +306,7 @@ export abstract class PikkuWorkflowService implements WorkflowService {
270
306
  */
271
307
  abstract getCompletedGraphState(runId: string): Promise<{
272
308
  completedNodeIds: string[]
309
+ failedNodeIds: string[]
273
310
  branchKeys: Record<string, string>
274
311
  }>
275
312
 
@@ -321,6 +358,18 @@ export abstract class PikkuWorkflowService implements WorkflowService {
321
358
  */
322
359
  abstract getRunState(runId: string): Promise<Record<string, unknown>>
323
360
 
361
+ abstract upsertWorkflowVersion(
362
+ name: string,
363
+ graphHash: string,
364
+ graph: any,
365
+ source: string
366
+ ): Promise<void>
367
+
368
+ abstract getWorkflowVersion(
369
+ name: string,
370
+ graphHash: string
371
+ ): Promise<{ graph: any; source: string } | null>
372
+
324
373
  // ============================================================================
325
374
  // Workflow Lifecycle Methods
326
375
  // ============================================================================
@@ -334,6 +383,21 @@ export abstract class PikkuWorkflowService implements WorkflowService {
334
383
  await queueService.add(this.getConfig().orchestratorQueueName, { runId })
335
384
  }
336
385
 
386
+ public async queueStepWorker(
387
+ runId: string,
388
+ stepName: string,
389
+ rpcName: string,
390
+ data: any
391
+ ): Promise<void> {
392
+ const queueService = this.verifyQueueService()
393
+ await queueService.add(this.getConfig().stepWorkerQueueName, {
394
+ runId,
395
+ stepName,
396
+ rpcName,
397
+ data,
398
+ })
399
+ }
400
+
337
401
  /**
338
402
  * Execute a workflow sleep step completion
339
403
  * Sets the step result to null and resumes the workflow
@@ -373,6 +437,7 @@ export abstract class PikkuWorkflowService implements WorkflowService {
373
437
  public async startWorkflow<I>(
374
438
  name: string,
375
439
  input: I,
440
+ wire: WorkflowRunWire,
376
441
  rpcService: any,
377
442
  options?: { inline?: boolean; startNode?: string }
378
443
  ): Promise<{ runId: string }> {
@@ -386,14 +451,16 @@ export abstract class PikkuWorkflowService implements WorkflowService {
386
451
 
387
452
  // Check if this is a graph workflow (source === 'graph')
388
453
  if (workflowMeta.source === 'graph') {
389
- // Graph workflows use the graph scheduler
454
+ const shouldInline =
455
+ options?.inline || !this.singletonServices?.queueService
390
456
  return runWorkflowGraph(
391
457
  this,
392
458
  name,
393
459
  input,
394
460
  rpcService,
395
- options?.inline,
396
- options?.startNode
461
+ shouldInline,
462
+ options?.startNode,
463
+ wire
397
464
  )
398
465
  }
399
466
 
@@ -405,36 +472,90 @@ export abstract class PikkuWorkflowService implements WorkflowService {
405
472
  throw new WorkflowNotFoundError(name)
406
473
  }
407
474
 
408
- // Create workflow run in state
409
- const runId = await this.createRun(name, input, options?.inline)
475
+ if (!workflowMeta.graphHash) {
476
+ throw new Error(`Missing workflow graphHash for '${name}'`)
477
+ }
478
+
479
+ const runId = await this.createRun(
480
+ name,
481
+ input,
482
+ options?.inline ?? false,
483
+ workflowMeta.graphHash,
484
+ wire
485
+ )
410
486
 
411
- // Register inline run for fast lookup
412
487
  if (options?.inline) {
413
488
  this.inlineRuns.add(runId)
414
489
  }
415
490
 
416
- try {
417
- // If inline mode or no queue service, execute directly
418
- // Otherwise, use remote execution (queue-based)
419
- if (options?.inline || !this.singletonServices?.queueService) {
420
- await this.runWorkflowJob(runId, rpcService)
421
- } else {
422
- await this.resumeWorkflow(runId)
423
- }
491
+ if (options?.inline || !this.singletonServices?.queueService) {
492
+ this.runWorkflowJob(runId, rpcService)
493
+ .catch(() => {})
494
+ .finally(() => {
495
+ if (options?.inline) {
496
+ this.inlineRuns.delete(runId)
497
+ }
498
+ })
499
+ } else {
500
+ await this.resumeWorkflow(runId)
501
+ }
502
+
503
+ return { runId }
504
+ }
424
505
 
425
- return { runId }
426
- } finally {
427
- // Clean up inline tracking
428
- if (options?.inline) {
429
- this.inlineRuns.delete(runId)
506
+ public async runToCompletion<I>(
507
+ name: string,
508
+ input: I,
509
+ rpcService: any,
510
+ options?: { pollIntervalMs?: number; wire?: WorkflowRunWire }
511
+ ): Promise<any> {
512
+ const pollInterval = options?.pollIntervalMs ?? 1000
513
+ const { runId } = await this.startWorkflow(
514
+ name,
515
+ input,
516
+ options?.wire ?? { type: 'internal' },
517
+ rpcService,
518
+ { inline: true }
519
+ )
520
+ while (true) {
521
+ const run = await this.getRun(runId)
522
+ if (!run) {
523
+ throw new WorkflowRunNotFoundError(runId)
524
+ }
525
+ if (WORKFLOW_END_STATES.has(run.status)) {
526
+ if (run.status === 'failed') {
527
+ throw new Error(run.error?.message || 'Workflow failed')
528
+ }
529
+ if (run.status === 'cancelled') {
530
+ throw new Error('Workflow was cancelled')
531
+ }
532
+ return run.output
430
533
  }
534
+ await new Promise((resolve) => setTimeout(resolve, pollInterval))
431
535
  }
432
536
  }
433
537
 
434
538
  public async runWorkflowJob(runId: string, rpcService: any): Promise<void> {
435
539
  const run = await this.getRun(runId)
436
540
  if (!run) {
437
- throw new WorkflowRunNotFound(runId)
541
+ throw new WorkflowRunNotFoundError(runId)
542
+ }
543
+
544
+ const meta = pikkuState(null, 'workflows', 'meta')
545
+ const workflowMeta = meta[run.workflow]
546
+
547
+ if (
548
+ run.graphHash &&
549
+ workflowMeta?.graphHash &&
550
+ run.graphHash !== workflowMeta.graphHash
551
+ ) {
552
+ await this.runVersionMismatchFallback(run, workflowMeta, rpcService)
553
+ return
554
+ }
555
+
556
+ if (workflowMeta?.source === 'graph') {
557
+ await continueGraph(this, runId, run.workflow)
558
+ return
438
559
  }
439
560
 
440
561
  const registrations = pikkuState(null, 'workflows', 'registrations')
@@ -444,9 +565,6 @@ export abstract class PikkuWorkflowService implements WorkflowService {
444
565
  }
445
566
 
446
567
  await this.withRunLock(runId, async () => {
447
- const meta = pikkuState(null, 'workflows', 'meta')
448
- const workflowMeta = meta[run.workflow]
449
-
450
568
  const workflowWire = this.createWorkflowWire(
451
569
  run.workflow,
452
570
  runId,
@@ -457,7 +575,7 @@ export abstract class PikkuWorkflowService implements WorkflowService {
457
575
  const result = await runPikkuFunc(
458
576
  'workflow',
459
577
  workflowMeta.name,
460
- workflowMeta.pikkuFuncName,
578
+ workflowMeta.pikkuFuncId,
461
579
  {
462
580
  singletonServices: this.singletonServices!,
463
581
  wire,
@@ -469,13 +587,10 @@ export abstract class PikkuWorkflowService implements WorkflowService {
469
587
  await this.updateRunStatus(runId, 'completed', result)
470
588
  } catch (error: any) {
471
589
  if (error instanceof WorkflowAsyncException) {
472
- // Normal - workflow paused for step execution
473
590
  throw error
474
591
  }
475
592
 
476
- // Check if it's a WorkflowCancelledException
477
593
  if (error instanceof WorkflowCancelledException) {
478
- // Workflow was cancelled - update status and rethrow
479
594
  await this.updateRunStatus(runId, 'cancelled', undefined, {
480
595
  message: error.message || 'Workflow cancelled',
481
596
  stack: '',
@@ -484,6 +599,15 @@ export abstract class PikkuWorkflowService implements WorkflowService {
484
599
  throw error
485
600
  }
486
601
 
602
+ if (error instanceof WorkflowSuspendedException) {
603
+ await this.updateRunStatus(runId, 'suspended', undefined, {
604
+ message: error.message || 'Workflow suspended',
605
+ stack: '',
606
+ code: 'WORKFLOW_SUSPENDED',
607
+ })
608
+ throw error
609
+ }
610
+
487
611
  await this.updateRunStatus(runId, 'failed', undefined, {
488
612
  message: error.message,
489
613
  stack: error.stack,
@@ -495,6 +619,35 @@ export abstract class PikkuWorkflowService implements WorkflowService {
495
619
  })
496
620
  }
497
621
 
622
+ private async runVersionMismatchFallback(
623
+ run: WorkflowRun,
624
+ currentMeta: { source: string },
625
+ rpcService: any
626
+ ): Promise<void> {
627
+ const source = currentMeta.source
628
+
629
+ if (source === 'complex') {
630
+ await this.updateRunStatus(run.id, 'failed', undefined, {
631
+ message: `Workflow '${run.workflow}' definition changed. Complex workflows with inline steps cannot be migrated.`,
632
+ stack: '',
633
+ code: 'VERSION_CONFLICT',
634
+ })
635
+ return
636
+ }
637
+
638
+ const version = await this.getWorkflowVersion(run.workflow, run.graphHash!)
639
+ if (!version) {
640
+ await this.updateRunStatus(run.id, 'failed', undefined, {
641
+ message: `Workflow '${run.workflow}' version '${run.graphHash}' not found. Cannot resume with changed definition.`,
642
+ stack: '',
643
+ code: 'VERSION_NOT_FOUND',
644
+ })
645
+ return
646
+ }
647
+
648
+ await runFromMeta(this, run.id, version.graph, rpcService)
649
+ }
650
+
498
651
  /**
499
652
  * Execute a single workflow step (called by worker)
500
653
  * Handles idempotency, RPC execution, result storage, retry logic, and orchestrator triggering
@@ -527,24 +680,23 @@ export abstract class PikkuWorkflowService implements WorkflowService {
527
680
  stepState = await this.createRetryAttempt(stepState.stepId, 'running')
528
681
  }
529
682
 
530
- if (stepState.status !== 'pending') {
531
- // Mark step as running (pending or failed status)
683
+ if (stepState.status === 'pending') {
684
+ // Mark pending step as running before execution
532
685
  await this.setStepRunning(stepState.stepId)
533
686
  }
534
687
 
535
688
  try {
536
689
  let result: any
537
690
 
538
- // Check if this is a graph node step (step name starts with 'node:')
539
- if (stepName.startsWith('node:')) {
540
- // Get the graph name from the workflow run
541
- const run = await this.getRun(runId)
542
- if (!run) {
543
- throw new Error(`Workflow run not found: ${runId}`)
544
- }
545
- const graphName = run.workflow
691
+ const run = await this.getRun(runId)
692
+ if (!run) {
693
+ throw new Error(`Workflow run not found: ${runId}`)
694
+ }
695
+
696
+ const meta = pikkuState(null, 'workflows', 'meta')
697
+ const workflowMeta = meta[run.workflow]
546
698
 
547
- // Execute as graph step with graph wire context
699
+ if (workflowMeta?.nodes && stepName in workflowMeta.nodes) {
548
700
  result = await executeGraphStep(
549
701
  this,
550
702
  rpcService,
@@ -553,10 +705,9 @@ export abstract class PikkuWorkflowService implements WorkflowService {
553
705
  stepName,
554
706
  rpcName,
555
707
  data,
556
- graphName
708
+ run.workflow
557
709
  )
558
710
  } else {
559
- // Execute RPC with workflow step context (regular workflow)
560
711
  result = await rpcService.rpcWithWire(rpcName, data, {
561
712
  workflowStep: {
562
713
  runId,
@@ -572,6 +723,15 @@ export abstract class PikkuWorkflowService implements WorkflowService {
572
723
  // Resume orchestrator to continue workflow
573
724
  await this.resumeWorkflow(runId)
574
725
  } catch (error: any) {
726
+ if (error instanceof RPCNotFoundError) {
727
+ await this.setStepError(stepState.stepId, error)
728
+ await this.updateRunStatus(runId, 'suspended', undefined, {
729
+ message: `RPC '${rpcName}' not found. Deploy the missing function and resume.`,
730
+ code: 'RPC_NOT_FOUND',
731
+ })
732
+ return
733
+ }
734
+
575
735
  // Store error and mark failed
576
736
  await this.setStepError(stepState.stepId, error)
577
737
 
@@ -603,7 +763,8 @@ export abstract class PikkuWorkflowService implements WorkflowService {
603
763
  } catch (error: any) {
604
764
  if (
605
765
  error.name === 'WorkflowAsyncException' ||
606
- error.name === 'WorkflowCancelledException'
766
+ error.name === 'WorkflowCancelledException' ||
767
+ error.name === 'WorkflowSuspendedException'
607
768
  ) {
608
769
  return
609
770
  }
@@ -724,6 +885,14 @@ export abstract class PikkuWorkflowService implements WorkflowService {
724
885
  await this.setStepResult(currentStepState.stepId, result)
725
886
  return result
726
887
  } catch (error: any) {
888
+ if (error instanceof RPCNotFoundError) {
889
+ await this.updateRunStatus(runId, 'suspended', undefined, {
890
+ message: `RPC '${rpcName}' not found. Deploy the missing function and resume.`,
891
+ code: 'RPC_NOT_FOUND',
892
+ })
893
+ throw error
894
+ }
895
+
727
896
  // Record the error (marks step as failed)
728
897
  await this.setStepError(currentStepState.stepId, error)
729
898
 
@@ -766,8 +935,8 @@ export abstract class PikkuWorkflowService implements WorkflowService {
766
935
  stepState = await this.insertStepState(
767
936
  runId,
768
937
  stepName,
769
- null, // No RPC for inline steps
770
- null, // No data for inline steps
938
+ null,
939
+ null,
771
940
  stepOptions
772
941
  )
773
942
  }
@@ -852,13 +1021,9 @@ export abstract class PikkuWorkflowService implements WorkflowService {
852
1021
  stepState = await this.getStepState(runId, stepName)
853
1022
  } catch {
854
1023
  // Step doesn't exist - create it (sleep step, no RPC)
855
- stepState = await this.insertStepState(
856
- runId,
857
- stepName,
858
- null, // No RPC for sleep steps
859
- { duration }, // Store duration as data
860
- undefined // No retry options for sleep
861
- )
1024
+ stepState = await this.insertStepState(runId, stepName, null, {
1025
+ duration,
1026
+ })
862
1027
  }
863
1028
 
864
1029
  if (stepState.status === 'succeeded') {
@@ -897,13 +1062,63 @@ export abstract class PikkuWorkflowService implements WorkflowService {
897
1062
  }
898
1063
  }
899
1064
 
1065
+ private getSuspendStepName(reason: string): string {
1066
+ if (!reason) {
1067
+ return '__workflow_suspend'
1068
+ }
1069
+ return '__workflow_suspend'
1070
+ }
1071
+
1072
+ private async suspendStep(runId: string, reason: string): Promise<void> {
1073
+ const stepName = this.getSuspendStepName(reason)
1074
+ await this.withStepLock(runId, stepName, async () => {
1075
+ let stepState: StepState
1076
+ try {
1077
+ stepState = await this.getStepState(runId, stepName)
1078
+ } catch {
1079
+ stepState = await this.insertStepState(
1080
+ runId,
1081
+ stepName,
1082
+ 'pikkuWorkflowSuspend',
1083
+ {
1084
+ reason,
1085
+ }
1086
+ )
1087
+ }
1088
+ if (!stepState.stepId) {
1089
+ stepState = await this.insertStepState(
1090
+ runId,
1091
+ stepName,
1092
+ 'pikkuWorkflowSuspend',
1093
+ {
1094
+ reason,
1095
+ }
1096
+ )
1097
+ }
1098
+
1099
+ if (stepState.status === 'succeeded') {
1100
+ return
1101
+ }
1102
+
1103
+ if (stepState.status === 'pending') {
1104
+ await this.setStepRunning(stepState.stepId)
1105
+ }
1106
+
1107
+ await this.setStepResult(stepState.stepId, {
1108
+ reason,
1109
+ suspendedAt: new Date().toISOString(),
1110
+ })
1111
+ throw new WorkflowSuspendedException(runId, reason)
1112
+ })
1113
+ }
1114
+
900
1115
  private createWorkflowWire(
901
- workflowName: string,
1116
+ name: string,
902
1117
  runId: string,
903
1118
  rpcService: any
904
1119
  ): PikkuWorkflowWire {
905
1120
  const workflowWire: PikkuWorkflowWire = {
906
- workflowName,
1121
+ name,
907
1122
  runId,
908
1123
  getRun: async () => (await this.getRun(runId)) as WorkflowRun,
909
1124
 
@@ -943,6 +1158,10 @@ export abstract class PikkuWorkflowService implements WorkflowService {
943
1158
  getDurationInMilliseconds(duration)
944
1159
  )
945
1160
  },
1161
+
1162
+ suspend: async (reason: string) => {
1163
+ await this.suspendStep(runId, reason || 'Workflow suspended')
1164
+ },
946
1165
  }
947
1166
  return workflowWire
948
1167
  }
@@ -0,0 +1,79 @@
1
+ import { WorkflowRunNotFoundError } from './pikku-workflow-service.js'
2
+
3
+ export function workflow<
4
+ TWorkflowMap extends Record<string, { input: any; output: any }>,
5
+ >(
6
+ workflowName: string & keyof TWorkflowMap,
7
+ options?: { pollIntervalMs?: number }
8
+ ): { func: (services: any, data: any, wire: any) => Promise<any> } {
9
+ return {
10
+ func: async (services: any, data: any, { rpc }: any) => {
11
+ return services.workflowService.runToCompletion(
12
+ workflowName,
13
+ data,
14
+ rpc,
15
+ options
16
+ )
17
+ },
18
+ }
19
+ }
20
+
21
+ export function workflowStart<
22
+ TWorkflowMap extends Record<string, { input: any; output: any }>,
23
+ >(
24
+ workflowName: string & keyof TWorkflowMap
25
+ ): {
26
+ func: (services: any, data: any, wire: any) => Promise<{ runId: string }>
27
+ } {
28
+ return {
29
+ func: async (_services: any, data: any, { rpc }: any) => {
30
+ return rpc.startWorkflow(workflowName, data)
31
+ },
32
+ }
33
+ }
34
+
35
+ export function workflowStatus<
36
+ TWorkflowMap extends Record<string, { input: any; output: any }>,
37
+ >(
38
+ _workflowName: string & keyof TWorkflowMap
39
+ ): {
40
+ func: (
41
+ services: any,
42
+ data: { runId: string }
43
+ ) => Promise<{
44
+ id: string
45
+ status: 'running' | 'suspended' | 'completed' | 'failed' | 'cancelled'
46
+ output?: any
47
+ error?: { message?: string }
48
+ }>
49
+ } {
50
+ return {
51
+ func: async (services: any, data: { runId: string }) => {
52
+ const run = await services.workflowService.getRun(data.runId)
53
+ if (!run) {
54
+ throw new WorkflowRunNotFoundError(data.runId)
55
+ }
56
+ return {
57
+ id: run.id,
58
+ status: run.status,
59
+ output: run.output,
60
+ error: run.error ? { message: run.error.message } : undefined,
61
+ }
62
+ },
63
+ }
64
+ }
65
+
66
+ export function graphStart<
67
+ TGraphsMap extends Record<string, Record<string, any>>,
68
+ >(
69
+ graphName: string & keyof TGraphsMap,
70
+ startNode: string
71
+ ): {
72
+ func: (services: any, data: any, wire: any) => Promise<{ runId: string }>
73
+ } {
74
+ return {
75
+ func: async (_services: any, data: any, { rpc }: any) => {
76
+ return rpc.startWorkflow(graphName, data, { startNode })
77
+ },
78
+ }
79
+ }