@pikku/core 0.12.107 → 0.12.112

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