@pikku/core 0.12.106 → 0.12.111

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 (589) hide show
  1. package/CHANGELOG.md +358 -0
  2. package/dist/analytics/analytics.d.ts +6 -0
  3. package/dist/analytics/analytics.js +58 -0
  4. package/dist/analytics/analytics.types.d.ts +156 -0
  5. package/dist/analytics/analytics.types.js +1 -0
  6. package/dist/analytics/anonymous-analytics-identity.d.ts +23 -0
  7. package/dist/analytics/anonymous-analytics-identity.js +27 -0
  8. package/dist/analytics/compose-analytics-identity.d.ts +14 -0
  9. package/dist/analytics/compose-analytics-identity.js +33 -0
  10. package/dist/analytics/cookie-analytics-identity.d.ts +24 -0
  11. package/dist/analytics/cookie-analytics-identity.js +37 -0
  12. package/dist/analytics/define-analytics-events.d.ts +22 -0
  13. package/dist/analytics/define-analytics-events.js +9 -0
  14. package/dist/analytics/fan-out-analytics.d.ts +15 -0
  15. package/dist/analytics/fan-out-analytics.js +36 -0
  16. package/dist/analytics/index.d.ts +13 -0
  17. package/dist/analytics/index.js +8 -0
  18. package/dist/analytics/logger-analytics-service.d.ts +14 -0
  19. package/dist/analytics/logger-analytics-service.js +23 -0
  20. package/dist/analytics/mint-cookie.d.ts +44 -0
  21. package/dist/analytics/mint-cookie.js +55 -0
  22. package/dist/errors/errors.d.ts +7 -0
  23. package/dist/errors/errors.js +17 -0
  24. package/dist/function/function-meta.types.d.ts +2 -0
  25. package/dist/function/function-runner.js +29 -0
  26. package/dist/function/functions.types.d.ts +22 -3
  27. package/dist/middleware/auth-apikey.d.ts +1 -1
  28. package/dist/pikku-state.d.ts +1 -1
  29. package/dist/scopes.d.ts +11 -0
  30. package/dist/scopes.js +17 -0
  31. package/dist/services/credential-wire-service.d.ts +1 -1
  32. package/dist/services/feature-flag-service.d.ts +100 -0
  33. package/dist/services/feature-flag-service.js +1 -0
  34. package/dist/services/index.d.ts +1 -0
  35. package/dist/services/meta-service.d.ts +16 -0
  36. package/dist/services/meta-service.js +19 -0
  37. package/dist/services/webhook-service.d.ts +7 -2
  38. package/dist/services/webhook-service.js +11 -6
  39. package/dist/testing/service-tests/agent-run-service-tests.d.ts +2 -2
  40. package/dist/testing/service-tests/agent-storage-service-tests.d.ts +1 -1
  41. package/dist/testing/service-tests/channel-store-tests.d.ts +1 -1
  42. package/dist/testing/service-tests/credential-service-tests.d.ts +1 -1
  43. package/dist/testing/service-tests/deployment-service-tests.d.ts +1 -1
  44. package/dist/testing/service-tests/event-hub-store-tests.d.ts +1 -1
  45. package/dist/testing/service-tests/secret-service-tests.d.ts +1 -1
  46. package/dist/testing/service-tests/session-store-tests.d.ts +1 -1
  47. package/dist/testing/service-tests/workflow-run-service-tests.d.ts +1 -1
  48. package/dist/testing/service-tests/workflow-service-tests.d.ts +1 -1
  49. package/dist/types/core.types.d.ts +23 -0
  50. package/dist/wirings/agent/agent-rpc.d.ts +1 -1
  51. package/dist/wirings/agent/agent-turn.d.ts +1 -1
  52. package/dist/wirings/agent-scorer/agent-scorer-live.d.ts +1 -1
  53. package/dist/wirings/channel/channel-common.d.ts +1 -1
  54. package/dist/wirings/channel/channel-rpc-validators.d.ts +2 -2
  55. package/dist/wirings/channel/channel-runner.d.ts +1 -1
  56. package/dist/wirings/channel/local/local-channel-runner.d.ts +1 -1
  57. package/dist/wirings/channel/serverless/serverless-channel-runner.d.ts +1 -1
  58. package/dist/wirings/cli/channel/cli-approval.d.ts +2 -2
  59. package/dist/wirings/flag/assert-feature-available.d.ts +15 -0
  60. package/dist/wirings/flag/assert-feature-available.js +23 -0
  61. package/dist/wirings/flag/cached-flag-source.d.ts +73 -0
  62. package/dist/wirings/flag/cached-flag-source.js +96 -0
  63. package/dist/wirings/flag/define-feature-flags.d.ts +33 -0
  64. package/dist/wirings/flag/define-feature-flags.js +32 -0
  65. package/dist/wirings/flag/flag.types.d.ts +97 -0
  66. package/dist/wirings/flag/flag.types.js +1 -0
  67. package/dist/wirings/flag/index.d.ts +7 -0
  68. package/dist/wirings/flag/index.js +5 -0
  69. package/dist/wirings/flag/resolve-flag.d.ts +52 -0
  70. package/dist/wirings/flag/resolve-flag.js +90 -0
  71. package/dist/wirings/flag/validate-flag-definitions.d.ts +26 -0
  72. package/dist/wirings/flag/validate-flag-definitions.js +103 -0
  73. package/dist/wirings/rpc/rpc-runner.js +3 -0
  74. package/dist/wirings/virtual-user/virtual-user-scaffold.d.ts +8 -8
  75. package/dist/wirings/virtual-user/virtual-user-schedule.d.ts +1 -1
  76. package/dist/wirings/workflow/workflow-queue-routing.d.ts +1 -1
  77. package/package.json +11 -5
  78. package/run-tests.sh +17 -9
  79. package/src/public-surface.json +26 -0
  80. package/src/agent-naming.test.ts +0 -68
  81. package/src/api-report.test.ts +0 -138
  82. package/src/app-alias-surface.test.ts +0 -179
  83. package/src/app-leaf-surface.test.ts +0 -222
  84. package/src/bootstrap-compat/root.ts +0 -20
  85. package/src/classification/column-form.test.ts +0 -97
  86. package/src/classification/column-form.ts +0 -55
  87. package/src/classification/data-classification.ts +0 -73
  88. package/src/classification/index.ts +0 -40
  89. package/src/classification/key-ids.ts +0 -2
  90. package/src/classification/secret-value.test.ts +0 -204
  91. package/src/classification/secret-value.ts +0 -106
  92. package/src/crypto-utils.test.ts +0 -668
  93. package/src/crypto-utils.ts +0 -460
  94. package/src/dev/hot-reload.test.ts +0 -587
  95. package/src/dev/hot-reload.ts +0 -225
  96. package/src/dev/module-runner.test.ts +0 -235
  97. package/src/dev/module-runner.ts +0 -138
  98. package/src/dev/reload-meta.test.ts +0 -183
  99. package/src/dev/reload-meta.ts +0 -144
  100. package/src/ecosystem-tier-removed.test.ts +0 -72
  101. package/src/env.ts +0 -1
  102. package/src/errors/error-handler.ts +0 -77
  103. package/src/errors/error.test.ts +0 -229
  104. package/src/errors/errors.ts +0 -393
  105. package/src/errors/index.ts +0 -11
  106. package/src/errors/serialized-error.ts +0 -12
  107. package/src/factory-functions.test.ts +0 -111
  108. package/src/function/abort-scope.test.ts +0 -97
  109. package/src/function/abort-scope.ts +0 -80
  110. package/src/function/function-meta.types.ts +0 -151
  111. package/src/function/function-runner.test.ts +0 -1619
  112. package/src/function/function-runner.ts +0 -499
  113. package/src/function/functions.types.ts +0 -319
  114. package/src/function/index.ts +0 -40
  115. package/src/function/list.types.test.ts +0 -100
  116. package/src/function/list.types.ts +0 -69
  117. package/src/function/pikku-request.test.ts +0 -35
  118. package/src/function/pikku-request.ts +0 -17
  119. package/src/gopass-secrets-removed.test.ts +0 -51
  120. package/src/handle-error.test.ts +0 -532
  121. package/src/handle-error.ts +0 -64
  122. package/src/middleware/auth-apikey.test.ts +0 -362
  123. package/src/middleware/auth-apikey.ts +0 -39
  124. package/src/middleware/auth-bearer.test.ts +0 -566
  125. package/src/middleware/auth-bearer.ts +0 -83
  126. package/src/middleware/auth-cookie.test.ts +0 -572
  127. package/src/middleware/auth-cookie.ts +0 -107
  128. package/src/middleware/cors.test.ts +0 -579
  129. package/src/middleware/cors.ts +0 -103
  130. package/src/middleware/index.ts +0 -30
  131. package/src/middleware/middleware-factories.ts +0 -76
  132. package/src/middleware/middleware.types.ts +0 -61
  133. package/src/middleware/remote-auth.test.ts +0 -547
  134. package/src/middleware/remote-auth.ts +0 -95
  135. package/src/middleware/remote-jobs-secret.ts +0 -35
  136. package/src/middleware/require-origin.test.ts +0 -115
  137. package/src/middleware/require-origin.ts +0 -79
  138. package/src/middleware/telemetry.ts +0 -83
  139. package/src/middleware-runner.test.ts +0 -578
  140. package/src/middleware-runner.ts +0 -232
  141. package/src/no-any-casts.test.ts +0 -57
  142. package/src/no-root-barrel.test.ts +0 -122
  143. package/src/permissions.test.ts +0 -256
  144. package/src/permissions.ts +0 -217
  145. package/src/pikku-state.test.ts +0 -198
  146. package/src/pikku-state.ts +0 -206
  147. package/src/production-barrels-stay-lean.test.ts +0 -110
  148. package/src/public-surface.test.ts +0 -133
  149. package/src/remote.test.ts +0 -173
  150. package/src/remote.ts +0 -56
  151. package/src/removed-legacy-exports.test.ts +0 -66
  152. package/src/run-tests-script.test.ts +0 -67
  153. package/src/schema.test.ts +0 -430
  154. package/src/schema.ts +0 -204
  155. package/src/scopes.test.ts +0 -203
  156. package/src/scopes.ts +0 -67
  157. package/src/services/agent-run-state-service.ts +0 -37
  158. package/src/services/agent-runner-service.ts +0 -238
  159. package/src/services/agent-storage-service.ts +0 -39
  160. package/src/services/ai-embedding-service.ts +0 -9
  161. package/src/services/audit-service.test.ts +0 -44
  162. package/src/services/audit-service.ts +0 -280
  163. package/src/services/content-service.ts +0 -65
  164. package/src/services/credential-service.ts +0 -20
  165. package/src/services/credential-wire-service.test.ts +0 -235
  166. package/src/services/credential-wire-service.ts +0 -120
  167. package/src/services/deployment-service.ts +0 -32
  168. package/src/services/email-service.test.ts +0 -100
  169. package/src/services/email-service.ts +0 -60
  170. package/src/services/email-template.test.ts +0 -311
  171. package/src/services/email-template.ts +0 -254
  172. package/src/services/file-scenario-run-store.test.ts +0 -349
  173. package/src/services/file-scenario-run-store.ts +0 -282
  174. package/src/services/gateway-service.ts +0 -5
  175. package/src/services/http-personas-converse.test.ts +0 -274
  176. package/src/services/http-personas.test.ts +0 -379
  177. package/src/services/http-personas.ts +0 -508
  178. package/src/services/in-memory-agent-run-state-service.ts +0 -87
  179. package/src/services/in-memory-queue-service.test.ts +0 -162
  180. package/src/services/in-memory-queue-service.ts +0 -78
  181. package/src/services/in-memory-session-store.test.ts +0 -58
  182. package/src/services/in-memory-session-store.ts +0 -20
  183. package/src/services/in-memory-trigger-service.ts +0 -47
  184. package/src/services/in-memory-workflow-service.test.ts +0 -430
  185. package/src/services/in-memory-workflow-service.ts +0 -595
  186. package/src/services/index.ts +0 -192
  187. package/src/services/istanbul-coverage-service.test.ts +0 -66
  188. package/src/services/istanbul-coverage-service.ts +0 -59
  189. package/src/services/jwt-service.ts +0 -15
  190. package/src/services/local-content-request-handler.test.ts +0 -240
  191. package/src/services/local-content-request-handler.ts +0 -295
  192. package/src/services/local-content.test.ts +0 -186
  193. package/src/services/local-content.ts +0 -242
  194. package/src/services/local-credential-service.ts +0 -63
  195. package/src/services/local-email-service.test.ts +0 -182
  196. package/src/services/local-email-service.ts +0 -43
  197. package/src/services/local-gateway-service.ts +0 -51
  198. package/src/services/local-secrets.test.ts +0 -95
  199. package/src/services/local-secrets.ts +0 -71
  200. package/src/services/local-variables.test.ts +0 -61
  201. package/src/services/local-variables.ts +0 -45
  202. package/src/services/logger-console.test.ts +0 -117
  203. package/src/services/logger-console.ts +0 -174
  204. package/src/services/logger.ts +0 -48
  205. package/src/services/meta-service.test.ts +0 -75
  206. package/src/services/meta-service.ts +0 -767
  207. package/src/services/persona-actor-secret.test.ts +0 -68
  208. package/src/services/persona-actor-secret.ts +0 -70
  209. package/src/services/persona-sign-in.test.ts +0 -219
  210. package/src/services/persona-sign-in.ts +0 -238
  211. package/src/services/personas-service.ts +0 -191
  212. package/src/services/pikku-user-id.test.ts +0 -62
  213. package/src/services/pikku-user-id.ts +0 -13
  214. package/src/services/queue-webhook-service.test.ts +0 -409
  215. package/src/services/queue-webhook-service.ts +0 -150
  216. package/src/services/remote-jobs.test.ts +0 -106
  217. package/src/services/remote-jobs.ts +0 -92
  218. package/src/services/scheduler-service.ts +0 -37
  219. package/src/services/schema-service.ts +0 -10
  220. package/src/services/scope-service.ts +0 -91
  221. package/src/services/scoped-credential-service.test.ts +0 -86
  222. package/src/services/scoped-credential-service.ts +0 -63
  223. package/src/services/scoped-secret-service.test.ts +0 -93
  224. package/src/services/scoped-secret-service.ts +0 -40
  225. package/src/services/secret-host-binding.test.ts +0 -138
  226. package/src/services/secret-host-binding.ts +0 -51
  227. package/src/services/secret-service.ts +0 -25
  228. package/src/services/secretless.test.ts +0 -54
  229. package/src/services/secretless.ts +0 -29
  230. package/src/services/session-store.ts +0 -9
  231. package/src/services/stub-tracker.test.ts +0 -104
  232. package/src/services/stub-tracker.ts +0 -175
  233. package/src/services/system-role-guard.test.ts +0 -96
  234. package/src/services/system-role-guard.ts +0 -71
  235. package/src/services/temporary-file-service.ts +0 -76
  236. package/src/services/trigger-service.ts +0 -5
  237. package/src/services/typed-credential-service.ts +0 -83
  238. package/src/services/typed-secret-service.test.ts +0 -204
  239. package/src/services/typed-secret-service.ts +0 -132
  240. package/src/services/typed-variables-service.test.ts +0 -166
  241. package/src/services/typed-variables-service.ts +0 -166
  242. package/src/services/user-session-service.test.ts +0 -147
  243. package/src/services/user-session-service.ts +0 -85
  244. package/src/services/v8-coverage-service.test.ts +0 -69
  245. package/src/services/v8-coverage-service.ts +0 -163
  246. package/src/services/variables-service.ts +0 -13
  247. package/src/services/webhook-service.ts +0 -147
  248. package/src/services/workflow-service.ts +0 -120
  249. package/src/side-effects-are-declared.test.ts +0 -84
  250. package/src/source-files-stay-composable.test.ts +0 -41
  251. package/src/template-alias-surface.test.ts +0 -240
  252. package/src/test-utils.ts +0 -38
  253. package/src/testing/index.ts +0 -3
  254. package/src/testing/service-tests/agent-run-service-tests.ts +0 -98
  255. package/src/testing/service-tests/agent-storage-service-tests.ts +0 -379
  256. package/src/testing/service-tests/channel-store-tests.ts +0 -98
  257. package/src/testing/service-tests/credential-service-tests.ts +0 -143
  258. package/src/testing/service-tests/deployment-service-tests.ts +0 -33
  259. package/src/testing/service-tests/event-hub-store-tests.ts +0 -48
  260. package/src/testing/service-tests/secret-service-tests.ts +0 -105
  261. package/src/testing/service-tests/session-store-tests.ts +0 -62
  262. package/src/testing/service-tests/workflow-run-service-tests.ts +0 -59
  263. package/src/testing/service-tests/workflow-service-tests.ts +0 -308
  264. package/src/testing/service-tests.ts +0 -95
  265. package/src/time-utils.test.ts +0 -56
  266. package/src/time-utils.ts +0 -89
  267. package/src/types/core.types.ts +0 -446
  268. package/src/types/index.ts +0 -25
  269. package/src/types/state.types.ts +0 -226
  270. package/src/types/typed-credentials.test.ts +0 -72
  271. package/src/utils/hash.test.ts +0 -68
  272. package/src/utils/hash.ts +0 -26
  273. package/src/utils/hmac.ts +0 -21
  274. package/src/utils/node-host-resolver.ts +0 -20
  275. package/src/utils/safe-fetch.test.ts +0 -515
  276. package/src/utils/safe-fetch.ts +0 -347
  277. package/src/utils.test.ts +0 -359
  278. package/src/utils.ts +0 -165
  279. package/src/version.test.ts +0 -80
  280. package/src/version.ts +0 -25
  281. package/src/wirings/actor-flow/actor-flow.types.ts +0 -40
  282. package/src/wirings/actor-flow/index.ts +0 -10
  283. package/src/wirings/actor-flow/run-conversation.test.ts +0 -187
  284. package/src/wirings/actor-flow/run-conversation.ts +0 -280
  285. package/src/wirings/addon/addon-auth-tags.test.ts +0 -219
  286. package/src/wirings/addon/addon-runner.ts +0 -236
  287. package/src/wirings/addon/addon-scopes.test.ts +0 -225
  288. package/src/wirings/addon/addon-secrets.test.ts +0 -391
  289. package/src/wirings/addon/index.ts +0 -12
  290. package/src/wirings/addon/remote-addon-auth.ts +0 -65
  291. package/src/wirings/addon/wire-addon.test.ts +0 -102
  292. package/src/wirings/addon/wire-addon.ts +0 -202
  293. package/src/wirings/addon/wire-remote-addon.ts +0 -41
  294. package/src/wirings/agent/agent-agui.test.ts +0 -1213
  295. package/src/wirings/agent/agent-agui.ts +0 -405
  296. package/src/wirings/agent/agent-authorization.test.ts +0 -208
  297. package/src/wirings/agent/agent-finalize.test.ts +0 -187
  298. package/src/wirings/agent/agent-finalize.ts +0 -197
  299. package/src/wirings/agent/agent-helpers.test.ts +0 -215
  300. package/src/wirings/agent/agent-helpers.ts +0 -96
  301. package/src/wirings/agent/agent-interrupt.test.ts +0 -833
  302. package/src/wirings/agent/agent-interrupt.ts +0 -400
  303. package/src/wirings/agent/agent-memory.test.ts +0 -397
  304. package/src/wirings/agent/agent-memory.ts +0 -496
  305. package/src/wirings/agent/agent-middleware.types.test.ts +0 -41
  306. package/src/wirings/agent/agent-model-config.test.ts +0 -103
  307. package/src/wirings/agent/agent-model-config.ts +0 -59
  308. package/src/wirings/agent/agent-prepare.test.ts +0 -977
  309. package/src/wirings/agent/agent-prepare.ts +0 -1083
  310. package/src/wirings/agent/agent-registry.test.ts +0 -455
  311. package/src/wirings/agent/agent-registry.ts +0 -99
  312. package/src/wirings/agent/agent-resume-authorization.test.ts +0 -206
  313. package/src/wirings/agent/agent-rpc.ts +0 -116
  314. package/src/wirings/agent/agent-runner.test.ts +0 -1229
  315. package/src/wirings/agent/agent-runner.ts +0 -858
  316. package/src/wirings/agent/agent-stream-delegate.test.ts +0 -381
  317. package/src/wirings/agent/agent-stream-output-hooks.test.ts +0 -353
  318. package/src/wirings/agent/agent-stream.test.ts +0 -1800
  319. package/src/wirings/agent/agent-stream.ts +0 -1639
  320. package/src/wirings/agent/agent-thread-ownership.test.ts +0 -297
  321. package/src/wirings/agent/agent-turn.test.ts +0 -65
  322. package/src/wirings/agent/agent-turn.ts +0 -122
  323. package/src/wirings/agent/agent-utils.ts +0 -20
  324. package/src/wirings/agent/agent.types.ts +0 -676
  325. package/src/wirings/agent/index.ts +0 -58
  326. package/src/wirings/agent/voice-input.test.ts +0 -220
  327. package/src/wirings/agent/voice-input.ts +0 -180
  328. package/src/wirings/agent/voice-output.test.ts +0 -510
  329. package/src/wirings/agent/voice-output.ts +0 -311
  330. package/src/wirings/agent-scorer/agent-scorer-grade.test.ts +0 -106
  331. package/src/wirings/agent-scorer/agent-scorer-grade.ts +0 -55
  332. package/src/wirings/agent-scorer/agent-scorer-judge.test.ts +0 -285
  333. package/src/wirings/agent-scorer/agent-scorer-judge.ts +0 -180
  334. package/src/wirings/agent-scorer/agent-scorer-live.test.ts +0 -174
  335. package/src/wirings/agent-scorer/agent-scorer-live.ts +0 -56
  336. package/src/wirings/agent-scorer/agent-scorer-registry.ts +0 -66
  337. package/src/wirings/agent-scorer/agent-scorer-sampling.test.ts +0 -34
  338. package/src/wirings/agent-scorer/agent-scorer-sampling.ts +0 -36
  339. package/src/wirings/agent-scorer/agent-scorer-snapshots.test.ts +0 -49
  340. package/src/wirings/agent-scorer/agent-scorer-snapshots.ts +0 -46
  341. package/src/wirings/agent-scorer/agent-scorer-worker.test.ts +0 -122
  342. package/src/wirings/agent-scorer/agent-scorer-worker.ts +0 -71
  343. package/src/wirings/agent-scorer/agent-scorer.ts +0 -102
  344. package/src/wirings/agent-scorer/agent-scorer.types.ts +0 -128
  345. package/src/wirings/agent-scorer/index.ts +0 -23
  346. package/src/wirings/channel/channel-common.ts +0 -102
  347. package/src/wirings/channel/channel-handler-shapes.test.ts +0 -72
  348. package/src/wirings/channel/channel-handler.test.ts +0 -322
  349. package/src/wirings/channel/channel-handler.ts +0 -259
  350. package/src/wirings/channel/channel-host-rpc.test.ts +0 -150
  351. package/src/wirings/channel/channel-host-rpc.ts +0 -69
  352. package/src/wirings/channel/channel-middleware-runner.test.ts +0 -188
  353. package/src/wirings/channel/channel-middleware-runner.ts +0 -126
  354. package/src/wirings/channel/channel-rpc-registry.ts +0 -116
  355. package/src/wirings/channel/channel-rpc-responder.ts +0 -117
  356. package/src/wirings/channel/channel-rpc-service.ts +0 -132
  357. package/src/wirings/channel/channel-rpc-validators.ts +0 -65
  358. package/src/wirings/channel/channel-rpc.test.ts +0 -841
  359. package/src/wirings/channel/channel-rpc.ts +0 -5
  360. package/src/wirings/channel/channel-rpc.types.ts +0 -160
  361. package/src/wirings/channel/channel-runner.ts +0 -164
  362. package/src/wirings/channel/channel-store.ts +0 -35
  363. package/src/wirings/channel/channel.types.ts +0 -193
  364. package/src/wirings/channel/define-channel-routes.ts +0 -5
  365. package/src/wirings/channel/eventhub-service.ts +0 -16
  366. package/src/wirings/channel/eventhub-store.ts +0 -13
  367. package/src/wirings/channel/index.ts +0 -40
  368. package/src/wirings/channel/local/index.ts +0 -3
  369. package/src/wirings/channel/local/local-channel-handler.ts +0 -98
  370. package/src/wirings/channel/local/local-channel-runner.test.ts +0 -339
  371. package/src/wirings/channel/local/local-channel-runner.ts +0 -271
  372. package/src/wirings/channel/local/local-eventhub-service.test.ts +0 -77
  373. package/src/wirings/channel/local/local-eventhub-service.ts +0 -69
  374. package/src/wirings/channel/log-channels.ts +0 -16
  375. package/src/wirings/channel/pikku-abstract-channel-handler.test.ts +0 -135
  376. package/src/wirings/channel/pikku-abstract-channel-handler.ts +0 -60
  377. package/src/wirings/channel/serverless/index.ts +0 -6
  378. package/src/wirings/channel/serverless/serverless-channel-runner.ts +0 -328
  379. package/src/wirings/cli/channel/cli-approval.test.ts +0 -177
  380. package/src/wirings/cli/channel/cli-approval.ts +0 -135
  381. package/src/wirings/cli/channel/cli-channel-runner.ts +0 -118
  382. package/src/wirings/cli/channel/cli-raw-channel-runner.test.ts +0 -192
  383. package/src/wirings/cli/channel/cli-raw-channel-runner.ts +0 -148
  384. package/src/wirings/cli/channel/cli-raw-client-runner.test.ts +0 -480
  385. package/src/wirings/cli/channel/cli-raw-client-runner.ts +0 -188
  386. package/src/wirings/cli/channel/index.ts +0 -6
  387. package/src/wirings/cli/cli-runner.test.ts +0 -969
  388. package/src/wirings/cli/cli-runner.ts +0 -538
  389. package/src/wirings/cli/cli.types.ts +0 -388
  390. package/src/wirings/cli/command-parser.test.ts +0 -1120
  391. package/src/wirings/cli/command-parser.ts +0 -713
  392. package/src/wirings/cli/define-cli-commands.ts +0 -13
  393. package/src/wirings/cli/format-cli-error.test.ts +0 -91
  394. package/src/wirings/cli/format-cli-error.ts +0 -96
  395. package/src/wirings/cli/index.ts +0 -19
  396. package/src/wirings/credential/credential.types.ts +0 -37
  397. package/src/wirings/credential/define-credential.ts +0 -49
  398. package/src/wirings/credential/index.ts +0 -7
  399. package/src/wirings/credential/validate-credential-definitions.ts +0 -63
  400. package/src/wirings/gateway/gateway-authorization.test.ts +0 -575
  401. package/src/wirings/gateway/gateway-channel-meta.test.ts +0 -44
  402. package/src/wirings/gateway/gateway-runner.test.ts +0 -581
  403. package/src/wirings/gateway/gateway-runner.ts +0 -411
  404. package/src/wirings/gateway/gateway.types.ts +0 -137
  405. package/src/wirings/gateway/index.ts +0 -15
  406. package/src/wirings/http/http-routes.test.ts +0 -318
  407. package/src/wirings/http/http-routes.ts +0 -111
  408. package/src/wirings/http/http-runner-addon-ref.test.ts +0 -238
  409. package/src/wirings/http/http-runner.test.ts +0 -503
  410. package/src/wirings/http/http-runner.ts +0 -462
  411. package/src/wirings/http/http.types.ts +0 -307
  412. package/src/wirings/http/index.ts +0 -27
  413. package/src/wirings/http/log-http-routes.ts +0 -18
  414. package/src/wirings/http/pikku-fetch-http-request.test.ts +0 -318
  415. package/src/wirings/http/pikku-fetch-http-request.ts +0 -260
  416. package/src/wirings/http/pikku-fetch-http-response.test.ts +0 -123
  417. package/src/wirings/http/pikku-fetch-http-response.ts +0 -149
  418. package/src/wirings/http/routers/http-router.ts +0 -13
  419. package/src/wirings/http/routers/path-to-regex.test.ts +0 -306
  420. package/src/wirings/http/routers/path-to-regex.ts +0 -115
  421. package/src/wirings/http/web-request.test.ts +0 -382
  422. package/src/wirings/http/web-request.ts +0 -162
  423. package/src/wirings/mcp/index.ts +0 -26
  424. package/src/wirings/mcp/mcp-endpoint-registry.test.ts +0 -388
  425. package/src/wirings/mcp/mcp-endpoint-registry.ts +0 -159
  426. package/src/wirings/mcp/mcp-runner.test.ts +0 -588
  427. package/src/wirings/mcp/mcp-runner.ts +0 -330
  428. package/src/wirings/mcp/mcp.types.ts +0 -199
  429. package/src/wirings/node/index.ts +0 -2
  430. package/src/wirings/node/node.types.ts +0 -23
  431. package/src/wirings/oauth2/index.ts +0 -1
  432. package/src/wirings/oauth2/oauth2.types.ts +0 -12
  433. package/src/wirings/persona/define-personas.ts +0 -33
  434. package/src/wirings/persona/index.ts +0 -75
  435. package/src/wirings/persona/persona-app-scopes.test.ts +0 -47
  436. package/src/wirings/persona/persona-app-scopes.ts +0 -74
  437. package/src/wirings/persona/persona-email.ts +0 -87
  438. package/src/wirings/persona/persona-environments.test.ts +0 -194
  439. package/src/wirings/persona/persona-environments.ts +0 -138
  440. package/src/wirings/persona/persona-mailbox.ts +0 -156
  441. package/src/wirings/persona/persona.test.ts +0 -230
  442. package/src/wirings/persona/persona.types.ts +0 -156
  443. package/src/wirings/persona/validate-personas.ts +0 -137
  444. package/src/wirings/queue/index.ts +0 -29
  445. package/src/wirings/queue/queue-identity.test.ts +0 -454
  446. package/src/wirings/queue/queue-identity.ts +0 -176
  447. package/src/wirings/queue/queue-runner.test.ts +0 -705
  448. package/src/wirings/queue/queue-runner.ts +0 -181
  449. package/src/wirings/queue/queue.types.ts +0 -161
  450. package/src/wirings/queue/register-queue-helper.ts +0 -46
  451. package/src/wirings/queue/signed-queue-service.ts +0 -60
  452. package/src/wirings/queue/validate-worker-config.test.ts +0 -108
  453. package/src/wirings/queue/validate-worker-config.ts +0 -90
  454. package/src/wirings/role/define-system-role.ts +0 -33
  455. package/src/wirings/role/index.ts +0 -13
  456. package/src/wirings/role/role.test.ts +0 -104
  457. package/src/wirings/role/role.types.ts +0 -47
  458. package/src/wirings/role/validate-role-definitions.ts +0 -93
  459. package/src/wirings/rpc/index.ts +0 -8
  460. package/src/wirings/rpc/rpc-runner.test.ts +0 -1130
  461. package/src/wirings/rpc/rpc-runner.ts +0 -566
  462. package/src/wirings/rpc/rpc-types.ts +0 -87
  463. package/src/wirings/scheduler/index.ts +0 -11
  464. package/src/wirings/scheduler/log-schedulers.ts +0 -16
  465. package/src/wirings/scheduler/scheduler-runner.test.ts +0 -835
  466. package/src/wirings/scheduler/scheduler-runner.ts +0 -138
  467. package/src/wirings/scheduler/scheduler.types.ts +0 -42
  468. package/src/wirings/scope/define-scope.ts +0 -33
  469. package/src/wirings/scope/index.ts +0 -15
  470. package/src/wirings/scope/scope.test.ts +0 -159
  471. package/src/wirings/scope/scope.types.ts +0 -42
  472. package/src/wirings/scope/validate-scope-definitions.ts +0 -107
  473. package/src/wirings/secret/index.ts +0 -9
  474. package/src/wirings/secret/secret.types.ts +0 -70
  475. package/src/wirings/secret/validate-secret-definitions.test.ts +0 -232
  476. package/src/wirings/secret/validate-secret-definitions.ts +0 -88
  477. package/src/wirings/trigger/index.ts +0 -9
  478. package/src/wirings/trigger/pikku-trigger-service.ts +0 -107
  479. package/src/wirings/trigger/trigger-runner.test.ts +0 -78
  480. package/src/wirings/trigger/trigger-runner.ts +0 -113
  481. package/src/wirings/trigger/trigger.types.ts +0 -100
  482. package/src/wirings/variable/index.ts +0 -8
  483. package/src/wirings/variable/validate-variable-definitions.test.ts +0 -115
  484. package/src/wirings/variable/validate-variable-definitions.ts +0 -71
  485. package/src/wirings/variable/variable.types.ts +0 -40
  486. package/src/wirings/virtual-user/index.ts +0 -107
  487. package/src/wirings/virtual-user/prepare-virtual-user-run.test.ts +0 -115
  488. package/src/wirings/virtual-user/prepare-virtual-user-run.ts +0 -95
  489. package/src/wirings/virtual-user/run-virtual-user.test.ts +0 -774
  490. package/src/wirings/virtual-user/run-virtual-user.ts +0 -684
  491. package/src/wirings/virtual-user/virtual-user-agents.test.ts +0 -72
  492. package/src/wirings/virtual-user/virtual-user-agents.ts +0 -62
  493. package/src/wirings/virtual-user/virtual-user-catalogue.test.ts +0 -215
  494. package/src/wirings/virtual-user/virtual-user-catalogue.ts +0 -184
  495. package/src/wirings/virtual-user/virtual-user-derive.test.ts +0 -426
  496. package/src/wirings/virtual-user/virtual-user-derive.ts +0 -161
  497. package/src/wirings/virtual-user/virtual-user-dispositions.test.ts +0 -68
  498. package/src/wirings/virtual-user/virtual-user-dispositions.ts +0 -210
  499. package/src/wirings/virtual-user/virtual-user-intents.test.ts +0 -218
  500. package/src/wirings/virtual-user/virtual-user-intents.ts +0 -185
  501. package/src/wirings/virtual-user/virtual-user-rng.test.ts +0 -72
  502. package/src/wirings/virtual-user/virtual-user-rng.ts +0 -50
  503. package/src/wirings/virtual-user/virtual-user-run-store.ts +0 -131
  504. package/src/wirings/virtual-user/virtual-user-scaffold.test.ts +0 -854
  505. package/src/wirings/virtual-user/virtual-user-scaffold.ts +0 -664
  506. package/src/wirings/virtual-user/virtual-user-schedule-store.ts +0 -93
  507. package/src/wirings/virtual-user/virtual-user-schedule.test.ts +0 -280
  508. package/src/wirings/virtual-user/virtual-user-schedule.ts +0 -156
  509. package/src/wirings/virtual-user/virtual-user-target.ts +0 -47
  510. package/src/wirings/virtual-user/virtual-user.types.ts +0 -215
  511. package/src/wirings/workflow/dsl/workflow-dsl.types.ts +0 -709
  512. package/src/wirings/workflow/dsl/workflow-runner.ts +0 -25
  513. package/src/wirings/workflow/feature.test.ts +0 -131
  514. package/src/wirings/workflow/feature.ts +0 -59
  515. package/src/wirings/workflow/graph/graph-foreach-queued.test.ts +0 -569
  516. package/src/wirings/workflow/graph/graph-foreach.test.ts +0 -626
  517. package/src/wirings/workflow/graph/graph-node.ts +0 -115
  518. package/src/wirings/workflow/graph/graph-runner.test.ts +0 -1049
  519. package/src/wirings/workflow/graph/graph-runner.ts +0 -1376
  520. package/src/wirings/workflow/graph/graph-validation.test.ts +0 -27
  521. package/src/wirings/workflow/graph/graph-validation.ts +0 -41
  522. package/src/wirings/workflow/graph/template.test.ts +0 -49
  523. package/src/wirings/workflow/graph/template.ts +0 -42
  524. package/src/wirings/workflow/graph/wire-workflow-graph.ts +0 -39
  525. package/src/wirings/workflow/graph/workflow-graph.types.ts +0 -75
  526. package/src/wirings/workflow/index.ts +0 -107
  527. package/src/wirings/workflow/pikku-scenario-service.ts +0 -1013
  528. package/src/wirings/workflow/pikku-workflow-service.test.ts +0 -963
  529. package/src/wirings/workflow/pikku-workflow-service.ts +0 -1993
  530. package/src/wirings/workflow/run-timeline.test.ts +0 -199
  531. package/src/wirings/workflow/run-timeline.ts +0 -185
  532. package/src/wirings/workflow/scenario-cookie-jar.test.ts +0 -107
  533. package/src/wirings/workflow/scenario-cookie-jar.ts +0 -57
  534. package/src/wirings/workflow/scenario-expectations.test.ts +0 -238
  535. package/src/wirings/workflow/scenario-hooks.test.ts +0 -259
  536. package/src/wirings/workflow/scenario-poll.test.ts +0 -64
  537. package/src/wirings/workflow/scenario-poll.ts +0 -27
  538. package/src/wirings/workflow/scenario-prose.test.ts +0 -275
  539. package/src/wirings/workflow/scenario-prose.ts +0 -86
  540. package/src/wirings/workflow/scenario-run.types.ts +0 -193
  541. package/src/wirings/workflow/scenario-service.test.ts +0 -147
  542. package/src/wirings/workflow/scenario-step-guards.ts +0 -23
  543. package/src/wirings/workflow/scenario-step.test.ts +0 -800
  544. package/src/wirings/workflow/scenario-step.types.ts +0 -262
  545. package/src/wirings/workflow/scenario-surface.test.ts +0 -146
  546. package/src/wirings/workflow/scenario-surface.ts +0 -71
  547. package/src/wirings/workflow/scenario.types.ts +0 -63
  548. package/src/wirings/workflow/workflow-approval-audit.ts +0 -47
  549. package/src/wirings/workflow/workflow-approval-policy.test.ts +0 -594
  550. package/src/wirings/workflow/workflow-approval-policy.ts +0 -68
  551. package/src/wirings/workflow/workflow-approval.ts +0 -290
  552. package/src/wirings/workflow/workflow-child-run-session.test.ts +0 -168
  553. package/src/wirings/workflow/workflow-constants.ts +0 -66
  554. package/src/wirings/workflow/workflow-dispatch-durability.test.ts +0 -116
  555. package/src/wirings/workflow/workflow-dispatch-payload.test.ts +0 -55
  556. package/src/wirings/workflow/workflow-dispatch-relay.test.ts +0 -128
  557. package/src/wirings/workflow/workflow-errors.ts +0 -132
  558. package/src/wirings/workflow/workflow-inline-authority.test.ts +0 -175
  559. package/src/wirings/workflow/workflow-invocation-id.test.ts +0 -51
  560. package/src/wirings/workflow/workflow-invocation-id.ts +0 -28
  561. package/src/wirings/workflow/workflow-meta-resolver.ts +0 -45
  562. package/src/wirings/workflow/workflow-mirror.test.ts +0 -171
  563. package/src/wirings/workflow/workflow-missing-meta.test.ts +0 -92
  564. package/src/wirings/workflow/workflow-on-error.test.ts +0 -145
  565. package/src/wirings/workflow/workflow-queue-routing.ts +0 -129
  566. package/src/wirings/workflow/workflow-queue-wiring.ts +0 -149
  567. package/src/wirings/workflow/workflow-queue-workers.test.ts +0 -131
  568. package/src/wirings/workflow/workflow-queue-workers.ts +0 -66
  569. package/src/wirings/workflow/workflow-recovery.ts +0 -192
  570. package/src/wirings/workflow/workflow-replay-snapshot.test.ts +0 -140
  571. package/src/wirings/workflow/workflow-retry-policy.test.ts +0 -113
  572. package/src/wirings/workflow/workflow-run-authority.test.ts +0 -212
  573. package/src/wirings/workflow/workflow-run-context.test.ts +0 -172
  574. package/src/wirings/workflow/workflow-run-engine.types.ts +0 -92
  575. package/src/wirings/workflow/workflow-run-ownership.ts +0 -37
  576. package/src/wirings/workflow/workflow-run-polling.test.ts +0 -127
  577. package/src/wirings/workflow/workflow-stalled-recovery.test.ts +0 -152
  578. package/src/wirings/workflow/workflow-status-stream.test.ts +0 -354
  579. package/src/wirings/workflow/workflow-status-stream.ts +0 -144
  580. package/src/wirings/workflow/workflow-step-claim.ts +0 -46
  581. package/src/wirings/workflow/workflow-step-ordinal.test.ts +0 -94
  582. package/src/wirings/workflow/workflow-step-session.test.ts +0 -87
  583. package/src/wirings/workflow/workflow-suspend.ts +0 -61
  584. package/src/wirings/workflow/workflow-terminal-run-guard.test.ts +0 -105
  585. package/src/wirings/workflow/workflow.types.ts +0 -268
  586. package/src/wirings-stay-decoupled.test.ts +0 -130
  587. package/tsconfig.json +0 -14
  588. package/tsconfig.tsbuildinfo +0 -1
  589. package/tsconfig.type-tests.json +0 -15
