@pikku/core 0.12.107 → 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 (588) hide show
  1. package/CHANGELOG.md +344 -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/virtual-user/virtual-user-scaffold.d.ts +8 -8
  74. package/dist/wirings/virtual-user/virtual-user-schedule.d.ts +1 -1
  75. package/dist/wirings/workflow/workflow-queue-routing.d.ts +1 -1
  76. package/package.json +11 -5
  77. package/run-tests.sh +17 -9
  78. package/src/public-surface.json +26 -0
  79. package/src/agent-naming.test.ts +0 -68
  80. package/src/api-report.test.ts +0 -138
  81. package/src/app-alias-surface.test.ts +0 -179
  82. package/src/app-leaf-surface.test.ts +0 -222
  83. package/src/bootstrap-compat/root.ts +0 -20
  84. package/src/classification/column-form.test.ts +0 -97
  85. package/src/classification/column-form.ts +0 -55
  86. package/src/classification/data-classification.ts +0 -73
  87. package/src/classification/index.ts +0 -40
  88. package/src/classification/key-ids.ts +0 -2
  89. package/src/classification/secret-value.test.ts +0 -204
  90. package/src/classification/secret-value.ts +0 -106
  91. package/src/crypto-utils.test.ts +0 -668
  92. package/src/crypto-utils.ts +0 -460
  93. package/src/dev/hot-reload.test.ts +0 -587
  94. package/src/dev/hot-reload.ts +0 -225
  95. package/src/dev/module-runner.test.ts +0 -235
  96. package/src/dev/module-runner.ts +0 -138
  97. package/src/dev/reload-meta.test.ts +0 -183
  98. package/src/dev/reload-meta.ts +0 -144
  99. package/src/ecosystem-tier-removed.test.ts +0 -72
  100. package/src/env.ts +0 -1
  101. package/src/errors/error-handler.ts +0 -77
  102. package/src/errors/error.test.ts +0 -229
  103. package/src/errors/errors.ts +0 -393
  104. package/src/errors/index.ts +0 -11
  105. package/src/errors/serialized-error.ts +0 -12
  106. package/src/factory-functions.test.ts +0 -111
  107. package/src/function/abort-scope.test.ts +0 -97
  108. package/src/function/abort-scope.ts +0 -80
  109. package/src/function/function-meta.types.ts +0 -151
  110. package/src/function/function-runner.test.ts +0 -1619
  111. package/src/function/function-runner.ts +0 -499
  112. package/src/function/functions.types.ts +0 -319
  113. package/src/function/index.ts +0 -40
  114. package/src/function/list.types.test.ts +0 -100
  115. package/src/function/list.types.ts +0 -69
  116. package/src/function/pikku-request.test.ts +0 -35
  117. package/src/function/pikku-request.ts +0 -17
  118. package/src/gopass-secrets-removed.test.ts +0 -51
  119. package/src/handle-error.test.ts +0 -532
  120. package/src/handle-error.ts +0 -64
  121. package/src/middleware/auth-apikey.test.ts +0 -362
  122. package/src/middleware/auth-apikey.ts +0 -39
  123. package/src/middleware/auth-bearer.test.ts +0 -566
  124. package/src/middleware/auth-bearer.ts +0 -83
  125. package/src/middleware/auth-cookie.test.ts +0 -572
  126. package/src/middleware/auth-cookie.ts +0 -107
  127. package/src/middleware/cors.test.ts +0 -579
  128. package/src/middleware/cors.ts +0 -103
  129. package/src/middleware/index.ts +0 -30
  130. package/src/middleware/middleware-factories.ts +0 -76
  131. package/src/middleware/middleware.types.ts +0 -61
  132. package/src/middleware/remote-auth.test.ts +0 -547
  133. package/src/middleware/remote-auth.ts +0 -95
  134. package/src/middleware/remote-jobs-secret.ts +0 -35
  135. package/src/middleware/require-origin.test.ts +0 -115
  136. package/src/middleware/require-origin.ts +0 -79
  137. package/src/middleware/telemetry.ts +0 -83
  138. package/src/middleware-runner.test.ts +0 -578
  139. package/src/middleware-runner.ts +0 -232
  140. package/src/no-any-casts.test.ts +0 -57
  141. package/src/no-root-barrel.test.ts +0 -122
  142. package/src/permissions.test.ts +0 -256
  143. package/src/permissions.ts +0 -217
  144. package/src/pikku-state.test.ts +0 -198
  145. package/src/pikku-state.ts +0 -206
  146. package/src/production-barrels-stay-lean.test.ts +0 -110
  147. package/src/public-surface.test.ts +0 -133
  148. package/src/remote.test.ts +0 -173
  149. package/src/remote.ts +0 -56
  150. package/src/removed-legacy-exports.test.ts +0 -66
  151. package/src/run-tests-script.test.ts +0 -67
  152. package/src/schema.test.ts +0 -430
  153. package/src/schema.ts +0 -204
  154. package/src/scopes.test.ts +0 -203
  155. package/src/scopes.ts +0 -67
  156. package/src/services/agent-run-state-service.ts +0 -37
  157. package/src/services/agent-runner-service.ts +0 -238
  158. package/src/services/agent-storage-service.ts +0 -39
  159. package/src/services/ai-embedding-service.ts +0 -9
  160. package/src/services/audit-service.test.ts +0 -44
  161. package/src/services/audit-service.ts +0 -280
  162. package/src/services/content-service.ts +0 -65
  163. package/src/services/credential-service.ts +0 -20
  164. package/src/services/credential-wire-service.test.ts +0 -235
  165. package/src/services/credential-wire-service.ts +0 -120
  166. package/src/services/deployment-service.ts +0 -32
  167. package/src/services/email-service.test.ts +0 -100
  168. package/src/services/email-service.ts +0 -60
  169. package/src/services/email-template.test.ts +0 -311
  170. package/src/services/email-template.ts +0 -254
  171. package/src/services/file-scenario-run-store.test.ts +0 -349
  172. package/src/services/file-scenario-run-store.ts +0 -282
  173. package/src/services/gateway-service.ts +0 -5
  174. package/src/services/http-personas-converse.test.ts +0 -274
  175. package/src/services/http-personas.test.ts +0 -379
  176. package/src/services/http-personas.ts +0 -508
  177. package/src/services/in-memory-agent-run-state-service.ts +0 -87
  178. package/src/services/in-memory-queue-service.test.ts +0 -162
  179. package/src/services/in-memory-queue-service.ts +0 -78
  180. package/src/services/in-memory-session-store.test.ts +0 -58
  181. package/src/services/in-memory-session-store.ts +0 -20
  182. package/src/services/in-memory-trigger-service.ts +0 -47
  183. package/src/services/in-memory-workflow-service.test.ts +0 -430
  184. package/src/services/in-memory-workflow-service.ts +0 -595
  185. package/src/services/index.ts +0 -192
  186. package/src/services/istanbul-coverage-service.test.ts +0 -66
  187. package/src/services/istanbul-coverage-service.ts +0 -59
  188. package/src/services/jwt-service.ts +0 -15
  189. package/src/services/local-content-request-handler.test.ts +0 -240
  190. package/src/services/local-content-request-handler.ts +0 -295
  191. package/src/services/local-content.test.ts +0 -186
  192. package/src/services/local-content.ts +0 -242
  193. package/src/services/local-credential-service.ts +0 -63
  194. package/src/services/local-email-service.test.ts +0 -182
  195. package/src/services/local-email-service.ts +0 -43
  196. package/src/services/local-gateway-service.ts +0 -51
  197. package/src/services/local-secrets.test.ts +0 -95
  198. package/src/services/local-secrets.ts +0 -71
  199. package/src/services/local-variables.test.ts +0 -61
  200. package/src/services/local-variables.ts +0 -45
  201. package/src/services/logger-console.test.ts +0 -117
  202. package/src/services/logger-console.ts +0 -174
  203. package/src/services/logger.ts +0 -48
  204. package/src/services/meta-service.test.ts +0 -75
  205. package/src/services/meta-service.ts +0 -767
  206. package/src/services/persona-actor-secret.test.ts +0 -68
  207. package/src/services/persona-actor-secret.ts +0 -70
  208. package/src/services/persona-sign-in.test.ts +0 -219
  209. package/src/services/persona-sign-in.ts +0 -238
  210. package/src/services/personas-service.ts +0 -191
  211. package/src/services/pikku-user-id.test.ts +0 -62
  212. package/src/services/pikku-user-id.ts +0 -13
  213. package/src/services/queue-webhook-service.test.ts +0 -409
  214. package/src/services/queue-webhook-service.ts +0 -150
  215. package/src/services/remote-jobs.test.ts +0 -106
  216. package/src/services/remote-jobs.ts +0 -92
  217. package/src/services/scheduler-service.ts +0 -37
  218. package/src/services/schema-service.ts +0 -10
  219. package/src/services/scope-service.ts +0 -91
  220. package/src/services/scoped-credential-service.test.ts +0 -86
  221. package/src/services/scoped-credential-service.ts +0 -63
  222. package/src/services/scoped-secret-service.test.ts +0 -93
  223. package/src/services/scoped-secret-service.ts +0 -40
  224. package/src/services/secret-host-binding.test.ts +0 -138
  225. package/src/services/secret-host-binding.ts +0 -51
  226. package/src/services/secret-service.ts +0 -25
  227. package/src/services/secretless.test.ts +0 -54
  228. package/src/services/secretless.ts +0 -29
  229. package/src/services/session-store.ts +0 -9
  230. package/src/services/stub-tracker.test.ts +0 -104
  231. package/src/services/stub-tracker.ts +0 -175
  232. package/src/services/system-role-guard.test.ts +0 -96
  233. package/src/services/system-role-guard.ts +0 -71
  234. package/src/services/temporary-file-service.ts +0 -76
  235. package/src/services/trigger-service.ts +0 -5
  236. package/src/services/typed-credential-service.ts +0 -83
  237. package/src/services/typed-secret-service.test.ts +0 -204
  238. package/src/services/typed-secret-service.ts +0 -132
  239. package/src/services/typed-variables-service.test.ts +0 -166
  240. package/src/services/typed-variables-service.ts +0 -166
  241. package/src/services/user-session-service.test.ts +0 -147
  242. package/src/services/user-session-service.ts +0 -85
  243. package/src/services/v8-coverage-service.test.ts +0 -69
  244. package/src/services/v8-coverage-service.ts +0 -163
  245. package/src/services/variables-service.ts +0 -13
  246. package/src/services/webhook-service.ts +0 -147
  247. package/src/services/workflow-service.ts +0 -120
  248. package/src/side-effects-are-declared.test.ts +0 -84
  249. package/src/source-files-stay-composable.test.ts +0 -41
  250. package/src/template-alias-surface.test.ts +0 -240
  251. package/src/test-utils.ts +0 -38
  252. package/src/testing/index.ts +0 -3
  253. package/src/testing/service-tests/agent-run-service-tests.ts +0 -98
  254. package/src/testing/service-tests/agent-storage-service-tests.ts +0 -379
  255. package/src/testing/service-tests/channel-store-tests.ts +0 -98
  256. package/src/testing/service-tests/credential-service-tests.ts +0 -143
  257. package/src/testing/service-tests/deployment-service-tests.ts +0 -33
  258. package/src/testing/service-tests/event-hub-store-tests.ts +0 -48
  259. package/src/testing/service-tests/secret-service-tests.ts +0 -105
  260. package/src/testing/service-tests/session-store-tests.ts +0 -62
  261. package/src/testing/service-tests/workflow-run-service-tests.ts +0 -59
  262. package/src/testing/service-tests/workflow-service-tests.ts +0 -308
  263. package/src/testing/service-tests.ts +0 -95
  264. package/src/time-utils.test.ts +0 -56
  265. package/src/time-utils.ts +0 -89
  266. package/src/types/core.types.ts +0 -446
  267. package/src/types/index.ts +0 -25
  268. package/src/types/state.types.ts +0 -226
  269. package/src/types/typed-credentials.test.ts +0 -72
  270. package/src/utils/hash.test.ts +0 -68
  271. package/src/utils/hash.ts +0 -26
  272. package/src/utils/hmac.ts +0 -21
  273. package/src/utils/node-host-resolver.ts +0 -20
  274. package/src/utils/safe-fetch.test.ts +0 -515
  275. package/src/utils/safe-fetch.ts +0 -347
  276. package/src/utils.test.ts +0 -359
  277. package/src/utils.ts +0 -165
  278. package/src/version.test.ts +0 -80
  279. package/src/version.ts +0 -25
  280. package/src/wirings/actor-flow/actor-flow.types.ts +0 -40
  281. package/src/wirings/actor-flow/index.ts +0 -10
  282. package/src/wirings/actor-flow/run-conversation.test.ts +0 -187
  283. package/src/wirings/actor-flow/run-conversation.ts +0 -280
  284. package/src/wirings/addon/addon-auth-tags.test.ts +0 -219
  285. package/src/wirings/addon/addon-runner.ts +0 -236
  286. package/src/wirings/addon/addon-scopes.test.ts +0 -225
  287. package/src/wirings/addon/addon-secrets.test.ts +0 -391
  288. package/src/wirings/addon/index.ts +0 -12
  289. package/src/wirings/addon/remote-addon-auth.ts +0 -65
  290. package/src/wirings/addon/wire-addon.test.ts +0 -102
  291. package/src/wirings/addon/wire-addon.ts +0 -202
  292. package/src/wirings/addon/wire-remote-addon.ts +0 -41
  293. package/src/wirings/agent/agent-agui.test.ts +0 -1213
  294. package/src/wirings/agent/agent-agui.ts +0 -405
  295. package/src/wirings/agent/agent-authorization.test.ts +0 -208
  296. package/src/wirings/agent/agent-finalize.test.ts +0 -187
  297. package/src/wirings/agent/agent-finalize.ts +0 -197
  298. package/src/wirings/agent/agent-helpers.test.ts +0 -215
  299. package/src/wirings/agent/agent-helpers.ts +0 -96
  300. package/src/wirings/agent/agent-interrupt.test.ts +0 -833
  301. package/src/wirings/agent/agent-interrupt.ts +0 -400
  302. package/src/wirings/agent/agent-memory.test.ts +0 -397
  303. package/src/wirings/agent/agent-memory.ts +0 -496
  304. package/src/wirings/agent/agent-middleware.types.test.ts +0 -41
  305. package/src/wirings/agent/agent-model-config.test.ts +0 -103
  306. package/src/wirings/agent/agent-model-config.ts +0 -59
  307. package/src/wirings/agent/agent-prepare.test.ts +0 -977
  308. package/src/wirings/agent/agent-prepare.ts +0 -1083
  309. package/src/wirings/agent/agent-registry.test.ts +0 -455
  310. package/src/wirings/agent/agent-registry.ts +0 -99
  311. package/src/wirings/agent/agent-resume-authorization.test.ts +0 -206
  312. package/src/wirings/agent/agent-rpc.ts +0 -116
  313. package/src/wirings/agent/agent-runner.test.ts +0 -1229
  314. package/src/wirings/agent/agent-runner.ts +0 -858
  315. package/src/wirings/agent/agent-stream-delegate.test.ts +0 -381
  316. package/src/wirings/agent/agent-stream-output-hooks.test.ts +0 -353
  317. package/src/wirings/agent/agent-stream.test.ts +0 -1800
  318. package/src/wirings/agent/agent-stream.ts +0 -1639
  319. package/src/wirings/agent/agent-thread-ownership.test.ts +0 -297
  320. package/src/wirings/agent/agent-turn.test.ts +0 -65
  321. package/src/wirings/agent/agent-turn.ts +0 -122
  322. package/src/wirings/agent/agent-utils.ts +0 -20
  323. package/src/wirings/agent/agent.types.ts +0 -676
  324. package/src/wirings/agent/index.ts +0 -58
  325. package/src/wirings/agent/voice-input.test.ts +0 -220
  326. package/src/wirings/agent/voice-input.ts +0 -180
  327. package/src/wirings/agent/voice-output.test.ts +0 -510
  328. package/src/wirings/agent/voice-output.ts +0 -311
  329. package/src/wirings/agent-scorer/agent-scorer-grade.test.ts +0 -106
  330. package/src/wirings/agent-scorer/agent-scorer-grade.ts +0 -55
  331. package/src/wirings/agent-scorer/agent-scorer-judge.test.ts +0 -285
  332. package/src/wirings/agent-scorer/agent-scorer-judge.ts +0 -180
  333. package/src/wirings/agent-scorer/agent-scorer-live.test.ts +0 -174
  334. package/src/wirings/agent-scorer/agent-scorer-live.ts +0 -56
  335. package/src/wirings/agent-scorer/agent-scorer-registry.ts +0 -66
  336. package/src/wirings/agent-scorer/agent-scorer-sampling.test.ts +0 -34
  337. package/src/wirings/agent-scorer/agent-scorer-sampling.ts +0 -36
  338. package/src/wirings/agent-scorer/agent-scorer-snapshots.test.ts +0 -49
  339. package/src/wirings/agent-scorer/agent-scorer-snapshots.ts +0 -46
  340. package/src/wirings/agent-scorer/agent-scorer-worker.test.ts +0 -122
  341. package/src/wirings/agent-scorer/agent-scorer-worker.ts +0 -71
  342. package/src/wirings/agent-scorer/agent-scorer.ts +0 -102
  343. package/src/wirings/agent-scorer/agent-scorer.types.ts +0 -128
  344. package/src/wirings/agent-scorer/index.ts +0 -23
  345. package/src/wirings/channel/channel-common.ts +0 -102
  346. package/src/wirings/channel/channel-handler-shapes.test.ts +0 -72
  347. package/src/wirings/channel/channel-handler.test.ts +0 -322
  348. package/src/wirings/channel/channel-handler.ts +0 -259
  349. package/src/wirings/channel/channel-host-rpc.test.ts +0 -150
  350. package/src/wirings/channel/channel-host-rpc.ts +0 -69
  351. package/src/wirings/channel/channel-middleware-runner.test.ts +0 -188
  352. package/src/wirings/channel/channel-middleware-runner.ts +0 -126
  353. package/src/wirings/channel/channel-rpc-registry.ts +0 -116
  354. package/src/wirings/channel/channel-rpc-responder.ts +0 -117
  355. package/src/wirings/channel/channel-rpc-service.ts +0 -132
  356. package/src/wirings/channel/channel-rpc-validators.ts +0 -65
  357. package/src/wirings/channel/channel-rpc.test.ts +0 -841
  358. package/src/wirings/channel/channel-rpc.ts +0 -5
  359. package/src/wirings/channel/channel-rpc.types.ts +0 -160
  360. package/src/wirings/channel/channel-runner.ts +0 -164
  361. package/src/wirings/channel/channel-store.ts +0 -35
  362. package/src/wirings/channel/channel.types.ts +0 -193
  363. package/src/wirings/channel/define-channel-routes.ts +0 -5
  364. package/src/wirings/channel/eventhub-service.ts +0 -16
  365. package/src/wirings/channel/eventhub-store.ts +0 -13
  366. package/src/wirings/channel/index.ts +0 -40
  367. package/src/wirings/channel/local/index.ts +0 -3
  368. package/src/wirings/channel/local/local-channel-handler.ts +0 -98
  369. package/src/wirings/channel/local/local-channel-runner.test.ts +0 -339
  370. package/src/wirings/channel/local/local-channel-runner.ts +0 -271
  371. package/src/wirings/channel/local/local-eventhub-service.test.ts +0 -77
  372. package/src/wirings/channel/local/local-eventhub-service.ts +0 -69
  373. package/src/wirings/channel/log-channels.ts +0 -16
  374. package/src/wirings/channel/pikku-abstract-channel-handler.test.ts +0 -135
  375. package/src/wirings/channel/pikku-abstract-channel-handler.ts +0 -60
  376. package/src/wirings/channel/serverless/index.ts +0 -6
  377. package/src/wirings/channel/serverless/serverless-channel-runner.ts +0 -328
  378. package/src/wirings/cli/channel/cli-approval.test.ts +0 -177
  379. package/src/wirings/cli/channel/cli-approval.ts +0 -135
  380. package/src/wirings/cli/channel/cli-channel-runner.ts +0 -118
  381. package/src/wirings/cli/channel/cli-raw-channel-runner.test.ts +0 -192
  382. package/src/wirings/cli/channel/cli-raw-channel-runner.ts +0 -148
  383. package/src/wirings/cli/channel/cli-raw-client-runner.test.ts +0 -480
  384. package/src/wirings/cli/channel/cli-raw-client-runner.ts +0 -188
  385. package/src/wirings/cli/channel/index.ts +0 -6
  386. package/src/wirings/cli/cli-runner.test.ts +0 -969
  387. package/src/wirings/cli/cli-runner.ts +0 -538
  388. package/src/wirings/cli/cli.types.ts +0 -388
  389. package/src/wirings/cli/command-parser.test.ts +0 -1120
  390. package/src/wirings/cli/command-parser.ts +0 -713
  391. package/src/wirings/cli/define-cli-commands.ts +0 -13
  392. package/src/wirings/cli/format-cli-error.test.ts +0 -91
  393. package/src/wirings/cli/format-cli-error.ts +0 -96
  394. package/src/wirings/cli/index.ts +0 -19
  395. package/src/wirings/credential/credential.types.ts +0 -37
  396. package/src/wirings/credential/define-credential.ts +0 -49
  397. package/src/wirings/credential/index.ts +0 -7
  398. package/src/wirings/credential/validate-credential-definitions.ts +0 -63
  399. package/src/wirings/gateway/gateway-authorization.test.ts +0 -575
  400. package/src/wirings/gateway/gateway-channel-meta.test.ts +0 -44
  401. package/src/wirings/gateway/gateway-runner.test.ts +0 -581
  402. package/src/wirings/gateway/gateway-runner.ts +0 -411
  403. package/src/wirings/gateway/gateway.types.ts +0 -137
  404. package/src/wirings/gateway/index.ts +0 -15
  405. package/src/wirings/http/http-routes.test.ts +0 -318
  406. package/src/wirings/http/http-routes.ts +0 -111
  407. package/src/wirings/http/http-runner-addon-ref.test.ts +0 -238
  408. package/src/wirings/http/http-runner.test.ts +0 -503
  409. package/src/wirings/http/http-runner.ts +0 -462
  410. package/src/wirings/http/http.types.ts +0 -307
  411. package/src/wirings/http/index.ts +0 -27
  412. package/src/wirings/http/log-http-routes.ts +0 -18
  413. package/src/wirings/http/pikku-fetch-http-request.test.ts +0 -318
  414. package/src/wirings/http/pikku-fetch-http-request.ts +0 -260
  415. package/src/wirings/http/pikku-fetch-http-response.test.ts +0 -123
  416. package/src/wirings/http/pikku-fetch-http-response.ts +0 -149
  417. package/src/wirings/http/routers/http-router.ts +0 -13
  418. package/src/wirings/http/routers/path-to-regex.test.ts +0 -306
  419. package/src/wirings/http/routers/path-to-regex.ts +0 -115
  420. package/src/wirings/http/web-request.test.ts +0 -382
  421. package/src/wirings/http/web-request.ts +0 -162
  422. package/src/wirings/mcp/index.ts +0 -26
  423. package/src/wirings/mcp/mcp-endpoint-registry.test.ts +0 -388
  424. package/src/wirings/mcp/mcp-endpoint-registry.ts +0 -159
  425. package/src/wirings/mcp/mcp-runner.test.ts +0 -588
  426. package/src/wirings/mcp/mcp-runner.ts +0 -330
  427. package/src/wirings/mcp/mcp.types.ts +0 -199
  428. package/src/wirings/node/index.ts +0 -2
  429. package/src/wirings/node/node.types.ts +0 -23
  430. package/src/wirings/oauth2/index.ts +0 -1
  431. package/src/wirings/oauth2/oauth2.types.ts +0 -12
  432. package/src/wirings/persona/define-personas.ts +0 -33
  433. package/src/wirings/persona/index.ts +0 -75
  434. package/src/wirings/persona/persona-app-scopes.test.ts +0 -47
  435. package/src/wirings/persona/persona-app-scopes.ts +0 -74
  436. package/src/wirings/persona/persona-email.ts +0 -87
  437. package/src/wirings/persona/persona-environments.test.ts +0 -194
  438. package/src/wirings/persona/persona-environments.ts +0 -138
  439. package/src/wirings/persona/persona-mailbox.ts +0 -156
  440. package/src/wirings/persona/persona.test.ts +0 -230
  441. package/src/wirings/persona/persona.types.ts +0 -156
  442. package/src/wirings/persona/validate-personas.ts +0 -137
  443. package/src/wirings/queue/index.ts +0 -29
  444. package/src/wirings/queue/queue-identity.test.ts +0 -454
  445. package/src/wirings/queue/queue-identity.ts +0 -176
  446. package/src/wirings/queue/queue-runner.test.ts +0 -705
  447. package/src/wirings/queue/queue-runner.ts +0 -181
  448. package/src/wirings/queue/queue.types.ts +0 -161
  449. package/src/wirings/queue/register-queue-helper.ts +0 -46
  450. package/src/wirings/queue/signed-queue-service.ts +0 -60
  451. package/src/wirings/queue/validate-worker-config.test.ts +0 -108
  452. package/src/wirings/queue/validate-worker-config.ts +0 -90
  453. package/src/wirings/role/define-system-role.ts +0 -33
  454. package/src/wirings/role/index.ts +0 -13
  455. package/src/wirings/role/role.test.ts +0 -104
  456. package/src/wirings/role/role.types.ts +0 -47
  457. package/src/wirings/role/validate-role-definitions.ts +0 -93
  458. package/src/wirings/rpc/index.ts +0 -8
  459. package/src/wirings/rpc/rpc-runner.test.ts +0 -1130
  460. package/src/wirings/rpc/rpc-runner.ts +0 -569
  461. package/src/wirings/rpc/rpc-types.ts +0 -87
  462. package/src/wirings/scheduler/index.ts +0 -11
  463. package/src/wirings/scheduler/log-schedulers.ts +0 -16
  464. package/src/wirings/scheduler/scheduler-runner.test.ts +0 -835
  465. package/src/wirings/scheduler/scheduler-runner.ts +0 -138
  466. package/src/wirings/scheduler/scheduler.types.ts +0 -42
  467. package/src/wirings/scope/define-scope.ts +0 -33
  468. package/src/wirings/scope/index.ts +0 -15
  469. package/src/wirings/scope/scope.test.ts +0 -159
  470. package/src/wirings/scope/scope.types.ts +0 -42
  471. package/src/wirings/scope/validate-scope-definitions.ts +0 -107
  472. package/src/wirings/secret/index.ts +0 -9
  473. package/src/wirings/secret/secret.types.ts +0 -70
  474. package/src/wirings/secret/validate-secret-definitions.test.ts +0 -232
  475. package/src/wirings/secret/validate-secret-definitions.ts +0 -88
  476. package/src/wirings/trigger/index.ts +0 -9
  477. package/src/wirings/trigger/pikku-trigger-service.ts +0 -107
  478. package/src/wirings/trigger/trigger-runner.test.ts +0 -78
  479. package/src/wirings/trigger/trigger-runner.ts +0 -113
  480. package/src/wirings/trigger/trigger.types.ts +0 -100
  481. package/src/wirings/variable/index.ts +0 -8
  482. package/src/wirings/variable/validate-variable-definitions.test.ts +0 -115
  483. package/src/wirings/variable/validate-variable-definitions.ts +0 -71
  484. package/src/wirings/variable/variable.types.ts +0 -40
  485. package/src/wirings/virtual-user/index.ts +0 -107
  486. package/src/wirings/virtual-user/prepare-virtual-user-run.test.ts +0 -115
  487. package/src/wirings/virtual-user/prepare-virtual-user-run.ts +0 -95
  488. package/src/wirings/virtual-user/run-virtual-user.test.ts +0 -774
  489. package/src/wirings/virtual-user/run-virtual-user.ts +0 -684
  490. package/src/wirings/virtual-user/virtual-user-agents.test.ts +0 -72
  491. package/src/wirings/virtual-user/virtual-user-agents.ts +0 -62
  492. package/src/wirings/virtual-user/virtual-user-catalogue.test.ts +0 -215
  493. package/src/wirings/virtual-user/virtual-user-catalogue.ts +0 -184
  494. package/src/wirings/virtual-user/virtual-user-derive.test.ts +0 -426
  495. package/src/wirings/virtual-user/virtual-user-derive.ts +0 -161
  496. package/src/wirings/virtual-user/virtual-user-dispositions.test.ts +0 -68
  497. package/src/wirings/virtual-user/virtual-user-dispositions.ts +0 -210
  498. package/src/wirings/virtual-user/virtual-user-intents.test.ts +0 -218
  499. package/src/wirings/virtual-user/virtual-user-intents.ts +0 -185
  500. package/src/wirings/virtual-user/virtual-user-rng.test.ts +0 -72
  501. package/src/wirings/virtual-user/virtual-user-rng.ts +0 -50
  502. package/src/wirings/virtual-user/virtual-user-run-store.ts +0 -131
  503. package/src/wirings/virtual-user/virtual-user-scaffold.test.ts +0 -854
  504. package/src/wirings/virtual-user/virtual-user-scaffold.ts +0 -664
  505. package/src/wirings/virtual-user/virtual-user-schedule-store.ts +0 -93
  506. package/src/wirings/virtual-user/virtual-user-schedule.test.ts +0 -280
  507. package/src/wirings/virtual-user/virtual-user-schedule.ts +0 -156
  508. package/src/wirings/virtual-user/virtual-user-target.ts +0 -47
  509. package/src/wirings/virtual-user/virtual-user.types.ts +0 -215
  510. package/src/wirings/workflow/dsl/workflow-dsl.types.ts +0 -709
  511. package/src/wirings/workflow/dsl/workflow-runner.ts +0 -25
  512. package/src/wirings/workflow/feature.test.ts +0 -131
  513. package/src/wirings/workflow/feature.ts +0 -59
  514. package/src/wirings/workflow/graph/graph-foreach-queued.test.ts +0 -569
  515. package/src/wirings/workflow/graph/graph-foreach.test.ts +0 -626
  516. package/src/wirings/workflow/graph/graph-node.ts +0 -115
  517. package/src/wirings/workflow/graph/graph-runner.test.ts +0 -1049
  518. package/src/wirings/workflow/graph/graph-runner.ts +0 -1376
  519. package/src/wirings/workflow/graph/graph-validation.test.ts +0 -27
  520. package/src/wirings/workflow/graph/graph-validation.ts +0 -41
  521. package/src/wirings/workflow/graph/template.test.ts +0 -49
  522. package/src/wirings/workflow/graph/template.ts +0 -42
  523. package/src/wirings/workflow/graph/wire-workflow-graph.ts +0 -39
  524. package/src/wirings/workflow/graph/workflow-graph.types.ts +0 -75
  525. package/src/wirings/workflow/index.ts +0 -107
  526. package/src/wirings/workflow/pikku-scenario-service.ts +0 -1013
  527. package/src/wirings/workflow/pikku-workflow-service.test.ts +0 -963
  528. package/src/wirings/workflow/pikku-workflow-service.ts +0 -1993
  529. package/src/wirings/workflow/run-timeline.test.ts +0 -199
  530. package/src/wirings/workflow/run-timeline.ts +0 -185
  531. package/src/wirings/workflow/scenario-cookie-jar.test.ts +0 -107
  532. package/src/wirings/workflow/scenario-cookie-jar.ts +0 -57
  533. package/src/wirings/workflow/scenario-expectations.test.ts +0 -238
  534. package/src/wirings/workflow/scenario-hooks.test.ts +0 -259
  535. package/src/wirings/workflow/scenario-poll.test.ts +0 -64
  536. package/src/wirings/workflow/scenario-poll.ts +0 -27
  537. package/src/wirings/workflow/scenario-prose.test.ts +0 -275
  538. package/src/wirings/workflow/scenario-prose.ts +0 -86
  539. package/src/wirings/workflow/scenario-run.types.ts +0 -193
  540. package/src/wirings/workflow/scenario-service.test.ts +0 -147
  541. package/src/wirings/workflow/scenario-step-guards.ts +0 -23
  542. package/src/wirings/workflow/scenario-step.test.ts +0 -800
  543. package/src/wirings/workflow/scenario-step.types.ts +0 -262
  544. package/src/wirings/workflow/scenario-surface.test.ts +0 -146
  545. package/src/wirings/workflow/scenario-surface.ts +0 -71
  546. package/src/wirings/workflow/scenario.types.ts +0 -63
  547. package/src/wirings/workflow/workflow-approval-audit.ts +0 -47
  548. package/src/wirings/workflow/workflow-approval-policy.test.ts +0 -594
  549. package/src/wirings/workflow/workflow-approval-policy.ts +0 -68
  550. package/src/wirings/workflow/workflow-approval.ts +0 -290
  551. package/src/wirings/workflow/workflow-child-run-session.test.ts +0 -168
  552. package/src/wirings/workflow/workflow-constants.ts +0 -66
  553. package/src/wirings/workflow/workflow-dispatch-durability.test.ts +0 -116
  554. package/src/wirings/workflow/workflow-dispatch-payload.test.ts +0 -55
  555. package/src/wirings/workflow/workflow-dispatch-relay.test.ts +0 -128
  556. package/src/wirings/workflow/workflow-errors.ts +0 -132
  557. package/src/wirings/workflow/workflow-inline-authority.test.ts +0 -175
  558. package/src/wirings/workflow/workflow-invocation-id.test.ts +0 -51
  559. package/src/wirings/workflow/workflow-invocation-id.ts +0 -28
  560. package/src/wirings/workflow/workflow-meta-resolver.ts +0 -45
  561. package/src/wirings/workflow/workflow-mirror.test.ts +0 -171
  562. package/src/wirings/workflow/workflow-missing-meta.test.ts +0 -92
  563. package/src/wirings/workflow/workflow-on-error.test.ts +0 -145
  564. package/src/wirings/workflow/workflow-queue-routing.ts +0 -129
  565. package/src/wirings/workflow/workflow-queue-wiring.ts +0 -149
  566. package/src/wirings/workflow/workflow-queue-workers.test.ts +0 -131
  567. package/src/wirings/workflow/workflow-queue-workers.ts +0 -66
  568. package/src/wirings/workflow/workflow-recovery.ts +0 -192
  569. package/src/wirings/workflow/workflow-replay-snapshot.test.ts +0 -140
  570. package/src/wirings/workflow/workflow-retry-policy.test.ts +0 -113
  571. package/src/wirings/workflow/workflow-run-authority.test.ts +0 -212
  572. package/src/wirings/workflow/workflow-run-context.test.ts +0 -172
  573. package/src/wirings/workflow/workflow-run-engine.types.ts +0 -92
  574. package/src/wirings/workflow/workflow-run-ownership.ts +0 -37
  575. package/src/wirings/workflow/workflow-run-polling.test.ts +0 -127
  576. package/src/wirings/workflow/workflow-stalled-recovery.test.ts +0 -152
  577. package/src/wirings/workflow/workflow-status-stream.test.ts +0 -354
  578. package/src/wirings/workflow/workflow-status-stream.ts +0 -144
  579. package/src/wirings/workflow/workflow-step-claim.ts +0 -46
  580. package/src/wirings/workflow/workflow-step-ordinal.test.ts +0 -94
  581. package/src/wirings/workflow/workflow-step-session.test.ts +0 -87
  582. package/src/wirings/workflow/workflow-suspend.ts +0 -61
  583. package/src/wirings/workflow/workflow-terminal-run-guard.test.ts +0 -105
  584. package/src/wirings/workflow/workflow.types.ts +0 -268
  585. package/src/wirings-stay-decoupled.test.ts +0 -130
  586. package/tsconfig.json +0 -14
  587. package/tsconfig.tsbuildinfo +0 -1
  588. 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
- }