@@ -1,1993 +0,0 @@
1
- import { runPikkuFunc } from '../../function/function-runner.js'
2
- import {
3
- getSingletonServices,
4
- getCreateWireServices,
5
- pikkuState,
6
- } from '../../pikku-state.js'
7
- import { getDurationInMilliseconds } from '../../time-utils.js'
8
- import type { CoreUserSession, PikkuRawWire } from '../../types/core.types.js'
9
- import type { SerializedError } from '../../errors/serialized-error.js'
10
- import type {
11
- GroupConcurrencyConfig,
12
- JobGroup,
13
- JobOptions,
14
- QueueService,
15
- } from '../queue/queue.types.js'
16
- import type {
17
- ApprovalOutcome,
18
- PikkuWorkflowWire,
19
- StepState,
20
- StepStatus,
21
- WorkflowApprovalOptions,
22
- WorkflowPlannedStep,
23
- WorkflowRun,
24
- WorkflowRunMirror,
25
- WorkflowRunStatus,
26
- WorkflowRunWire,
27
- WorkflowStatus,
28
- WorkflowVersionStatus,
29
- WorkflowQueueOptions,
30
- WorkflowServiceConfig,
31
- WorkflowStepOptions,
32
- } from './workflow.types.js'
33
- import {
34
- ChildWorkflowStartedException,
35
- continueGraph,
36
- executeGraphStep,
37
- runWorkflowGraph,
38
- runFromMeta,
39
- stripInstanceOrdinal,
40
- } from './graph/graph-runner.js'
41
- import type { WorkflowService } from '../../services/workflow-service.js'
42
- import type { ScenarioPersonas } from '../../services/personas-service.js'
43
- import { isExpectedError } from '../../errors/error-handler.js'
44
- import { PikkuMissingMetaError } from '../../errors/errors.js'
45
- import { RPCNotFoundError } from '../rpc/rpc-runner.js'
46
- import type { PikkuRPC } from '../rpc/rpc-types.js'
47
- import { deriveInvocationId } from './workflow-invocation-id.js'
48
- import { approvalDeciderFrom } from './workflow-approval-policy.js'
49
- import {
50
- buildRunTimeline,
51
- reconstructStateAt,
52
- type RunTimeline,
53
- type ReconstructedRunState,
54
- } from './run-timeline.js'
55
- import {
56
- DEFAULT_STEP_RETRIES,
57
- WORKFLOW_CHILD_POLL_MAX_MS,
58
- WORKFLOW_END_STATES,
59
- WORKFLOW_POLL_FACTOR,
60
- WORKFLOW_POLL_MIN_MS,
61
- WORKFLOW_TERMINAL_STATES,
62
- isRunSettled,
63
- } from './workflow-constants.js'
64
- import {
65
- WorkflowAsyncException,
66
- WorkflowCancelledException,
67
- WorkflowDispatchException,
68
- WorkflowNotFoundError,
69
- WorkflowRunCancelledError,
70
- WorkflowRunFailedError,
71
- WorkflowRunNotFoundError,
72
- WorkflowStepNameNotString,
73
- WorkflowSuspendedException,
74
- } from './workflow-errors.js'
75
- import type {
76
- RunContext,
77
- RunLifecycleContext,
78
- WorkflowRunEngine,
79
- WorkflowRunExtension,
80
- } from './workflow-run-engine.types.js'
81
- import { resolveWorkflowMeta } from './workflow-meta-resolver.js'
82
- import {
83
- jobGroupFor,
84
- orchestratorQueueName,
85
- resolveWorkflowConfig,
86
- stepDispatchTarget,
87
- stepJobOptions,
88
- stepWorkerQueueName,
89
- } from './workflow-queue-routing.js'
90
- import { wireWorkflowQueueWorkers } from './workflow-queue-wiring.js'
91
- import {
92
- approvalStepNameFor,
93
- evaluateApprovalStep,
94
- recordApprovalDecision,
95
- type ApprovalStore,
96
- } from './workflow-approval.js'
97
- import { auditApprovalDecision } from './workflow-approval-audit.js'
98
- import { recordSuspension, suspendStepNameFor } from './workflow-suspend.js'
99
- import { claimStepByReadThenWrite } from './workflow-step-claim.js'
100
- import {
101
- RedispatchBackoff,
102
- sweepStalledRuns,
103
- sweepUndispatchedSteps,
104
- } from './workflow-recovery.js'
105
-
106
- export abstract class PikkuWorkflowService implements WorkflowService {
107
- private runExtension?: WorkflowRunExtension
108
-
109
- private runContexts = new Map<string, RunContext>()
110
-
111
- private contextFor(runId: string): RunContext {
112
- let context = this.runContexts.get(runId)
113
- if (!context) {
114
- context = { activeExecutions: 0, ordinals: new Map() }
115
- this.runContexts.set(runId, context)
116
- }
117
- return context
118
- }
119
-
120
- private releaseContext(runId: string): void {
121
- const context = this.runContexts.get(runId)
122
- if (!context) return
123
- if (context.activeExecutions > 0) return
124
- this.runContexts.delete(runId)
125
- }
126
-
127
- private enterExecution(runId: string): RunContext {
128
- const context = this.contextFor(runId)
129
- context.activeExecutions++
130
- return context
131
- }
132
-
133
- private exitExecution(runId: string): void {
134
- const context = this.runContexts.get(runId)
135
- if (!context) return
136
- if (context.activeExecutions > 0) {
137
- context.activeExecutions--
138
- }
139
- if (context.activeExecutions === 0) {
140
- context.replay = undefined
141
- context.ordinals = new Map()
142
- context.lastStep = undefined
143
- }
144
- this.releaseContext(runId)
145
- }
146
-
147
- protected get logger() {
148
- return getSingletonServices()?.logger
149
- }
150
-
151
- protected mirror?: WorkflowRunMirror
152
-
153
- protected readonly queueStrategy: 'per-workflow' | 'shared-groups'
154
- protected readonly queueConcurrency: number
155
- protected readonly queueGroupConcurrency: number | GroupConcurrencyConfig
156
-
157
- constructor(
158
- options: {
159
- wireQueues?: boolean
160
- mirror?: WorkflowRunMirror
161
- } & WorkflowQueueOptions = {}
162
- ) {
163
- const wireQueues = options.wireQueues ?? true
164
- this.mirror = options.mirror
165
- this.queueStrategy = options.queueStrategy ?? 'per-workflow'
166
- this.queueConcurrency = options.queueConcurrency ?? 20
167
- this.queueGroupConcurrency = options.queueGroupConcurrency ?? 2
168
- if (wireQueues) {
169
- this.wireQueueWorkers()
170
- }
171
- }
172
-
173
- private async mirrored<T>(
174
- write: () => Promise<T>,
175
- mirror: (mirror: WorkflowRunMirror, written: T) => Promise<void>
176
- ): Promise<T> {
177
- const written = await write()
178
- if (this.mirror) {
179
- try {
180
- await mirror(this.mirror, written)
181
- } catch (err: any) {
182
- try {
183
- this.logger?.warn?.(
184
- `[pikku] WorkflowRunMirror write failed: ${err?.message ?? err}`
185
- )
186
- } catch {
187
- // knowledge: decisions/internals/workflow-run-mirror-is-never-a-source-of-truth.md
188
- }
189
- }
190
- }
191
- return written
192
- }
193
-
194
- public wireQueueWorkers(): void {
195
- wireWorkflowQueueWorkers({
196
- strategy: this.queueStrategy,
197
- concurrency: this.queueConcurrency,
198
- groupConcurrency: this.queueGroupConcurrency,
199
- getLogger: () => this.logger,
200
- })
201
- }
202
-
203
- protected async isInline(runId: string): Promise<boolean> {
204
- const context = this.runContexts.get(runId)
205
- if (context?.inline !== undefined) {
206
- return context.inline
207
- }
208
- const inline = (await this.getRunIdentity(runId))?.inline === true
209
- if (context) {
210
- context.inline = inline
211
- }
212
- return inline
213
- }
214
-
215
- public registerInlineRun(runId: string): void {
216
- this.contextFor(runId).inline = true
217
- }
218
-
219
- public unregisterInlineRun(runId: string): void {
220
- const context = this.runContexts.get(runId)
221
- if (!context) return
222
- context.inline = undefined
223
- this.releaseContext(runId)
224
- }
225
-
226
- public async registerWorkflowVersions(): Promise<void> {
227
- const allMeta = pikkuState(null, 'workflows', 'meta')
228
- for (const [name, meta] of Object.entries(allMeta)) {
229
- if (!meta.graphHash) continue
230
- await this.upsertWorkflowVersion(name, meta.graphHash, meta, meta.source)
231
- }
232
- }
233
-
234
- public async createRun(
235
- workflowName: string,
236
- input: any,
237
- inline: boolean,
238
- graphHash: string,
239
- wire: WorkflowRunWire,
240
- options?: {
241
- deterministic?: boolean
242
- plannedSteps?: WorkflowPlannedStep[]
243
- }
244
- ): Promise<string> {
245
- return this.mirrored(
246
- () =>
247
- this.createRunImpl(
248
- workflowName,
249
- input,
250
- inline,
251
- graphHash,
252
- wire,
253
- options
254
- ),
255
- (mirror, runId) =>
256
- mirror.createRun(
257
- runId,
258
- workflowName,
259
- input,
260
- inline,
261
- graphHash,
262
- wire,
263
- options
264
- )
265
- )
266
- }
267
-
268
- protected abstract createRunImpl(
269
- workflowName: string,
270
- input: any,
271
- inline: boolean,
272
- graphHash: string,
273
- wire: WorkflowRunWire,
274
- options?: {
275
- deterministic?: boolean
276
- plannedSteps?: WorkflowPlannedStep[]
277
- }
278
- ): Promise<string>
279
-
280
- abstract getRun(id: string): Promise<WorkflowRun | null>
281
-
282
- async getRunStatus(id: string): Promise<WorkflowRunStatus | null> {
283
- const run = await this.getRun(id)
284
- if (!run) return null
285
-
286
- const history = await this.getRunHistory(id)
287
- const terminalStatuses = new Set(['completed', 'failed', 'cancelled'])
288
-
289
- const stepMap = new Map<
290
- string,
291
- {
292
- status: StepStatus
293
- startedAt?: Date
294
- completedAt?: Date
295
- attempts: number
296
- }
297
- >()
298
- for (const step of history) {
299
- const existing = stepMap.get(step.stepName)
300
- if (!existing || step.updatedAt > existing.completedAt!) {
301
- stepMap.set(step.stepName, {
302
- status: step.status,
303
- startedAt: step.runningAt ?? step.createdAt,
304
- completedAt: step.succeededAt ?? step.failedAt,
305
- attempts: step.attemptCount,
306
- })
307
- }
308
- }
309
-
310
- const steps = [...stepMap.entries()].map(([name, s]) => ({
311
- name,
312
- status: s.status,
313
- duration:
314
- s.startedAt && s.completedAt
315
- ? s.completedAt.getTime() - s.startedAt.getTime()
316
- : undefined,
317
- attempts: s.attempts,
318
- }))
319
-
320
- return {
321
- id: run.id,
322
- status: run.status,
323
- startedAt: run.createdAt,
324
- completedAt: terminalStatuses.has(run.status) ? run.updatedAt : undefined,
325
- deterministic: run.deterministic,
326
- plannedSteps: run.plannedSteps,
327
- steps,
328
- output: run.status === 'completed' ? run.output : undefined,
329
- error: run.error
330
- ? { message: run.error.message ?? 'Unknown error' }
331
- : undefined,
332
- }
333
- }
334
-
335
- public async getRunTimeline(id: string): Promise<RunTimeline | null> {
336
- const run = await this.getRun(id)
337
- if (!run) return null
338
- return buildRunTimeline(await this.getRunHistory(id))
339
- }
340
-
341
- public async reconstructRunStateAt(
342
- id: string,
343
- at?: number | Date
344
- ): Promise<ReconstructedRunState | null> {
345
- const timeline = await this.getRunTimeline(id)
346
- if (!timeline) return null
347
- return reconstructStateAt(timeline, at ?? timeline.length - 1)
348
- }
349
-
350
- abstract getRunHistory(
351
- runId: string
352
- ): Promise<Array<StepState & { stepName: string }>>
353
-
354
- public async updateRunStatus(
355
- id: string,
356
- status: WorkflowStatus,
357
- output?: any,
358
- error?: SerializedError
359
- ): Promise<void> {
360
- await this.mirrored(
361
- () => this.updateRunStatusImpl(id, status, output, error),
362
- (mirror) => mirror.updateRunStatus(id, status, output, error)
363
- )
364
- if (WORKFLOW_TERMINAL_STATES.has(status)) {
365
- this.runExtension?.detachRunContext(id)
366
- this.releaseContext(id)
367
- }
368
- }
369
-
370
- protected abstract updateRunStatusImpl(
371
- id: string,
372
- status: WorkflowStatus,
373
- output?: any,
374
- error?: SerializedError
375
- ): Promise<void>
376
-
377
- public async insertStepState(
378
- runId: string,
379
- stepName: string,
380
- rpcName: string | null,
381
- data: any,
382
- stepOptions?: WorkflowStepOptions,
383
- fromStepName?: string
384
- ): Promise<StepState> {
385
- return this.mirrored(
386
- () =>
387
- this.insertStepStateImpl(
388
- runId,
389
- stepName,
390
- rpcName,
391
- data,
392
- stepOptions,
393
- fromStepName
394
- ),
395
- (mirror, step) =>
396
- mirror.insertStepState(runId, { ...step, stepName, rpcName, data })
397
- )
398
- }
399
-
400
- protected abstract insertStepStateImpl(
401
- runId: string,
402
- stepName: string,
403
- rpcName: string | null,
404
- data: any,
405
- stepOptions?: WorkflowStepOptions,
406
- fromStepName?: string
407
- ): Promise<StepState>
408
-
409
- abstract getStepState(runId: string, stepName: string): Promise<StepState>
410
-
411
- public async setStepRunning(stepId: string): Promise<void> {
412
- await this.mirrored(
413
- () => this.setStepRunningImpl(stepId),
414
- (mirror) => mirror.setStepRunning(stepId)
415
- )
416
- }
417
-
418
- protected abstract setStepRunningImpl(stepId: string): Promise<void>
419
-
420
- public async setStepScheduled(stepId: string): Promise<void> {
421
- await this.mirrored(
422
- () => this.setStepScheduledImpl(stepId),
423
- (mirror) => mirror.setStepScheduled(stepId)
424
- )
425
- }
426
-
427
- protected abstract setStepScheduledImpl(stepId: string): Promise<void>
428
-
429
- public async setStepResult(stepId: string, result: any): Promise<void> {
430
- await this.mirrored(
431
- () => this.setStepResultImpl(stepId, result),
432
- (mirror) => mirror.setStepResult(stepId, result)
433
- )
434
- }
435
-
436
- protected abstract setStepResultImpl(
437
- stepId: string,
438
- result: any
439
- ): Promise<void>
440
-
441
- public async setStepChildRunId(
442
- stepId: string,
443
- childRunId: string
444
- ): Promise<void> {
445
- await this.mirrored(
446
- () => this.setStepChildRunIdImpl(stepId, childRunId),
447
- (mirror) => mirror.setStepChildRunId(stepId, childRunId)
448
- )
449
- }
450
-
451
- protected abstract setStepChildRunIdImpl(
452
- stepId: string,
453
- childRunId: string
454
- ): Promise<void>
455
-
456
- public async setStepError(stepId: string, error: Error): Promise<void> {
457
- await this.mirrored(
458
- () => this.setStepErrorImpl(stepId, error),
459
- (mirror) => {
460
- const serialized: SerializedError = {
461
- message: error.message,
462
- stack: error.stack,
463
- code: (error as { code?: string }).code,
464
- expected: isExpectedError(error),
465
- }
466
- return mirror.setStepError(stepId, serialized)
467
- }
468
- )
469
- }
470
-
471
- protected abstract setStepErrorImpl(
472
- stepId: string,
473
- error: Error
474
- ): Promise<void>
475
-
476
- public async createRetryAttempt(
477
- failedStepId: string,
478
- status: 'pending' | 'running'
479
- ): Promise<StepState> {
480
- return this.mirrored(
481
- () => this.createRetryAttemptImpl(failedStepId, status),
482
- (mirror, newStep) =>
483
- mirror.createRetryAttempt(failedStepId, {
484
- ...newStep,
485
- stepName: (newStep as { stepName?: string }).stepName ?? '',
486
- })
487
- )
488
- }
489
-
490
- protected abstract createRetryAttemptImpl(
491
- failedStepId: string,
492
- status: 'pending' | 'running'
493
- ): Promise<StepState>
494
-
495
- abstract withRunLock<T>(id: string, fn: () => Promise<T>): Promise<T>
496
-
497
- abstract withStepLock<T>(
498
- runId: string,
499
- stepName: string,
500
- fn: () => Promise<T>
501
- ): Promise<T>
502
-
503
- abstract close(): Promise<void>
504
-
505
- abstract getCompletedGraphState(runId: string): Promise<{
506
- completedNodeIds: string[]
507
- failedNodeIds: string[]
508
- branchKeys: Record<string, string>
509
- }>
510
-
511
- abstract getStepInstances(runId: string): Promise<
512
- Array<{
513
- stepName: string
514
- status: StepStatus
515
- fromStepName?: string
516
- }>
517
- >
518
-
519
- abstract getNodeResults(
520
- runId: string,
521
- nodeIds: string[]
522
- ): Promise<Record<string, any>>
523
-
524
- public async setBranchTaken(
525
- stepId: string,
526
- branchKey: string
527
- ): Promise<void> {
528
- await this.mirrored(
529
- () => this.setBranchTakenImpl(stepId, branchKey),
530
- (mirror) => mirror.setBranchTaken(stepId, branchKey)
531
- )
532
- }
533
-
534
- protected abstract setBranchTakenImpl(
535
- stepId: string,
536
- branchKey: string
537
- ): Promise<void>
538
-
539
- public async updateRunState(
540
- runId: string,
541
- name: string,
542
- value: unknown
543
- ): Promise<void> {
544
- await this.mirrored(
545
- () => this.updateRunStateImpl(runId, name, value),
546
- (mirror) => mirror.updateRunState(runId, name, value)
547
- )
548
- }
549
-
550
- protected abstract updateRunStateImpl(
551
- runId: string,
552
- name: string,
553
- value: unknown
554
- ): Promise<void>
555
-
556
- abstract getRunState(runId: string): Promise<Record<string, unknown>>
557
-
558
- public async upsertWorkflowVersion(
559
- name: string,
560
- graphHash: string,
561
- graph: any,
562
- source: string,
563
- status?: WorkflowVersionStatus
564
- ): Promise<void> {
565
- await this.mirrored(
566
- () =>
567
- this.upsertWorkflowVersionImpl(name, graphHash, graph, source, status),
568
- (mirror) =>
569
- mirror.upsertWorkflowVersion(name, graphHash, graph, source, status)
570
- )
571
- }
572
-
573
- protected abstract upsertWorkflowVersionImpl(
574
- name: string,
575
- graphHash: string,
576
- graph: any,
577
- source: string,
578
- status?: WorkflowVersionStatus
579
- ): Promise<void>
580
-
581
- public async updateWorkflowVersionStatus(
582
- name: string,
583
- graphHash: string,
584
- status: WorkflowVersionStatus
585
- ): Promise<void> {
586
- await this.mirrored(
587
- () => this.updateWorkflowVersionStatusImpl(name, graphHash, status),
588
- (mirror) => mirror.updateWorkflowVersionStatus(name, graphHash, status)
589
- )
590
- }
591
-
592
- protected abstract updateWorkflowVersionStatusImpl(
593
- name: string,
594
- graphHash: string,
595
- status: WorkflowVersionStatus
596
- ): Promise<void>
597
-
598
- abstract getWorkflowVersion(
599
- name: string,
600
- graphHash: string
601
- ): Promise<{ graph: any; source: string } | null>
602
-
603
- public async resumeWorkflow(
604
- runId: string,
605
- workflowName?: string
606
- ): Promise<void> {
607
- const queueService = this.verifyQueueService()
608
- if (!workflowName) {
609
- const run = await this.getRun(runId)
610
- workflowName = run?.workflow
611
- }
612
- await queueService.add(
613
- this.getOrchestratorQueueName(workflowName),
614
- { runId },
615
- {
616
- ...this.resolveStepJobOptions(),
617
- group: this.getJobGroup(workflowName),
618
- }
619
- )
620
- }
621
-
622
- /**
623
- * Ids of runs that are stalled: still `running`, with no step in a state that
624
- * something is expected to complete (`running`, `scheduled`, `suspended`),
625
- * and no step activity since `before`.
626
- *
627
- * Returns nothing by default so a store that cannot express the query keeps
628
- * working unchanged; a store that overrides it gains crash recovery through
629
- * `recoverStalledRuns`.
630
- */
631
- protected async findStalledRunIds(
632
- _before: Date,
633
- _limit: number
634
- ): Promise<string[]> {
635
- return []
636
- }
637
-
638
- /**
639
- * Runs holding a step that has sat `pending` since before `before`, paired
640
- * with the step that flagged them.
641
- *
642
- * Returns nothing by default so a store that cannot express the query keeps
643
- * working unchanged — and, because it does not opt in, gains no re-dispatches
644
- * either. A store must have an atomic `claimStepForExecution` before
645
- * overriding this, or no concurrency for one to exclude: the relay makes
646
- * duplicate dispatch routine, and the claim is what keeps a duplicate from
647
- * becoming a second execution. Every `@pikku/kysely` dialect qualifies on its
648
- * status-guarded claim, `mongodb` on the same claim expressed as a
649
- * single-document update, `in-memory` on being inline and single-process —
650
- * none of them overrides this yet.
651
- */
652
- protected async findUndispatchedSteps(
653
- _before: Date,
654
- _limit: number
655
- ): Promise<Array<{ runId: string; stepId: string }>> {
656
- return []
657
- }
658
-
659
- private readonly redispatchBackoff = new RedispatchBackoff()
660
-
661
- private get sweepDeps() {
662
- return {
663
- resume: (runId: string) => this.resumeWorkflow(runId),
664
- logger: getSingletonServices()?.logger,
665
- }
666
- }
667
-
668
- /**
669
- * Re-drive runs whose next move was lost. Not self-starting — call it from a
670
- * scheduled task at whatever interval suits the workload.
671
- */
672
- public async recoverStalledRuns(options?: {
673
- stalledAfterMs?: number
674
- limit?: number
675
- }): Promise<{ resumed: string[] }> {
676
- return sweepStalledRuns(
677
- (before, limit) => this.findStalledRunIds(before, limit),
678
- this.redispatchBackoff,
679
- options,
680
- this.sweepDeps
681
- )
682
- }
683
-
684
- /**
685
- * Re-drive steps whose dispatch was lost. Not self-starting — call it from a
686
- * scheduled task; ~30s suits a queue whose `pending`→`running` latency is
687
- * well under that.
688
- */
689
- public async relayUndispatchedSteps(options?: {
690
- undispatchedAfterMs?: number
691
- limit?: number
692
- }): Promise<{ redispatched: string[] }> {
693
- return sweepUndispatchedSteps(
694
- (before, limit) => this.findUndispatchedSteps(before, limit),
695
- this.redispatchBackoff,
696
- options,
697
- this.sweepDeps
698
- )
699
- }
700
-
701
- protected resolveStepJobOptions(
702
- stepOptions?: WorkflowStepOptions
703
- ): JobOptions {
704
- return stepJobOptions(stepOptions)
705
- }
706
-
707
- public async queueStepWorker(
708
- runId: string,
709
- stepName: string,
710
- rpcName: string,
711
- data: any,
712
- stepOptions?: WorkflowStepOptions,
713
- fromStepName?: string
714
- ): Promise<void> {
715
- const queueService = this.verifyQueueService()
716
- await queueService.add(
717
- this.getStepWorkerQueueName(rpcName),
718
- { runId, stepName, rpcName, data, fromStepName },
719
- {
720
- ...this.resolveStepJobOptions(stepOptions),
721
- group: this.getJobGroup(rpcName),
722
- }
723
- )
724
- }
725
-
726
- public async executeWorkflowSleepCompleted(
727
- runId: string,
728
- stepId: string
729
- ): Promise<void> {
730
- await this.setStepResult(stepId, null)
731
- await this.resumeWorkflow(runId)
732
- }
733
-
734
- protected async scheduleOrchestratorRetry(
735
- runId: string,
736
- retryDelay?: number | string,
737
- workflowName?: string
738
- ): Promise<void> {
739
- const queueService = this.verifyQueueService()
740
- if (!workflowName) {
741
- const run = await this.getRun(runId)
742
- workflowName = run?.workflow
743
- }
744
- await queueService.add(
745
- this.getOrchestratorQueueName(workflowName),
746
- { runId },
747
- {
748
- ...(retryDelay
749
- ? { delay: getDurationInMilliseconds(retryDelay) }
750
- : undefined),
751
- group: this.getJobGroup(workflowName),
752
- }
753
- )
754
- }
755
-
756
- protected async dispatchStep(
757
- runId: string,
758
- stepName: string,
759
- rpcName: string,
760
- data: unknown,
761
- stepOptions?: WorkflowStepOptions,
762
- fromStepName?: string
763
- ): Promise<boolean> {
764
- const target = await stepDispatchTarget(rpcName, stepName, () =>
765
- this.isInline(runId)
766
- )
767
- if (target === 'inline') {
768
- return false
769
- }
770
- try {
771
- await getSingletonServices()!.queueService!.add(
772
- this.getStepWorkerQueueName(rpcName),
773
- { runId, stepName, rpcName, data, fromStepName },
774
- {
775
- ...this.resolveStepJobOptions(stepOptions),
776
- group: this.getJobGroup(rpcName),
777
- }
778
- )
779
- } catch (cause) {
780
- throw new WorkflowDispatchException(runId, stepName, { cause })
781
- }
782
- return true
783
- }
784
-
785
- protected async scheduleSleep(
786
- runId: string,
787
- stepId: string,
788
- duration: number | string
789
- ): Promise<boolean> {
790
- if (
791
- (await this.isInline(runId)) ||
792
- !getSingletonServices()?.schedulerService
793
- ) {
794
- return false
795
- }
796
- await getSingletonServices()!.schedulerService!.scheduleRPC(
797
- duration,
798
- this.getConfig().sleeperRPCName,
799
- { runId, stepId }
800
- )
801
- return true
802
- }
803
-
804
- public setRunExtension<T extends WorkflowRunExtension>(
805
- create: (engine: WorkflowRunEngine) => T
806
- ): T {
807
- const engine: WorkflowRunEngine = {
808
- inlineStep: this.inlineStep.bind(this),
809
- updateRunStatus: this.updateRunStatus.bind(this),
810
- onChildWorkflowFailed: this.onChildWorkflowFailed.bind(this),
811
- verifyStepName: this.verifyStepName.bind(this),
812
- }
813
- const extension = create(engine)
814
- this.runExtension = extension
815
- return extension
816
- }
817
-
818
- public getRunExtension(): WorkflowRunExtension | undefined {
819
- return this.runExtension
820
- }
821
-
822
- public async startWorkflow<I>(
823
- name: string,
824
- input: I,
825
- wire: WorkflowRunWire,
826
- rpcService: PikkuRPC,
827
- options?: {
828
- inline?: boolean
829
- startNode?: string
830
- actors?: ScenarioPersonas
831
- onRunCreated?: (runId: string) => void
832
- }
833
- ): Promise<{ runId: string }> {
834
- const resolved = resolveWorkflowMeta(name)
835
- const workflowMeta = resolved?.meta
836
- const packageName = resolved?.packageName ?? null
837
-
838
- if (!workflowMeta) {
839
- throw new WorkflowNotFoundError(name)
840
- }
841
-
842
- if (workflowMeta.source === 'graph') {
843
- // A caller-supplied startNode must be one of the graph's declared entry
844
- // nodes. startWorkflow is the boundary the public
845
- // `/workflow/:name/graph/:nodeId` route and triggers enter through, so
846
- // without this a request could name any dependency-free node — one whose
847
- // input reads only `trigger` — and fire its RPC directly with
848
- // attacker-chosen data, skipping every upstream eligibility, validation or
849
- // approval node. (Internal resume/replay drives runWorkflowGraph directly
850
- // and is unaffected.)
851
- if (
852
- options?.startNode &&
853
- !(workflowMeta.entryNodeIds ?? []).includes(options.startNode)
854
- ) {
855
- throw new Error(
856
- `Workflow graph '${name}': '${options.startNode}' is not a declared entry node`
857
- )
858
- }
859
- const shouldInline =
860
- options?.inline || !getSingletonServices()?.queueService
861
- return runWorkflowGraph(
862
- this,
863
- name,
864
- input,
865
- rpcService,
866
- shouldInline,
867
- options?.startNode,
868
- wire,
869
- workflowMeta
870
- )
871
- }
872
-
873
- const registrations = pikkuState(packageName, 'workflows', 'registrations')
874
- const workflow = registrations.get(resolved?.resolvedName ?? name)
875
-
876
- if (!workflow) {
877
- throw new WorkflowNotFoundError(name)
878
- }
879
-
880
- if (!workflowMeta.graphHash) {
881
- throw new Error(`Missing workflow graphHash for '${name}'`)
882
- }
883
-
884
- const shouldInline =
885
- options?.inline || !getSingletonServices()?.queueService
886
-
887
- const runId = await this.createRun(
888
- name,
889
- input,
890
- shouldInline,
891
- workflowMeta.graphHash,
892
- wire,
893
- {
894
- deterministic: workflowMeta.deterministic,
895
- plannedSteps: workflowMeta.plannedSteps,
896
- }
897
- )
898
-
899
- options?.onRunCreated?.(runId)
900
-
901
- await this.runExtension?.attachRunContext(runId, workflowMeta, options)
902
-
903
- if (shouldInline) {
904
- this.registerInlineRun(runId)
905
- try {
906
- await this.runWorkflowJob(runId, rpcService)
907
- } catch (error: any) {
908
- if (
909
- error.name !== 'WorkflowAsyncException' &&
910
- error.name !== 'WorkflowCancelledException' &&
911
- error.name !== 'WorkflowSuspendedException' &&
912
- error.name !== 'WorkflowDispatchException'
913
- ) {
914
- await this.updateRunStatus(runId, 'failed', undefined, {
915
- name: error.name,
916
- message: error.message,
917
- stack: error.stack,
918
- })
919
- getSingletonServices()!.logger.error(
920
- `Workflow ${name} (run ${runId}) failed:`,
921
- isExpectedError(error) ? error.message : error
922
- )
923
- throw error
924
- }
925
- if (error.name === 'WorkflowDispatchException') {
926
- throw error
927
- }
928
- } finally {
929
- this.unregisterInlineRun(runId)
930
- this.runExtension?.detachRunContext(runId)
931
- }
932
- } else {
933
- await this.resumeWorkflow(runId)
934
- }
935
-
936
- return { runId }
937
- }
938
-
939
- public async runToCompletion<I>(
940
- name: string,
941
- input: I,
942
- rpcService: PikkuRPC,
943
- options?: { pollIntervalMs?: number; wire?: WorkflowRunWire }
944
- ): Promise<any> {
945
- const { runId } = await this.startWorkflow(
946
- name,
947
- input,
948
- options?.wire ?? { type: 'internal' },
949
- rpcService,
950
- { inline: true }
951
- )
952
- const run = await this.awaitRunEnd(runId, options?.pollIntervalMs ?? 1000)
953
- if (run.status === 'failed') {
954
- throw new WorkflowRunFailedError(run.error?.message)
955
- }
956
- if (run.status === 'cancelled') {
957
- throw new WorkflowRunCancelledError()
958
- }
959
- return run.output
960
- }
961
-
962
- protected async awaitRunEnd(
963
- runId: string,
964
- maxIntervalMs: number
965
- ): Promise<WorkflowRun> {
966
- let interval = Math.min(WORKFLOW_POLL_MIN_MS, maxIntervalMs)
967
- while (true) {
968
- const run = await this.getRun(runId)
969
- if (!run) {
970
- throw new WorkflowRunNotFoundError(runId)
971
- }
972
- if (WORKFLOW_END_STATES.has(run.status)) {
973
- return run
974
- }
975
- await this.waitBeforeNextRead(interval)
976
- interval = Math.min(interval * WORKFLOW_POLL_FACTOR, maxIntervalMs)
977
- }
978
- }
979
-
980
- protected async waitBeforeNextRead(ms: number): Promise<void> {
981
- await new Promise((resolve) => setTimeout(resolve, ms))
982
- }
983
-
984
- protected async listStepStates(
985
- _runId: string
986
- ): Promise<Array<StepState & { stepName: string }> | null> {
987
- return null
988
- }
989
-
990
- private async beginReplay(runId: string): Promise<void> {
991
- const context = this.enterExecution(runId)
992
- context.ordinals = new Map()
993
- context.lastStep = undefined
994
- context.replay = {}
995
- const steps = await this.listStepStates(runId)
996
- if (steps) {
997
- context.replay.steps = new Map(steps.map((step) => [step.stepName, step]))
998
- }
999
- }
1000
-
1001
- private endReplay(runId: string): void {
1002
- this.exitExecution(runId)
1003
- }
1004
-
1005
- private async loadOrCreateStep(
1006
- runId: string,
1007
- stepName: string,
1008
- create: () => Promise<StepState>
1009
- ): Promise<StepState> {
1010
- const snapshot = this.runContexts.get(runId)?.replay?.steps
1011
- if (snapshot) {
1012
- const cached = snapshot.get(stepName)
1013
- if (cached) {
1014
- return cached
1015
- }
1016
- } else {
1017
- try {
1018
- return await this.getStepState(runId, stepName)
1019
- } catch {
1020
- // knowledge: decisions/internals/workflow-replay-reads-its-steps-once-and-caches-only-the-immutable-half.md
1021
- }
1022
- }
1023
-
1024
- let step: StepState
1025
- try {
1026
- step = await create()
1027
- } catch (error) {
1028
- try {
1029
- step = await this.getStepState(runId, stepName)
1030
- } catch {
1031
- throw error
1032
- }
1033
- }
1034
- snapshot?.set(stepName, step)
1035
- return step
1036
- }
1037
-
1038
- private async getRunIdentity(runId: string): Promise<WorkflowRun | null> {
1039
- const replay = this.runContexts.get(runId)?.replay
1040
- if (replay?.run) {
1041
- return replay.run
1042
- }
1043
- const run = await this.getRun(runId)
1044
- if (run && replay) {
1045
- replay.run = run
1046
- }
1047
- return run
1048
- }
1049
-
1050
- private lastStepName(runId: string): string | undefined {
1051
- return this.runContexts.get(runId)?.lastStep
1052
- }
1053
-
1054
- private nextStepKey(runId: string, logicalStepName: string): string {
1055
- const context = this.contextFor(runId)
1056
- const ordinal = context.ordinals.get(logicalStepName) ?? 0
1057
- context.ordinals.set(logicalStepName, ordinal + 1)
1058
- const stepName =
1059
- ordinal === 0 ? logicalStepName : `${logicalStepName}#${ordinal}`
1060
- context.lastStep = stepName
1061
- return stepName
1062
- }
1063
-
1064
- public async runWorkflowJob(
1065
- runId: string,
1066
- rpcService: PikkuRPC
1067
- ): Promise<void> {
1068
- await this.beginReplay(runId)
1069
- try {
1070
- await this.runWorkflowJobInner(runId, rpcService)
1071
- } finally {
1072
- this.endReplay(runId)
1073
- }
1074
- }
1075
-
1076
- private async runWorkflowJobInner(
1077
- runId: string,
1078
- rpcService: PikkuRPC
1079
- ): Promise<void> {
1080
- const run = await this.getRunIdentity(runId)
1081
- if (!run) {
1082
- throw new WorkflowRunNotFoundError(runId)
1083
- }
1084
- if (isRunSettled(run.status)) return
1085
-
1086
- const resolved = resolveWorkflowMeta(run.workflow)
1087
- const workflowMeta = resolved?.meta
1088
- const pkgName = resolved?.packageName ?? null
1089
-
1090
- if (
1091
- run.graphHash &&
1092
- workflowMeta?.graphHash &&
1093
- run.graphHash !== workflowMeta.graphHash
1094
- ) {
1095
- await this.runVersionMismatchFallback(run, workflowMeta, rpcService)
1096
- return
1097
- }
1098
-
1099
- if (workflowMeta?.source === 'graph') {
1100
- await continueGraph(this, runId, run.workflow)
1101
- const updatedRun = await this.getRun(runId)
1102
- if (updatedRun?.status === 'completed') {
1103
- await this.onChildWorkflowCompleted(updatedRun, updatedRun.output)
1104
- } else if (
1105
- updatedRun?.status === 'failed' ||
1106
- updatedRun?.status === 'cancelled'
1107
- ) {
1108
- await this.onChildWorkflowFailed(
1109
- updatedRun,
1110
- new Error(updatedRun.error?.message || 'Child workflow failed')
1111
- )
1112
- }
1113
- return
1114
- }
1115
-
1116
- const registrations = pikkuState(pkgName, 'workflows', 'registrations')
1117
- if (!workflowMeta) {
1118
- throw new PikkuMissingMetaError(
1119
- `Missing generated metadata for workflow '${run.workflow}'`
1120
- )
1121
- }
1122
-
1123
- const workflow = registrations.get(resolved?.resolvedName ?? run.workflow)
1124
- if (!workflow) {
1125
- throw new WorkflowNotFoundError(run.workflow)
1126
- }
1127
-
1128
- await this.withRunLock(runId, async () => {
1129
- const addonNs = run.workflow.includes(':')
1130
- ? run.workflow.substring(0, run.workflow.indexOf(':'))
1131
- : null
1132
- const workflowWire = this.createWorkflowWire(
1133
- run.workflow,
1134
- runId,
1135
- rpcService,
1136
- addonNs
1137
- )
1138
- workflowWire.pikkuUserId = run.wire?.pikkuUserId
1139
- // No `rpc` yet — runPikkuFunc attaches it lazily for the invocation.
1140
- const wire: PikkuRawWire = {
1141
- workflow: workflowWire,
1142
- pikkuUserId: run.wire?.pikkuUserId,
1143
- }
1144
- this.runExtension?.decorateRunWire(wire, {
1145
- runId,
1146
- workflowMeta,
1147
- workflowWire,
1148
- })
1149
-
1150
- const lifecycle: RunLifecycleContext = {
1151
- runId,
1152
- run,
1153
- workflowMeta,
1154
- workflow,
1155
- wire,
1156
- packageName: pkgName,
1157
- }
1158
-
1159
- let outcome: 'completed' | 'failed' | 'interrupted' = 'completed'
1160
- let failure: any
1161
- try {
1162
- await this.runExtension?.onBeforeRunFunc(lifecycle)
1163
-
1164
- const result = await runPikkuFunc(
1165
- 'workflow',
1166
- workflowMeta.name,
1167
- workflowMeta.pikkuFuncId,
1168
- {
1169
- singletonServices: getSingletonServices()!,
1170
- wire,
1171
- createWireServices: getCreateWireServices(),
1172
- data: () => run.input,
1173
- packageName: pkgName ?? undefined,
1174
- }
1175
- )
1176
-
1177
- await this.updateRunStatus(runId, 'completed', result)
1178
- await this.onChildWorkflowCompleted(run, result)
1179
- } catch (error: any) {
1180
- failure = error
1181
-
1182
- if (error instanceof WorkflowAsyncException) {
1183
- outcome = 'interrupted'
1184
- throw error
1185
- }
1186
-
1187
- if (error instanceof WorkflowCancelledException) {
1188
- outcome = 'failed'
1189
- await this.updateRunStatus(runId, 'cancelled', undefined, {
1190
- message: error.message || 'Workflow cancelled',
1191
- stack: '',
1192
- code: 'WORKFLOW_CANCELLED',
1193
- })
1194
- await this.onChildWorkflowFailed(run, error)
1195
- throw error
1196
- }
1197
-
1198
- if (error instanceof WorkflowSuspendedException) {
1199
- outcome = 'interrupted'
1200
- await this.updateRunStatus(runId, 'suspended', undefined, {
1201
- message: error.message || 'Workflow suspended',
1202
- stack: '',
1203
- code: 'WORKFLOW_SUSPENDED',
1204
- })
1205
- throw error
1206
- }
1207
-
1208
- outcome = 'failed'
1209
- await this.updateRunStatus(runId, 'failed', undefined, {
1210
- message: error.message,
1211
- stack: error.stack,
1212
- code: error.code,
1213
- })
1214
- await this.onChildWorkflowFailed(run, error)
1215
-
1216
- throw error
1217
- } finally {
1218
- await this.runExtension?.onAfterRunFunc(lifecycle, outcome, failure)
1219
- }
1220
- })
1221
- }
1222
-
1223
- private async onChildWorkflowCompleted(
1224
- childRun: WorkflowRun,
1225
- result: any
1226
- ): Promise<void> {
1227
- const { parentRunId, parentStepId } = childRun.wire ?? {}
1228
- if (!parentRunId || !parentStepId) return
1229
-
1230
- this.logger?.debug(
1231
- `Child workflow ${childRun.id} completed, updating parent step ${parentStepId}`
1232
- )
1233
- await this.setStepResult(parentStepId, result)
1234
- await this.resumeWorkflow(parentRunId)
1235
- }
1236
-
1237
- protected async onChildWorkflowFailed(
1238
- childRun: WorkflowRun,
1239
- error: Error
1240
- ): Promise<void> {
1241
- const { parentRunId, parentStepId } = childRun.wire ?? {}
1242
- if (!parentRunId || !parentStepId) return
1243
-
1244
- this.logger?.debug(
1245
- `Child workflow ${childRun.id} failed, updating parent step ${parentStepId}`
1246
- )
1247
- await this.setStepError(parentStepId, error)
1248
- await this.resumeWorkflow(parentRunId)
1249
- }
1250
-
1251
- private async runVersionMismatchFallback(
1252
- run: WorkflowRun,
1253
- currentMeta: { source: string },
1254
- rpcService: PikkuRPC
1255
- ): Promise<void> {
1256
- const source = currentMeta.source
1257
-
1258
- if (source === 'complex') {
1259
- await this.updateRunStatus(run.id, 'failed', undefined, {
1260
- message: `Workflow '${run.workflow}' definition changed. Complex workflows with inline steps cannot be migrated.`,
1261
- stack: '',
1262
- code: 'VERSION_CONFLICT',
1263
- })
1264
- return
1265
- }
1266
-
1267
- const version = await this.getWorkflowVersion(run.workflow, run.graphHash!)
1268
- if (!version) {
1269
- await this.updateRunStatus(run.id, 'failed', undefined, {
1270
- message: `Workflow '${run.workflow}' version '${run.graphHash}' not found. Cannot resume with changed definition.`,
1271
- stack: '',
1272
- code: 'VERSION_NOT_FOUND',
1273
- })
1274
- return
1275
- }
1276
-
1277
- await runFromMeta(this, run.id, version.graph, rpcService)
1278
- }
1279
-
1280
- public async executeWorkflowStep(
1281
- runId: string,
1282
- stepName: string,
1283
- rpcName: string,
1284
- data: any,
1285
- rpcService: PikkuRPC
1286
- ): Promise<void> {
1287
- this.enterExecution(runId)
1288
- try {
1289
- await this.executeWorkflowStepInner(
1290
- runId,
1291
- stepName,
1292
- rpcName,
1293
- data,
1294
- rpcService
1295
- )
1296
- } finally {
1297
- this.exitExecution(runId)
1298
- }
1299
- }
1300
-
1301
- /**
1302
- * Take sole ownership of a step before it runs, returning the state to run
1303
- * under — or `null` when another dispatch already owns it.
1304
- *
1305
- * Dispatch is at-least-once by design: the relay re-dispatches steps it
1306
- * believes were dropped, and a queue can redeliver a job it already handed
1307
- * out. This is the one place that keeps a duplicate dispatch from becoming a
1308
- * second execution of a side-effecting step, so it is only as strong as the
1309
- * exclusion it is built on — and `withStepLock` excludes nothing unless the
1310
- * store backs it with a real primitive. A store able to express the decision
1311
- * as one conditional write should override this rather than reach for a lock,
1312
- * which is what `@pikku/kysely` does with a status-guarded `UPDATE`.
1313
- */
1314
- protected async claimStepForExecution(
1315
- runId: string,
1316
- stepName: string,
1317
- rpcName: string
1318
- ): Promise<StepState | null> {
1319
- return this.withStepLock(runId, stepName, () =>
1320
- claimStepByReadThenWrite(this, runId, stepName, rpcName)
1321
- )
1322
- }
1323
-
1324
- private async executeWorkflowStepInner(
1325
- runId: string,
1326
- stepName: string,
1327
- rpcName: string,
1328
- data: any,
1329
- rpcService: PikkuRPC
1330
- ): Promise<void> {
1331
- const claimed = await this.claimStepForExecution(runId, stepName, rpcName)
1332
-
1333
- if (!claimed) {
1334
- return
1335
- }
1336
- const stepState = claimed
1337
-
1338
- try {
1339
- let result: any
1340
-
1341
- const run = await this.getRun(runId)
1342
- if (!run) {
1343
- throw new Error(`Workflow run not found: ${runId}`)
1344
- }
1345
-
1346
- const meta = pikkuState(null, 'workflows', 'meta')
1347
- const workflowMeta = meta[run.workflow]
1348
-
1349
- const isGraphWorkflow = workflowMeta?.source === 'graph'
1350
- let graphNodeId: string | undefined
1351
- if (isGraphWorkflow && workflowMeta?.nodes) {
1352
- if (stepName in workflowMeta.nodes) {
1353
- graphNodeId = stepName
1354
- } else {
1355
- const base = stripInstanceOrdinal(stepName)
1356
- if (base !== stepName && base in workflowMeta.nodes)
1357
- graphNodeId = base
1358
- }
1359
- }
1360
- if (graphNodeId) {
1361
- result = await executeGraphStep(
1362
- this,
1363
- rpcService,
1364
- runId,
1365
- stepState.stepId,
1366
- graphNodeId,
1367
- workflowMeta!.nodes![graphNodeId].rpcName,
1368
- data,
1369
- run.workflow
1370
- )
1371
- } else {
1372
- const subWorkflowMeta = meta[rpcName]
1373
- if (subWorkflowMeta) {
1374
- const childWire: WorkflowRunWire = {
1375
- type: 'workflow',
1376
- id: rpcName,
1377
- parentRunId: runId,
1378
- parentStepId: stepState.stepId,
1379
- pikkuUserId: run.wire?.pikkuUserId,
1380
- }
1381
- const shouldInline = !getSingletonServices()?.queueService
1382
- const { runId: childRunId } = await this.startWorkflow(
1383
- rpcName,
1384
- data,
1385
- childWire,
1386
- rpcService,
1387
- { inline: shouldInline }
1388
- )
1389
- await this.setStepChildRunId(stepState.stepId, childRunId)
1390
- if (shouldInline) {
1391
- const childRun = await this.getRun(childRunId)
1392
- if (childRun?.status === 'failed') {
1393
- throw new Error(childRun.error?.message || 'Sub-workflow failed')
1394
- }
1395
- if (childRun?.status === 'cancelled') {
1396
- throw new Error('Sub-workflow was cancelled')
1397
- }
1398
- result = childRun?.output
1399
- } else {
1400
- throw new ChildWorkflowStartedException(
1401
- runId,
1402
- stepState.stepId,
1403
- childRunId
1404
- )
1405
- }
1406
- } else {
1407
- result = await this.invokeStepRpc(
1408
- runId,
1409
- stepName,
1410
- stepState,
1411
- rpcName,
1412
- data,
1413
- rpcService,
1414
- run
1415
- )
1416
- }
1417
- }
1418
-
1419
- await this.setStepResult(stepState.stepId, result)
1420
-
1421
- await this.resumeWorkflow(runId)
1422
- } catch (error: any) {
1423
- if (error instanceof ChildWorkflowStartedException) {
1424
- this.logger?.debug(
1425
- `Workflow step '${stepName}': child workflow ${error.childRunId} started, waiting for completion`
1426
- )
1427
- return
1428
- }
1429
-
1430
- if (error instanceof RPCNotFoundError) {
1431
- await this.setStepError(stepState.stepId, error)
1432
- await this.updateRunStatus(runId, 'suspended', undefined, {
1433
- message: `RPC '${rpcName}' not found. Deploy the missing function and resume.`,
1434
- code: 'RPC_NOT_FOUND',
1435
- })
1436
- return
1437
- }
1438
-
1439
- await this.setStepError(stepState.stepId, error)
1440
-
1441
- const maxAttempts = (stepState.retries ?? DEFAULT_STEP_RETRIES) + 1
1442
- const retriesExhausted = stepState.attemptCount >= maxAttempts
1443
-
1444
- if (retriesExhausted) {
1445
- await this.resumeWorkflow(runId)
1446
- }
1447
-
1448
- throw error
1449
- }
1450
- }
1451
-
1452
- public async orchestrateWorkflow(
1453
- runId: string,
1454
- rpcService: PikkuRPC
1455
- ): Promise<void> {
1456
- try {
1457
- await this.runWorkflowJob(runId, rpcService)
1458
- } catch (error: any) {
1459
- if (
1460
- error.name === 'WorkflowAsyncException' ||
1461
- error.name === 'WorkflowCancelledException' ||
1462
- error.name === 'WorkflowSuspendedException'
1463
- ) {
1464
- return
1465
- }
1466
-
1467
- if (error.name === 'WorkflowDispatchException') {
1468
- getSingletonServices()!.logger.warn(
1469
- `Workflow run ${runId} could not dispatch a step (queue unavailable); leaving run for orchestrator retry`,
1470
- error
1471
- )
1472
- throw error
1473
- }
1474
-
1475
- await this.updateRunStatus(runId, 'failed', undefined, {
1476
- message: error.message,
1477
- stack: error.stack,
1478
- code: error.code,
1479
- })
1480
-
1481
- throw error
1482
- }
1483
- }
1484
-
1485
- private verifyQueueService(): QueueService {
1486
- if (!getSingletonServices()?.queueService) {
1487
- throw new Error(
1488
- 'QueueService not configured. Remote workflows require a queue service.'
1489
- )
1490
- }
1491
-
1492
- return getSingletonServices()!.queueService!
1493
- }
1494
-
1495
- private async invokeStepRpc(
1496
- runId: string,
1497
- stepName: string,
1498
- stepState: StepState,
1499
- rpcName: string,
1500
- data: any,
1501
- rpcService: PikkuRPC,
1502
- knownRun?: WorkflowRun | null
1503
- ): Promise<any> {
1504
- const run = knownRun ?? (await this.getRunIdentity(runId))
1505
- return rpcService.rpcWithWire(rpcName, data, {
1506
- ...(run?.wire?.pikkuUserId ? { pikkuUserId: run.wire.pikkuUserId } : {}),
1507
- workflowStep: {
1508
- runId,
1509
- stepId: stepState.stepId,
1510
- invocationId: deriveInvocationId(runId, stepName),
1511
- attemptCount: stepState.attemptCount,
1512
- fromInvocationId: stepState.fromStepName
1513
- ? deriveInvocationId(runId, stepState.fromStepName)
1514
- : undefined,
1515
- },
1516
- })
1517
- }
1518
-
1519
- private async runInlineRetryLoop(
1520
- stepState: StepState,
1521
- retries: number,
1522
- retryDelay: WorkflowStepOptions['retryDelay'],
1523
- doWork: (currentStepState: StepState) => Promise<any>,
1524
- onError?: (error: any) => Promise<void>
1525
- ): Promise<any> {
1526
- let currentStepState = stepState
1527
- while (true) {
1528
- try {
1529
- await this.setStepRunning(currentStepState.stepId)
1530
- const result = await doWork(currentStepState)
1531
- await this.setStepResult(currentStepState.stepId, result)
1532
- return result
1533
- } catch (error: any) {
1534
- if (onError) await onError(error)
1535
-
1536
- await this.setStepError(currentStepState.stepId, error)
1537
-
1538
- if (currentStepState.attemptCount < retries) {
1539
- currentStepState = await this.createRetryAttempt(
1540
- currentStepState.stepId,
1541
- 'pending'
1542
- )
1543
- if (retryDelay) {
1544
- await new Promise((resolve) =>
1545
- setTimeout(resolve, getDurationInMilliseconds(retryDelay))
1546
- )
1547
- }
1548
- } else {
1549
- throw error
1550
- }
1551
- }
1552
- }
1553
- }
1554
-
1555
- private async runStepCompensation(
1556
- runId: string,
1557
- stepName: string,
1558
- onErrorRpcName: string,
1559
- rpcService: PikkuRPC,
1560
- error: Error
1561
- ): Promise<void> {
1562
- await this.rpcStep(
1563
- runId,
1564
- `${stepName}:onError`,
1565
- onErrorRpcName,
1566
- { error: { message: error.message } },
1567
- rpcService,
1568
- { retries: 0 }
1569
- )
1570
- }
1571
-
1572
- private async rpcStep(
1573
- runId: string,
1574
- logicalStepName: string,
1575
- rpcName: string,
1576
- data: any,
1577
- rpcService: PikkuRPC,
1578
- stepOptions?: WorkflowStepOptions
1579
- ): Promise<any> {
1580
- const fromStepName = this.lastStepName(runId)
1581
- const stepName = this.nextStepKey(runId, logicalStepName)
1582
- const resolvedStepOptions: WorkflowStepOptions = {
1583
- retries: stepOptions?.retries ?? DEFAULT_STEP_RETRIES,
1584
- retryDelay: stepOptions?.retryDelay,
1585
- actor: stepOptions?.actor,
1586
- onError: stepOptions?.onError,
1587
- }
1588
- const stepState = await this.loadOrCreateStep(runId, stepName, () =>
1589
- this.insertStepState(
1590
- runId,
1591
- stepName,
1592
- rpcName,
1593
- data,
1594
- resolvedStepOptions,
1595
- fromStepName
1596
- )
1597
- )
1598
-
1599
- if (stepState.status === 'succeeded') {
1600
- return stepState.result
1601
- }
1602
-
1603
- if (stepState.status === 'failed') {
1604
- const error = new Error(
1605
- stepState.error?.message ||
1606
- `Step '${stepName}' failed after exhausting all retries`
1607
- )
1608
- if (resolvedStepOptions.onError) {
1609
- await this.runStepCompensation(
1610
- runId,
1611
- stepName,
1612
- resolvedStepOptions.onError,
1613
- rpcService,
1614
- error
1615
- )
1616
- }
1617
- if (stepState.error) {
1618
- Object.assign(error, stepState.error)
1619
- }
1620
- throw error
1621
- }
1622
-
1623
- if (stepState.status === 'scheduled') {
1624
- throw new WorkflowAsyncException(runId, stepName)
1625
- }
1626
-
1627
- const dispatched = resolvedStepOptions.actor
1628
- ? false
1629
- : await this.dispatchStep(
1630
- runId,
1631
- stepName,
1632
- rpcName,
1633
- data,
1634
- resolvedStepOptions,
1635
- fromStepName
1636
- )
1637
- if (dispatched) {
1638
- await this.setStepScheduled(stepState.stepId)
1639
- throw new WorkflowAsyncException(runId, stepName)
1640
- }
1641
-
1642
- const retries = resolvedStepOptions.retries ?? this.getConfig().retries
1643
- const retryDelay = resolvedStepOptions.retryDelay
1644
-
1645
- return this.runInlineRetryLoop(
1646
- stepState,
1647
- retries,
1648
- retryDelay,
1649
- async (currentStepState) => {
1650
- if (resolvedStepOptions.actor) {
1651
- return resolvedStepOptions.actor.invoke(rpcName, data)
1652
- }
1653
- const workflowMeta = pikkuState(null, 'workflows', 'meta')[rpcName]
1654
- if (workflowMeta) {
1655
- const childWire = {
1656
- type: 'workflow',
1657
- id: rpcName,
1658
- parentRunId: runId,
1659
- pikkuUserId: (await this.getRunIdentity(runId))?.wire?.pikkuUserId,
1660
- }
1661
- const { runId: childRunId } = await this.startWorkflow(
1662
- rpcName,
1663
- data,
1664
- childWire,
1665
- rpcService,
1666
- { inline: true }
1667
- )
1668
- await this.setStepChildRunId(currentStepState.stepId, childRunId)
1669
- const childRun = await this.awaitRunEnd(
1670
- childRunId,
1671
- WORKFLOW_CHILD_POLL_MAX_MS
1672
- )
1673
- if (childRun.status === 'failed') {
1674
- throw new Error(childRun.error?.message || 'Sub-workflow failed')
1675
- }
1676
- if (childRun.status === 'cancelled') {
1677
- throw new Error('Sub-workflow was cancelled')
1678
- }
1679
- return childRun.output
1680
- }
1681
- return this.invokeStepRpc(
1682
- runId,
1683
- stepName,
1684
- currentStepState,
1685
- rpcName,
1686
- data,
1687
- rpcService
1688
- )
1689
- },
1690
- async (error) => {
1691
- if (error instanceof RPCNotFoundError) {
1692
- await this.updateRunStatus(runId, 'suspended', undefined, {
1693
- message: `RPC '${rpcName}' not found. Deploy the missing function and resume.`,
1694
- code: 'RPC_NOT_FOUND',
1695
- })
1696
- throw error
1697
- }
1698
- }
1699
- )
1700
- }
1701
-
1702
- protected async inlineStep(
1703
- runId: string,
1704
- logicalStepName: string,
1705
- fn: Function,
1706
- stepOptions?: WorkflowStepOptions,
1707
- data: any = null,
1708
- rpcName: string | null = null
1709
- ): Promise<any> {
1710
- const fromStepName = this.lastStepName(runId)
1711
- const stepName = this.nextStepKey(runId, logicalStepName)
1712
- const stepState = await this.loadOrCreateStep(runId, stepName, () =>
1713
- this.insertStepState(
1714
- runId,
1715
- stepName,
1716
- rpcName,
1717
- data,
1718
- stepOptions,
1719
- fromStepName
1720
- )
1721
- )
1722
-
1723
- if (stepState.status === 'succeeded') {
1724
- return stepState.result
1725
- }
1726
-
1727
- const retries = stepOptions?.retries ?? this.getConfig().retries
1728
- const retryDelay = stepOptions?.retryDelay ?? this.getConfig().retryDelay
1729
-
1730
- if (await this.isInline(runId)) {
1731
- return this.runInlineRetryLoop(stepState, retries, retryDelay, () => fn())
1732
- } else {
1733
- let currentStepState = stepState
1734
- try {
1735
- await this.setStepRunning(currentStepState.stepId)
1736
- const result = await fn()
1737
- await this.setStepResult(currentStepState.stepId, result)
1738
- return result
1739
- } catch (error: any) {
1740
- await this.setStepError(currentStepState.stepId, error)
1741
-
1742
- if (currentStepState.attemptCount < retries) {
1743
- await this.createRetryAttempt(currentStepState.stepId, 'pending')
1744
-
1745
- await this.scheduleOrchestratorRetry(runId, retryDelay)
1746
-
1747
- throw new WorkflowAsyncException(runId, stepName)
1748
- }
1749
-
1750
- throw error
1751
- }
1752
- }
1753
- }
1754
-
1755
- private async sleepStep(
1756
- runId: string,
1757
- logicalStepName: string,
1758
- duration: number
1759
- ) {
1760
- const fromStepName = this.lastStepName(runId)
1761
- const stepName = this.nextStepKey(runId, logicalStepName)
1762
- const stepState = await this.loadOrCreateStep(runId, stepName, () =>
1763
- this.insertStepState(
1764
- runId,
1765
- stepName,
1766
- null,
1767
- { duration },
1768
- undefined,
1769
- fromStepName
1770
- )
1771
- )
1772
-
1773
- if (stepState.status === 'succeeded') {
1774
- return
1775
- }
1776
-
1777
- if (stepState.status === 'scheduled') {
1778
- throw new WorkflowAsyncException(runId, stepName)
1779
- }
1780
-
1781
- let scheduled: boolean
1782
- try {
1783
- scheduled = await this.scheduleSleep(runId, stepState.stepId, duration)
1784
- } catch (cause) {
1785
- throw new WorkflowDispatchException(runId, stepName, { cause })
1786
- }
1787
- if (scheduled) {
1788
- await this.setStepScheduled(stepState.stepId)
1789
- throw new WorkflowAsyncException(runId, stepName)
1790
- }
1791
-
1792
- await new Promise((resolve) =>
1793
- setTimeout(resolve, getDurationInMilliseconds(duration))
1794
- )
1795
- await this.setStepResult(stepState.stepId, null)
1796
- }
1797
-
1798
- private async suspendStep(runId: string, reason: string): Promise<void> {
1799
- const fromStepName = this.lastStepName(runId)
1800
- const suspendStepName = this.nextStepKey(runId, suspendStepNameFor(reason))
1801
- await this.withStepLock(runId, suspendStepName, () =>
1802
- recordSuspension(
1803
- this.approvalStore,
1804
- runId,
1805
- reason,
1806
- suspendStepName,
1807
- fromStepName
1808
- )
1809
- )
1810
- }
1811
-
1812
- private async scheduleRunWake(runId: string, delay: number): Promise<void> {
1813
- try {
1814
- const queueService = this.verifyQueueService()
1815
- const run = await this.getRun(runId)
1816
- if (!run?.workflow) return
1817
- await queueService.add(
1818
- this.getOrchestratorQueueName(run.workflow),
1819
- { runId },
1820
- {
1821
- ...this.resolveStepJobOptions(),
1822
- delay,
1823
- group: this.getJobGroup(run.workflow),
1824
- }
1825
- )
1826
- } catch (error) {
1827
- this.logger?.warn(
1828
- `Failed to schedule approval expiry wake for run ${runId}; expiry will still resolve on the next replay`,
1829
- error
1830
- )
1831
- }
1832
- }
1833
-
1834
- private get approvalStore(): ApprovalStore {
1835
- return {
1836
- getStepState: (runId, stepName) => this.getStepState(runId, stepName),
1837
- insertStepState: (
1838
- runId,
1839
- stepName,
1840
- rpcName,
1841
- input,
1842
- output,
1843
- fromStepName
1844
- ) =>
1845
- this.insertStepState(
1846
- runId,
1847
- stepName,
1848
- rpcName,
1849
- input,
1850
- output,
1851
- fromStepName
1852
- ),
1853
- setStepRunning: (stepId) => this.setStepRunning(stepId),
1854
- setStepResult: (stepId, result) => this.setStepResult(stepId, result),
1855
- getRunState: (runId) => this.getRunState(runId),
1856
- updateRunState: (runId, key, value) =>
1857
- this.updateRunState(runId, key, value),
1858
- resumeWorkflow: (runId) => this.resumeWorkflow(runId),
1859
- scheduleRunWake: (runId, delay) => this.scheduleRunWake(runId, delay),
1860
- getRunOwner: async (runId) =>
1861
- (await this.getRunIdentity(runId))?.wire?.pikkuUserId,
1862
- auditApproval: (event) => auditApprovalDecision(event),
1863
- }
1864
- }
1865
-
1866
- public async approveStep(
1867
- runId: string,
1868
- reason: string,
1869
- decision: unknown,
1870
- session?: CoreUserSession
1871
- ): Promise<void> {
1872
- return recordApprovalDecision(
1873
- this.approvalStore,
1874
- runId,
1875
- reason,
1876
- decision,
1877
- approvalDeciderFrom(session)
1878
- )
1879
- }
1880
-
1881
- private async approvalStep(
1882
- runId: string,
1883
- reason: string,
1884
- options: WorkflowApprovalOptions
1885
- ): Promise<ApprovalOutcome<unknown>> {
1886
- const fromStepName = this.lastStepName(runId)
1887
- const approvalStepName = this.nextStepKey(
1888
- runId,
1889
- approvalStepNameFor(reason)
1890
- )
1891
- return await this.withStepLock(runId, approvalStepName, () =>
1892
- evaluateApprovalStep(
1893
- this.approvalStore,
1894
- runId,
1895
- reason,
1896
- approvalStepName,
1897
- fromStepName,
1898
- options
1899
- )
1900
- )
1901
- }
1902
-
1903
- public createWorkflowWire(
1904
- name: string,
1905
- runId: string,
1906
- rpcService: PikkuRPC,
1907
- addonNamespace?: string | null
1908
- ): PikkuWorkflowWire {
1909
- const workflowWire: PikkuWorkflowWire = {
1910
- name,
1911
- runId,
1912
- getRun: async () => (await this.getRun(runId)) as WorkflowRun,
1913
-
1914
- do: async (
1915
- stepName: string,
1916
- rpcNameOrFn: any,
1917
- dataOrOptions?: any,
1918
- options?: any
1919
- ) => {
1920
- this.verifyStepName(stepName)
1921
- if (typeof rpcNameOrFn === 'string') {
1922
- const resolvedRpcName =
1923
- addonNamespace && !rpcNameOrFn.includes(':')
1924
- ? `${addonNamespace}:${rpcNameOrFn}`
1925
- : rpcNameOrFn
1926
- return await this.rpcStep(
1927
- runId,
1928
- stepName,
1929
- resolvedRpcName,
1930
- dataOrOptions,
1931
- rpcService,
1932
- options
1933
- )
1934
- } else {
1935
- return await this.inlineStep(
1936
- runId,
1937
- stepName,
1938
- rpcNameOrFn,
1939
- dataOrOptions
1940
- )
1941
- }
1942
- },
1943
-
1944
- sleep: async (stepName: string, duration: string | number) => {
1945
- this.verifyStepName(stepName)
1946
- await this.sleepStep(
1947
- runId,
1948
- stepName,
1949
- getDurationInMilliseconds(duration)
1950
- )
1951
- },
1952
-
1953
- suspend: async (reason: string) => {
1954
- this.verifyStepName(reason)
1955
- await this.suspendStep(runId, reason)
1956
- },
1957
-
1958
- approval: (async (reason: string, options: WorkflowApprovalOptions) => {
1959
- this.verifyStepName(reason)
1960
- return await this.approvalStep(runId, reason, options)
1961
- }) as PikkuWorkflowWire['approval'],
1962
- }
1963
- this.runExtension?.decorateWorkflowWire(workflowWire, {
1964
- name,
1965
- runId,
1966
- rpcService,
1967
- addonNamespace,
1968
- })
1969
- return workflowWire
1970
- }
1971
-
1972
- protected verifyStepName(stepName: string) {
1973
- if (typeof stepName !== 'string') {
1974
- throw new WorkflowStepNameNotString(stepName)
1975
- }
1976
- }
1977
-
1978
- private getConfig(): WorkflowServiceConfig {
1979
- return resolveWorkflowConfig()
1980
- }
1981
-
1982
- protected getOrchestratorQueueName(workflowName?: string): string {
1983
- return orchestratorQueueName(this.queueStrategy, workflowName)
1984
- }
1985
-
1986
- protected getStepWorkerQueueName(rpcName?: string): string {
1987
- return stepWorkerQueueName(this.queueStrategy, rpcName)
1988
- }
1989
-
1990
- protected getJobGroup(id?: string): JobGroup | undefined {
1991
- return jobGroupFor(this.queueStrategy, id)
1992
- }
1993
- }