@pikku/core 0.12.72 → 0.12.74

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 (811) hide show
  1. package/CHANGELOG.md +793 -0
  2. package/dist/crypto-utils.d.ts +30 -5
  3. package/dist/crypto-utils.js +146 -41
  4. package/dist/dev/hot-reload.js +11 -30
  5. package/dist/dev/module-runner.d.ts +3 -7
  6. package/dist/dev/module-runner.js +4 -10
  7. package/dist/dev/reload-meta.d.ts +8 -20
  8. package/dist/dev/reload-meta.js +9 -29
  9. package/dist/errors/error-handler.d.ts +5 -30
  10. package/dist/errors/error-handler.js +16 -32
  11. package/dist/errors/errors.d.ts +32 -151
  12. package/dist/errors/errors.js +55 -157
  13. package/dist/function/abort-scope.d.ts +47 -0
  14. package/dist/function/abort-scope.js +63 -0
  15. package/dist/function/function-runner.js +37 -32
  16. package/dist/function/functions.types.d.ts +44 -136
  17. package/dist/function/functions.types.js +0 -58
  18. package/dist/function/list.types.d.ts +12 -62
  19. package/dist/function/list.types.js +4 -25
  20. package/dist/handle-error.d.ts +0 -13
  21. package/dist/handle-error.js +0 -18
  22. package/dist/index.d.ts +5 -2
  23. package/dist/index.js +4 -1
  24. package/dist/middleware/auth-apikey.d.ts +3 -18
  25. package/dist/middleware/auth-apikey.js +0 -17
  26. package/dist/middleware/auth-bearer.d.ts +6 -41
  27. package/dist/middleware/auth-bearer.js +3 -40
  28. package/dist/middleware/auth-cookie.d.ts +5 -27
  29. package/dist/middleware/auth-cookie.js +2 -26
  30. package/dist/middleware/cors.d.ts +7 -34
  31. package/dist/middleware/cors.js +7 -34
  32. package/dist/middleware/remote-auth.d.ts +3 -1
  33. package/dist/middleware/remote-auth.js +3 -2
  34. package/dist/middleware/telemetry.d.ts +8 -33
  35. package/dist/middleware/telemetry.js +2 -31
  36. package/dist/middleware-runner.d.ts +4 -55
  37. package/dist/middleware-runner.js +5 -74
  38. package/dist/permissions.d.ts +3 -44
  39. package/dist/permissions.js +19 -71
  40. package/dist/pikku-request.d.ts +0 -6
  41. package/dist/pikku-request.js +0 -6
  42. package/dist/pikku-state.d.ts +0 -26
  43. package/dist/pikku-state.js +2 -30
  44. package/dist/remote.d.ts +3 -5
  45. package/dist/remote.js +8 -7
  46. package/dist/schema.d.ts +5 -39
  47. package/dist/schema.js +5 -39
  48. package/dist/scopes.d.ts +4 -23
  49. package/dist/scopes.js +7 -48
  50. package/dist/services/ai-agent-runner-service.d.ts +20 -0
  51. package/dist/services/ai-embedding-service.d.ts +2 -25
  52. package/dist/services/audit-service.js +1 -2
  53. package/dist/services/content-service.d.ts +1 -46
  54. package/dist/services/credential-service.d.ts +3 -40
  55. package/dist/services/deployment-service.d.ts +3 -9
  56. package/dist/services/gateway-service.d.ts +0 -15
  57. package/dist/services/http-personas.d.ts +80 -0
  58. package/dist/services/http-personas.js +233 -0
  59. package/dist/services/in-memory-queue-service.d.ts +0 -14
  60. package/dist/services/in-memory-queue-service.js +1 -15
  61. package/dist/services/in-memory-trigger-service.d.ts +0 -18
  62. package/dist/services/in-memory-trigger-service.js +1 -18
  63. package/dist/services/in-memory-workflow-service.d.ts +0 -16
  64. package/dist/services/in-memory-workflow-service.js +4 -33
  65. package/dist/services/index.d.ts +5 -6
  66. package/dist/services/index.js +2 -5
  67. package/dist/services/istanbul-coverage-service.d.ts +1 -5
  68. package/dist/services/istanbul-coverage-service.js +2 -8
  69. package/dist/services/jwt-service.d.ts +1 -16
  70. package/dist/services/local-content.d.ts +13 -2
  71. package/dist/services/local-content.js +40 -13
  72. package/dist/services/local-gateway-service.d.ts +0 -16
  73. package/dist/services/local-gateway-service.js +2 -17
  74. package/dist/services/local-secrets.d.ts +0 -4
  75. package/dist/services/local-secrets.js +0 -4
  76. package/dist/services/logger-console.d.ts +3 -7
  77. package/dist/services/logger-console.js +3 -7
  78. package/dist/services/logger.d.ts +2 -37
  79. package/dist/services/meta-service.d.ts +23 -26
  80. package/dist/services/meta-service.js +22 -36
  81. package/dist/services/personas-service.d.ts +134 -0
  82. package/dist/services/personas-service.js +40 -0
  83. package/dist/services/pikku-user-id.js +0 -4
  84. package/dist/services/queue-webhook-service.d.ts +2 -36
  85. package/dist/services/queue-webhook-service.js +9 -41
  86. package/dist/services/scheduler-service.d.ts +1 -50
  87. package/dist/services/scheduler-service.js +0 -10
  88. package/dist/services/schema-service.d.ts +1 -24
  89. package/dist/services/scope-service.d.ts +49 -34
  90. package/dist/services/scoped-secret-service.d.ts +0 -4
  91. package/dist/services/scoped-secret-service.js +0 -4
  92. package/dist/services/secret-host-binding.d.ts +8 -0
  93. package/dist/services/secret-host-binding.js +36 -0
  94. package/dist/services/secret-service.d.ts +5 -33
  95. package/dist/services/secretless.d.ts +6 -0
  96. package/dist/services/secretless.js +21 -0
  97. package/dist/services/stub-tracker.d.ts +7 -18
  98. package/dist/services/stub-tracker.js +8 -18
  99. package/dist/services/system-role-guard.d.ts +33 -0
  100. package/dist/services/system-role-guard.js +38 -0
  101. package/dist/services/trigger-service.d.ts +0 -12
  102. package/dist/services/typed-secret-service.d.ts +0 -7
  103. package/dist/services/typed-secret-service.js +1 -7
  104. package/dist/services/v8-coverage-service.d.ts +2 -3
  105. package/dist/services/v8-coverage-service.js +1 -2
  106. package/dist/services/variables-service.d.ts +1 -8
  107. package/dist/services/webhook-service.d.ts +19 -63
  108. package/dist/services/webhook-service.js +6 -20
  109. package/dist/services/workflow-service.d.ts +3 -15
  110. package/dist/testing/service-tests.js +0 -17
  111. package/dist/time-utils.d.ts +0 -16
  112. package/dist/time-utils.js +1 -19
  113. package/dist/types/core.types.d.ts +99 -219
  114. package/dist/types/core.types.js +0 -42
  115. package/dist/types/state.types.d.ts +4 -9
  116. package/dist/utils/hmac.d.ts +4 -10
  117. package/dist/utils/hmac.js +4 -10
  118. package/dist/utils/safe-fetch.d.ts +7 -35
  119. package/dist/utils/safe-fetch.js +13 -53
  120. package/dist/utils.d.ts +1 -6
  121. package/dist/utils.js +6 -15
  122. package/dist/wirings/actor-flow/actor-flow.types.d.ts +1 -34
  123. package/dist/wirings/actor-flow/index.d.ts +0 -9
  124. package/dist/wirings/actor-flow/run-conversation.d.ts +5 -5
  125. package/dist/wirings/actor-flow/run-conversation.js +14 -7
  126. package/dist/wirings/ai-agent/ai-agent-agui.d.ts +0 -5
  127. package/dist/wirings/ai-agent/ai-agent-agui.js +34 -12
  128. package/dist/wirings/ai-agent/ai-agent-helpers.d.ts +7 -0
  129. package/dist/wirings/ai-agent/ai-agent-helpers.js +7 -0
  130. package/dist/wirings/ai-agent/ai-agent-interrupt.d.ts +153 -0
  131. package/dist/wirings/ai-agent/ai-agent-interrupt.js +256 -0
  132. package/dist/wirings/ai-agent/ai-agent-memory.js +0 -2
  133. package/dist/wirings/ai-agent/ai-agent-model-config.d.ts +0 -9
  134. package/dist/wirings/ai-agent/ai-agent-model-config.js +1 -9
  135. package/dist/wirings/ai-agent/ai-agent-prepare.d.ts +10 -99
  136. package/dist/wirings/ai-agent/ai-agent-prepare.js +58 -131
  137. package/dist/wirings/ai-agent/ai-agent-registry.d.ts +2 -1
  138. package/dist/wirings/ai-agent/ai-agent-registry.js +5 -1
  139. package/dist/wirings/ai-agent/ai-agent-runner.js +49 -20
  140. package/dist/wirings/ai-agent/ai-agent-stream.d.ts +25 -2
  141. package/dist/wirings/ai-agent/ai-agent-stream.js +153 -63
  142. package/dist/wirings/ai-agent/ai-agent.types.d.ts +84 -4
  143. package/dist/wirings/ai-agent/index.d.ts +6 -4
  144. package/dist/wirings/ai-agent/index.js +5 -4
  145. package/dist/wirings/ai-agent/voice-input.d.ts +39 -1
  146. package/dist/wirings/ai-agent/voice-input.js +46 -3
  147. package/dist/wirings/ai-agent/voice-output.d.ts +54 -1
  148. package/dist/wirings/ai-agent/voice-output.js +153 -50
  149. package/dist/wirings/channel/channel-common.d.ts +7 -20
  150. package/dist/wirings/channel/channel-common.js +7 -21
  151. package/dist/wirings/channel/channel-handler.js +25 -6
  152. package/dist/wirings/channel/channel-host-rpc.d.ts +25 -0
  153. package/dist/wirings/channel/channel-host-rpc.js +38 -0
  154. package/dist/wirings/channel/channel-middleware-runner.d.ts +0 -12
  155. package/dist/wirings/channel/channel-middleware-runner.js +0 -12
  156. package/dist/wirings/channel/channel-rpc-registry.d.ts +31 -0
  157. package/dist/wirings/channel/channel-rpc-registry.js +89 -0
  158. package/dist/wirings/channel/channel-rpc-responder.d.ts +15 -0
  159. package/dist/wirings/channel/channel-rpc-responder.js +71 -0
  160. package/dist/wirings/channel/channel-rpc-service.d.ts +40 -0
  161. package/dist/wirings/channel/channel-rpc-service.js +106 -0
  162. package/dist/wirings/channel/channel-rpc-validators.d.ts +14 -0
  163. package/dist/wirings/channel/channel-rpc-validators.js +30 -0
  164. package/dist/wirings/channel/channel-rpc.d.ts +5 -0
  165. package/dist/wirings/channel/channel-rpc.js +5 -0
  166. package/dist/wirings/channel/channel-rpc.types.d.ts +90 -0
  167. package/dist/wirings/channel/channel-rpc.types.js +50 -0
  168. package/dist/wirings/channel/channel-runner.d.ts +0 -4
  169. package/dist/wirings/channel/channel-runner.js +0 -14
  170. package/dist/wirings/channel/channel-store.d.ts +0 -10
  171. package/dist/wirings/channel/channel.types.d.ts +12 -1
  172. package/dist/wirings/channel/define-channel-routes.d.ts +0 -20
  173. package/dist/wirings/channel/define-channel-routes.js +0 -20
  174. package/dist/wirings/channel/eventhub-service.d.ts +0 -18
  175. package/dist/wirings/channel/index.d.ts +4 -1
  176. package/dist/wirings/channel/index.js +2 -0
  177. package/dist/wirings/channel/local/local-channel-runner.js +3 -1
  178. package/dist/wirings/channel/local/local-eventhub-service.d.ts +0 -33
  179. package/dist/wirings/channel/local/local-eventhub-service.js +2 -36
  180. package/dist/wirings/channel/log-channels.d.ts +0 -4
  181. package/dist/wirings/channel/log-channels.js +0 -4
  182. package/dist/wirings/channel/pikku-abstract-channel-handler.js +6 -0
  183. package/dist/wirings/channel/serverless/serverless-channel-runner.js +2 -5
  184. package/dist/wirings/cli/channel/cli-approval.d.ts +41 -0
  185. package/dist/wirings/cli/channel/cli-approval.js +81 -0
  186. package/dist/wirings/cli/channel/cli-channel-runner.d.ts +0 -4
  187. package/dist/wirings/cli/channel/cli-channel-runner.js +3 -25
  188. package/dist/wirings/cli/channel/cli-raw-channel-runner.d.ts +47 -9
  189. package/dist/wirings/cli/channel/cli-raw-channel-runner.js +24 -16
  190. package/dist/wirings/cli/channel/cli-raw-client-runner.d.ts +20 -0
  191. package/dist/wirings/cli/channel/cli-raw-client-runner.js +121 -0
  192. package/dist/wirings/cli/channel/index.d.ts +4 -0
  193. package/dist/wirings/cli/channel/index.js +2 -0
  194. package/dist/wirings/cli/cli-runner.d.ts +20 -20
  195. package/dist/wirings/cli/cli-runner.js +28 -89
  196. package/dist/wirings/cli/cli.types.d.ts +14 -3
  197. package/dist/wirings/cli/command-parser.d.ts +1 -10
  198. package/dist/wirings/cli/command-parser.js +10 -87
  199. package/dist/wirings/cli/define-cli-commands.d.ts +1 -17
  200. package/dist/wirings/cli/define-cli-commands.js +1 -17
  201. package/dist/wirings/credential/credential.types.d.ts +0 -12
  202. package/dist/wirings/credential/define-credential.d.ts +48 -0
  203. package/dist/wirings/credential/define-credential.js +47 -0
  204. package/dist/wirings/credential/index.d.ts +1 -1
  205. package/dist/wirings/credential/index.js +1 -1
  206. package/dist/wirings/credential/validate-credential-definitions.d.ts +2 -4
  207. package/dist/wirings/gateway/gateway-runner.d.ts +1 -20
  208. package/dist/wirings/gateway/gateway-runner.js +8 -105
  209. package/dist/wirings/gateway/gateway.types.d.ts +7 -80
  210. package/dist/wirings/http/http-routes.d.ts +0 -63
  211. package/dist/wirings/http/http-routes.js +0 -63
  212. package/dist/wirings/http/http-runner.d.ts +0 -99
  213. package/dist/wirings/http/http-runner.js +9 -165
  214. package/dist/wirings/http/http.types.d.ts +14 -55
  215. package/dist/wirings/http/log-http-routes.d.ts +0 -4
  216. package/dist/wirings/http/log-http-routes.js +0 -4
  217. package/dist/wirings/http/pikku-fetch-http-request.d.ts +0 -40
  218. package/dist/wirings/http/pikku-fetch-http-request.js +0 -58
  219. package/dist/wirings/http/pikku-fetch-http-response.js +0 -3
  220. package/dist/wirings/http/routers/path-to-regex.js +2 -13
  221. package/dist/wirings/http/web-request.d.ts +0 -8
  222. package/dist/wirings/http/web-request.js +25 -17
  223. package/dist/wirings/mcp/mcp-runner.d.ts +1 -4
  224. package/dist/wirings/mcp/mcp-runner.js +1 -14
  225. package/dist/wirings/mcp/mcp.types.d.ts +2 -35
  226. package/dist/wirings/oauth2/oauth2.types.d.ts +0 -28
  227. package/dist/wirings/oauth2/oauth2.types.js +0 -3
  228. package/dist/wirings/persona/define-personas.d.ts +28 -0
  229. package/dist/wirings/persona/define-personas.js +27 -0
  230. package/dist/wirings/persona/index.d.ts +21 -0
  231. package/dist/wirings/persona/index.js +17 -0
  232. package/dist/wirings/persona/persona-email.d.ts +37 -0
  233. package/dist/wirings/persona/persona-email.js +69 -0
  234. package/dist/wirings/persona/persona-environments.d.ts +45 -0
  235. package/dist/wirings/persona/persona-environments.js +81 -0
  236. package/dist/wirings/persona/persona-mailbox.d.ts +101 -0
  237. package/dist/wirings/persona/persona-mailbox.js +53 -0
  238. package/dist/wirings/persona/persona.types.d.ts +125 -0
  239. package/dist/wirings/persona/persona.types.js +1 -0
  240. package/dist/wirings/persona/validate-personas.d.ts +53 -0
  241. package/dist/wirings/persona/validate-personas.js +94 -0
  242. package/dist/wirings/queue/index.d.ts +3 -0
  243. package/dist/wirings/queue/index.js +2 -3
  244. package/dist/wirings/queue/queue-identity.d.ts +28 -0
  245. package/dist/wirings/queue/queue-identity.js +102 -0
  246. package/dist/wirings/queue/queue-runner.d.ts +0 -19
  247. package/dist/wirings/queue/queue-runner.js +9 -30
  248. package/dist/wirings/queue/queue.types.d.ts +18 -89
  249. package/dist/wirings/queue/register-queue-helper.d.ts +0 -12
  250. package/dist/wirings/queue/register-queue-helper.js +0 -11
  251. package/dist/wirings/queue/signed-queue-service.d.ts +16 -0
  252. package/dist/wirings/queue/signed-queue-service.js +42 -0
  253. package/dist/wirings/queue/validate-worker-config.d.ts +2 -23
  254. package/dist/wirings/queue/validate-worker-config.js +0 -14
  255. package/dist/wirings/role/define-system-role.d.ts +32 -0
  256. package/dist/wirings/role/define-system-role.js +31 -0
  257. package/dist/wirings/role/index.d.ts +3 -0
  258. package/dist/wirings/role/index.js +2 -0
  259. package/dist/wirings/role/role.types.d.ts +43 -0
  260. package/dist/wirings/role/role.types.js +1 -0
  261. package/dist/wirings/role/validate-role-definitions.d.ts +21 -0
  262. package/dist/wirings/role/validate-role-definitions.js +71 -0
  263. package/dist/wirings/rpc/addon-runner.d.ts +0 -19
  264. package/dist/wirings/rpc/addon-runner.js +0 -51
  265. package/dist/wirings/rpc/remote-addon-auth.d.ts +1 -12
  266. package/dist/wirings/rpc/remote-addon-auth.js +1 -9
  267. package/dist/wirings/rpc/rpc-runner.d.ts +11 -18
  268. package/dist/wirings/rpc/rpc-runner.js +88 -105
  269. package/dist/wirings/rpc/rpc-types.d.ts +7 -6
  270. package/dist/wirings/rpc/wire-addon.d.ts +25 -0
  271. package/dist/wirings/rpc/wire-addon.js +62 -0
  272. package/dist/wirings/rpc/wire-remote-addon.d.ts +3 -28
  273. package/dist/wirings/rpc/wire-remote-addon.js +0 -8
  274. package/dist/wirings/scheduler/log-schedulers.d.ts +0 -4
  275. package/dist/wirings/scheduler/log-schedulers.js +0 -4
  276. package/dist/wirings/scheduler/scheduler-runner.d.ts +0 -1
  277. package/dist/wirings/scheduler/scheduler-runner.js +0 -1
  278. package/dist/wirings/scheduler/scheduler.types.d.ts +1 -14
  279. package/dist/wirings/scope/define-scope.d.ts +32 -0
  280. package/dist/wirings/scope/define-scope.js +31 -0
  281. package/dist/wirings/scope/index.d.ts +1 -1
  282. package/dist/wirings/scope/index.js +1 -1
  283. package/dist/wirings/scope/scope.types.d.ts +7 -9
  284. package/dist/wirings/scope/validate-scope-definitions.d.ts +2 -21
  285. package/dist/wirings/scope/validate-scope-definitions.js +3 -21
  286. package/dist/wirings/secret/index.d.ts +1 -1
  287. package/dist/wirings/secret/index.js +1 -1
  288. package/dist/wirings/secret/secret.types.d.ts +19 -15
  289. package/dist/wirings/secret/secret.types.js +1 -1
  290. package/dist/wirings/secret/validate-secret-definitions.d.ts +2 -4
  291. package/dist/wirings/trigger/trigger-runner.d.ts +0 -27
  292. package/dist/wirings/trigger/trigger-runner.js +1 -24
  293. package/dist/wirings/trigger/trigger.types.d.ts +1 -82
  294. package/dist/wirings/trigger/trigger.types.js +0 -34
  295. package/dist/wirings/variable/index.d.ts +1 -1
  296. package/dist/wirings/variable/index.js +1 -1
  297. package/dist/wirings/variable/validate-variable-definitions.d.ts +2 -4
  298. package/dist/wirings/variable/variable.types.d.ts +1 -13
  299. package/dist/wirings/variable/variable.types.js +1 -1
  300. package/dist/wirings/virtual-user/index.d.ts +27 -0
  301. package/dist/wirings/virtual-user/index.js +8 -0
  302. package/dist/wirings/virtual-user/run-virtual-user.d.ts +92 -0
  303. package/dist/wirings/virtual-user/run-virtual-user.js +478 -0
  304. package/dist/wirings/virtual-user/virtual-user-agents.d.ts +38 -0
  305. package/dist/wirings/virtual-user/virtual-user-agents.js +24 -0
  306. package/dist/wirings/virtual-user/virtual-user-catalogue.d.ts +92 -0
  307. package/dist/wirings/virtual-user/virtual-user-catalogue.js +134 -0
  308. package/dist/wirings/virtual-user/virtual-user-derive.d.ts +26 -0
  309. package/dist/wirings/virtual-user/virtual-user-derive.js +137 -0
  310. package/dist/wirings/virtual-user/virtual-user-dispositions.d.ts +79 -0
  311. package/dist/wirings/virtual-user/virtual-user-dispositions.js +128 -0
  312. package/dist/wirings/virtual-user/virtual-user-intents.d.ts +78 -0
  313. package/dist/wirings/virtual-user/virtual-user-intents.js +142 -0
  314. package/dist/wirings/virtual-user/virtual-user-rng.d.ts +24 -0
  315. package/dist/wirings/virtual-user/virtual-user-rng.js +44 -0
  316. package/dist/wirings/virtual-user/virtual-user-target.d.ts +21 -0
  317. package/dist/wirings/virtual-user/virtual-user-target.js +34 -0
  318. package/dist/wirings/virtual-user/virtual-user.types.d.ts +199 -0
  319. package/dist/wirings/virtual-user/virtual-user.types.js +8 -0
  320. package/dist/wirings/workflow/dsl/workflow-dsl.types.d.ts +5 -5
  321. package/dist/wirings/workflow/dsl/workflow-runner.d.ts +0 -4
  322. package/dist/wirings/workflow/dsl/workflow-runner.js +0 -4
  323. package/dist/wirings/workflow/feature.d.ts +0 -19
  324. package/dist/wirings/workflow/feature.js +0 -19
  325. package/dist/wirings/workflow/graph/graph-node.d.ts +0 -98
  326. package/dist/wirings/workflow/graph/graph-node.js +0 -34
  327. package/dist/wirings/workflow/graph/graph-runner.js +6 -41
  328. package/dist/wirings/workflow/graph/wire-workflow-graph.d.ts +0 -4
  329. package/dist/wirings/workflow/graph/workflow-graph.types.d.ts +0 -58
  330. package/dist/wirings/workflow/graph/workflow-graph.types.js +0 -6
  331. package/dist/wirings/workflow/index.d.ts +5 -7
  332. package/dist/wirings/workflow/index.js +3 -17
  333. package/dist/wirings/workflow/pikku-scenario-service.d.ts +87 -5
  334. package/dist/wirings/workflow/pikku-scenario-service.js +204 -42
  335. package/dist/wirings/workflow/pikku-workflow-service.d.ts +7 -459
  336. package/dist/wirings/workflow/pikku-workflow-service.js +58 -551
  337. package/dist/wirings/workflow/run-timeline.d.ts +0 -47
  338. package/dist/wirings/workflow/run-timeline.js +0 -22
  339. package/dist/wirings/workflow/scenario-cookie-jar.d.ts +0 -23
  340. package/dist/wirings/workflow/scenario-cookie-jar.js +0 -16
  341. package/dist/wirings/workflow/scenario-poll.d.ts +0 -15
  342. package/dist/wirings/workflow/scenario-poll.js +0 -12
  343. package/dist/wirings/workflow/scenario-prose.d.ts +0 -28
  344. package/dist/wirings/workflow/scenario-prose.js +0 -18
  345. package/dist/wirings/workflow/scenario-step-guards.d.ts +0 -13
  346. package/dist/wirings/workflow/scenario-step-guards.js +1 -14
  347. package/dist/wirings/workflow/scenario-step.types.d.ts +72 -6
  348. package/dist/wirings/workflow/scenario-step.types.js +5 -1
  349. package/dist/wirings/workflow/scenario-surface.d.ts +16 -0
  350. package/dist/wirings/workflow/scenario-surface.js +56 -0
  351. package/dist/wirings/workflow/workflow-invocation-id.d.ts +0 -18
  352. package/dist/wirings/workflow/workflow-invocation-id.js +2 -22
  353. package/dist/wirings/workflow/workflow-queue-workers.d.ts +0 -20
  354. package/dist/wirings/workflow/workflow-queue-workers.js +0 -19
  355. package/dist/wirings/workflow/workflow.types.d.ts +0 -197
  356. package/knowledge/decisions/index.md +19 -0
  357. package/knowledge/decisions/internals/a-secret-that-fails-to-decrypt-fails-the-whole-read.md +49 -0
  358. package/knowledge/decisions/internals/actor-flow-conversations-seed-a-hidden-kickoff-message.md +23 -0
  359. package/knowledge/decisions/internals/actor-flow-drives-the-target-through-a-transport-seam.md +24 -0
  360. package/knowledge/decisions/internals/actor-flow-verdicts-are-llm-self-evaluations.md +25 -0
  361. package/knowledge/decisions/internals/addon-package-roots-resolve-by-walking-node-module-search-paths.md +26 -0
  362. package/knowledge/decisions/internals/addon-singleton-services-are-cached-per-namespace-not-per-package.md +33 -0
  363. package/knowledge/decisions/internals/addon-workflow-names-are-prefixed-with-the-consumer-namespace.md +28 -0
  364. package/knowledge/decisions/internals/ai-agent-agui-bridge-obeys-the-client-ordering-contract.md +29 -0
  365. package/knowledge/decisions/internals/ai-agent-audio-chunks-carry-the-format-the-provider-returned.md +20 -0
  366. package/knowledge/decisions/internals/ai-agent-credential-suspensions-hide-the-tool-result.md +26 -0
  367. package/knowledge/decisions/internals/ai-agent-delegate-and-supervise-hide-different-text.md +26 -0
  368. package/knowledge/decisions/internals/ai-agent-llm-tool-arguments-have-nulls-stripped.md +23 -0
  369. package/knowledge/decisions/internals/ai-agent-model-config-stays-a-single-resolution-seam.md +25 -0
  370. package/knowledge/decisions/internals/ai-agent-onerror-hooks-cannot-change-the-failure.md +22 -0
  371. package/knowledge/decisions/internals/ai-agent-runner-methods-must-keep-their-receiver.md +22 -0
  372. package/knowledge/decisions/internals/ai-agent-stream-persistence-is-best-effort.md +27 -0
  373. package/knowledge/decisions/internals/ai-agent-sub-agents-inherit-the-parent-context-block.md +26 -0
  374. package/knowledge/decisions/internals/ai-agent-tool-execute-failures-are-logged-unconditionally.md +25 -0
  375. package/knowledge/decisions/internals/ai-agent-voice-input-transcribes-audio-parts-in-place.md +22 -0
  376. package/knowledge/decisions/internals/ai-agent-working-memory-is-persisted-only-when-valid.md +25 -0
  377. package/knowledge/decisions/internals/channel-message-handlers-accept-three-config-shapes.md +30 -0
  378. package/knowledge/decisions/internals/channel-middleware-caches-only-statically-resolved-middleware.md +31 -0
  379. package/knowledge/decisions/internals/channel-state-is-per-socket-session-state-is-per-user.md +29 -0
  380. package/knowledge/decisions/internals/channel-user-id-is-persisted-after-onconnect-middleware-runs.md +28 -0
  381. package/knowledge/decisions/internals/cli-option-names-are-camelcase-in-state-and-kebab-on-the-command-line.md +27 -0
  382. package/knowledge/decisions/internals/cli-parse-errors-are-routed-by-message-prefix.md +28 -0
  383. package/knowledge/decisions/internals/cli-stdout-is-reserved-for-machine-readable-output.md +34 -0
  384. package/knowledge/decisions/internals/cli-unknown-long-options-warn-instead-of-failing.md +29 -0
  385. package/knowledge/decisions/internals/core-data-classification-brand-is-an-optional-property.md +34 -0
  386. package/knowledge/decisions/internals/core-function-runner-restores-the-wire-fields-it-overwrites.md +44 -0
  387. package/knowledge/decisions/internals/core-hot-reload-merges-generated-meta-never-replaces-it.md +39 -0
  388. package/knowledge/decisions/internals/core-hot-reload-owns-its-module-registry.md +42 -0
  389. package/knowledge/decisions/internals/core-middleware-order-is-scope-then-priority.md +39 -0
  390. package/knowledge/decisions/internals/core-schema-defaults-apply-on-every-transport.md +43 -0
  391. package/knowledge/decisions/internals/core-scopes-are-an-and-gate-separate-from-permissions.md +38 -0
  392. package/knowledge/decisions/internals/core-state-is-a-global-map-written-only-at-registration-time.md +44 -0
  393. package/knowledge/decisions/internals/email-meta-is-read-uncached-because-codegen-rewrites-it-mid-session.md +27 -0
  394. package/knowledge/decisions/internals/gateway-adapters-resolve-lazily-and-are-promise-cached.md +32 -0
  395. package/knowledge/decisions/internals/gateway-webhook-challenges-echo-bytes-not-json.md +28 -0
  396. package/knowledge/decisions/internals/gateway-wiring-is-a-meta-wiring-over-http-and-channels.md +31 -0
  397. package/knowledge/decisions/internals/generated-src-paths-in-pikku-meta-are-absolute.md +26 -0
  398. package/knowledge/decisions/internals/http-request-bodies-are-read-once-and-shared.md +32 -0
  399. package/knowledge/decisions/internals/http-route-groups-cascade-config-in-a-fixed-order.md +28 -0
  400. package/knowledge/decisions/internals/http-router-matches-normalized-paths-but-returns-registered-ones.md +30 -0
  401. package/knowledge/decisions/internals/http-runner-logs-through-a-trace-scoped-logger-functions-do-not.md +26 -0
  402. package/knowledge/decisions/internals/http-set-cookie-headers-are-appended-never-joined.md +28 -0
  403. package/knowledge/decisions/internals/http-sse-streams-flush-headers-only-after-middleware.md +32 -0
  404. package/knowledge/decisions/internals/http-wiring-without-metadata-is-skipped-not-fatal.md +26 -0
  405. package/knowledge/decisions/internals/in-a-scenario-a-4xx-is-data-not-an-exception.md +25 -0
  406. package/knowledge/decisions/internals/in-memory-workflow-history-aliases-the-live-step-object.md +26 -0
  407. package/knowledge/decisions/internals/index.md +113 -0
  408. package/knowledge/decisions/internals/istanbul-statement-counts-attach-to-the-start-line-only.md +25 -0
  409. package/knowledge/decisions/internals/local-trigger-and-gateway-services-assume-a-single-process.md +26 -0
  410. package/knowledge/decisions/internals/node-only-builtins-are-imported-dynamically.md +24 -0
  411. package/knowledge/decisions/internals/queue-group-concurrency-keeps-one-shared-queue-fair.md +28 -0
  412. package/knowledge/decisions/internals/queue-jobs-always-carry-an-explicit-attempts-count.md +27 -0
  413. package/knowledge/decisions/internals/remote-addons-dispatch-over-http-instead-of-local-meta.md +31 -0
  414. package/knowledge/decisions/internals/rpc-names-resolve-through-package-scope-before-root.md +32 -0
  415. package/knowledge/decisions/internals/scenario-agent-calls-sign-in-on-401-only.md +27 -0
  416. package/knowledge/decisions/internals/scenario-meta-lives-apart-from-app-meta-but-merges-when-read-off-disk.md +26 -0
  417. package/knowledge/decisions/internals/scenario-steps-return-drained-response-records.md +27 -0
  418. package/knowledge/decisions/internals/scope-roots-may-be-co-declared-by-an-addon-and-its-host-app.md +30 -0
  419. package/knowledge/decisions/internals/serverless-channel-disconnect-must-tolerate-a-missing-channel.md +28 -0
  420. package/knowledge/decisions/internals/the-dev-queue-copies-prod-timing-and-serialization-semantics.md +30 -0
  421. package/knowledge/decisions/internals/the-embedding-model-is-pinned-per-service-and-doc-query-embedding-is-split.md +29 -0
  422. package/knowledge/decisions/internals/the-in-memory-workflow-service-is-inline-only-and-single-process.md +27 -0
  423. package/knowledge/decisions/internals/the-kek-salt-is-scoped-to-the-key-version.md +40 -0
  424. package/knowledge/decisions/internals/the-schema-service-is-never-stubbed.md +26 -0
  425. package/knowledge/decisions/internals/trigger-declaration-is-split-from-trigger-source.md +33 -0
  426. package/knowledge/decisions/internals/typed-secret-service-caches-for-the-process-lifetime.md +26 -0
  427. package/knowledge/decisions/internals/webhook-delivery-history-records-every-attempt-best-effort.md +26 -0
  428. package/knowledge/decisions/internals/webhook-service-collaborators-are-constructor-args-not-locator-lookups.md +25 -0
  429. package/knowledge/decisions/internals/whether-a-run-is-inline-is-read-from-the-run-record.md +58 -0
  430. package/knowledge/decisions/internals/workflow-approval-expiry-is-decided-from-a-recorded-deadline.md +34 -0
  431. package/knowledge/decisions/internals/workflow-core-never-imports-a-browser-driver.md +42 -0
  432. package/knowledge/decisions/internals/workflow-dsl-meta-separates-runtime-expressions-from-literals.md +38 -0
  433. package/knowledge/decisions/internals/workflow-features-resolve-scenarios-by-object-identity.md +29 -0
  434. package/knowledge/decisions/internals/workflow-graph-inline-and-queued-runs-share-one-planner.md +42 -0
  435. package/knowledge/decisions/internals/workflow-graph-node-notes-are-excluded-from-the-graph-hash.md +25 -0
  436. package/knowledge/decisions/internals/workflow-inline-runs-report-their-run-id-before-they-can-fail.md +29 -0
  437. package/knowledge/decisions/internals/workflow-invocation-id-is-the-dedupe-key-not-step-id.md +43 -0
  438. package/knowledge/decisions/internals/workflow-queued-step-dispatch-requires-an-explicit-opt-in.md +29 -0
  439. package/knowledge/decisions/internals/workflow-queues-are-per-workflow-by-default.md +42 -0
  440. package/knowledge/decisions/internals/workflow-repeated-step-names-get-an-ordinal-suffix.md +33 -0
  441. package/knowledge/decisions/internals/workflow-replay-reads-its-steps-once-and-caches-only-the-immutable-half.md +32 -0
  442. package/knowledge/decisions/internals/workflow-retries-are-owned-by-the-workflow-not-the-queue.md +31 -0
  443. package/knowledge/decisions/internals/workflow-run-capabilities-are-extensions-not-subclasses.md +39 -0
  444. package/knowledge/decisions/internals/workflow-run-mirror-is-never-a-source-of-truth.md +29 -0
  445. package/knowledge/decisions/internals/workflow-run-polling-backs-off-to-the-callers-ceiling.md +33 -0
  446. package/knowledge/decisions/internals/workflow-run-timeline-is-a-pure-fold-over-durable-history.md +37 -0
  447. package/knowledge/decisions/internals/workflow-scenario-assertions-never-retry-and-record-one-step.md +50 -0
  448. package/knowledge/decisions/internals/workflow-scenario-hooks-are-a-scenario-only-affordance.md +43 -0
  449. package/knowledge/decisions/internals/workflow-scenario-prose-is-rendered-from-typed-calls-not-parsed-from-english.md +32 -0
  450. package/knowledge/decisions/internals/workflow-scenario-quarantine-reason-lives-in-code.md +18 -0
  451. package/knowledge/decisions/internals/workflow-scenario-step-targets-are-string-literals-for-the-inspector.md +34 -0
  452. package/knowledge/decisions/internals/workflow-step-compensation-runs-as-its-own-durable-step.md +26 -0
  453. package/knowledge/decisions/internals/workflow-step-dispatch-failure-is-transient-not-a-run-failure.md +33 -0
  454. package/knowledge/decisions/internals/workflow-step-lock-is-held-only-to-claim-the-step.md +27 -0
  455. package/knowledge/decisions/internals/workflow-step-rpc-name-is-provenance-only.md +34 -0
  456. package/knowledge/decisions/internals/workflow-suspend-and-approval-reasons-are-durable-step-identities.md +38 -0
  457. package/knowledge/decisions/internals/workflow-suspended-runs-keep-their-in-process-context.md +30 -0
  458. package/knowledge/decisions/security/a-dropped-audit-write-is-always-logged.md +26 -0
  459. package/knowledge/decisions/security/actor-flow-missing-approval-decisions-default-to-denied.md +22 -0
  460. package/knowledge/decisions/security/actor-sign-in-is-proven-by-set-cookie-not-a-non-empty-jar.md +27 -0
  461. package/knowledge/decisions/security/actor-sign-in-only-works-for-actor-flagged-users.md +27 -0
  462. package/knowledge/decisions/security/addon-auth-and-tags-only-tighten.md +43 -0
  463. package/knowledge/decisions/security/addon-config-gates-apply-only-at-the-namespaced-rpc-boundary.md +52 -0
  464. package/knowledge/decisions/security/addon-scopes-are-resolved-where-the-function-runs.md +46 -0
  465. package/knowledge/decisions/security/ai-agent-approval-forwarding-requires-a-symbol-brand.md +27 -0
  466. package/knowledge/decisions/security/ai-agent-credential-requests-are-symbol-branded.md +37 -0
  467. package/knowledge/decisions/security/ai-agent-gate-requires-a-session-only-when-auth-is-true.md +31 -0
  468. package/knowledge/decisions/security/ai-agent-ownership-failures-never-echo-the-resource.md +23 -0
  469. package/knowledge/decisions/security/ai-agent-resume-re-runs-the-authorization-gate.md +22 -0
  470. package/knowledge/decisions/security/ai-agent-sessionless-deployments-have-no-thread-ownership.md +39 -0
  471. package/knowledge/decisions/security/ai-agent-thread-ownership-composes-the-session-principal.md +30 -0
  472. package/knowledge/decisions/security/ai-agent-tool-filtering-reads-the-live-function-config.md +24 -0
  473. package/knowledge/decisions/security/an-empty-owners-constraint-matches-nothing.md +30 -0
  474. package/knowledge/decisions/security/an-exposed-ungated-function-is-a-codegen-warning.md +49 -0
  475. package/knowledge/decisions/security/console-addon-privileged-functions-gate-themselves.md +76 -0
  476. package/knowledge/decisions/security/core-safe-fetch-blocks-ssrf-by-host-literal-not-dns.md +39 -0
  477. package/knowledge/decisions/security/core-secrets-use-a-per-secret-dek-wrapped-by-a-kek.md +37 -0
  478. package/knowledge/decisions/security/gateway-handlers-run-through-the-function-runner-gate.md +31 -0
  479. package/knowledge/decisions/security/gateway-middleware-sessions-must-be-bridged-onto-the-wire.md +30 -0
  480. package/knowledge/decisions/security/global-permissions-and-function-permissions-are-independent-gates.md +40 -0
  481. package/knowledge/decisions/security/http-error-detail-is-withheld-from-clients-in-production.md +33 -0
  482. package/knowledge/decisions/security/http-request-bodies-are-bounded-before-they-are-buffered.md +46 -0
  483. package/knowledge/decisions/security/index.md +55 -0
  484. package/knowledge/decisions/security/mcp-internal-error-details-are-double-gated-on-production.md +27 -0
  485. package/knowledge/decisions/security/passphrases-are-stretched-key-material-is-expanded.md +40 -0
  486. package/knowledge/decisions/security/permission-auth-filtering-requires-live-permission-functions.md +31 -0
  487. package/knowledge/decisions/security/pikku-carries-actor-scopes-as-data-and-the-app-grants-them.md +26 -0
  488. package/knowledge/decisions/security/queue-job-identities-are-signed-at-enqueue.md +69 -0
  489. package/knowledge/decisions/security/queue-jobs-carry-the-producers-pikku-user-id.md +39 -0
  490. package/knowledge/decisions/security/remote-addon-tokens-are-client-credentials-not-mesh-trust.md +34 -0
  491. package/knowledge/decisions/security/scaffold-features-are-authenticated-unless-opted-out.md +49 -0
  492. package/knowledge/decisions/security/scenario-step-functions-are-never-externally-invocable.md +30 -0
  493. package/knowledge/decisions/security/scope-resolution-happens-at-the-session-boundary-and-sync-never-deletes.md +28 -0
  494. package/knowledge/decisions/security/self-authentication-is-declared-not-detected.md +34 -0
  495. package/knowledge/decisions/security/signed-content-urls-bind-the-request-path.md +37 -0
  496. package/knowledge/decisions/security/webhook-bodies-are-signed-before-they-are-enqueued.md +25 -0
  497. package/knowledge/decisions/security/workflow-actor-steps-always-use-the-real-transport.md +34 -0
  498. package/knowledge/decisions/security/workflow-approval-payloads-are-validated-on-replay-inside-the-workflow.md +40 -0
  499. package/knowledge/decisions/security/workflow-queued-steps-rehydrate-their-session-from-the-run-wire.md +32 -0
  500. package/knowledge/decisions/security/workflow-scenario-sessions-are-isolated-per-actor-and-per-scenario.md +32 -0
  501. package/knowledge/decisions/security/workflow-scenario-steps-are-never-network-invocable.md +31 -0
  502. package/knowledge/index.md +24 -0
  503. package/knowledge/questions/index.md +15 -0
  504. package/package.json +4 -1
  505. package/run-tests.sh +0 -0
  506. package/src/crypto-utils.test.ts +460 -19
  507. package/src/crypto-utils.ts +283 -55
  508. package/src/data-classification.ts +1 -7
  509. package/src/dev/hot-reload.test.ts +0 -4
  510. package/src/dev/hot-reload.ts +11 -30
  511. package/src/dev/module-runner.ts +7 -32
  512. package/src/dev/reload-meta.ts +9 -29
  513. package/src/errors/error-handler.ts +20 -35
  514. package/src/errors/error.test.ts +30 -1
  515. package/src/errors/errors.ts +73 -157
  516. package/src/function/abort-scope.test.ts +97 -0
  517. package/src/function/abort-scope.ts +80 -0
  518. package/src/function/function-runner.test.ts +0 -7
  519. package/src/function/function-runner.ts +62 -32
  520. package/src/function/functions.types.ts +56 -136
  521. package/src/function/list.types.test.ts +3 -25
  522. package/src/function/list.types.ts +12 -62
  523. package/src/handle-error.ts +0 -18
  524. package/src/index.ts +60 -1
  525. package/src/middleware/auth-apikey.test.ts +0 -1
  526. package/src/middleware/auth-apikey.ts +0 -17
  527. package/src/middleware/auth-bearer.test.ts +0 -3
  528. package/src/middleware/auth-bearer.ts +3 -40
  529. package/src/middleware/auth-cookie.test.ts +0 -6
  530. package/src/middleware/auth-cookie.ts +2 -26
  531. package/src/middleware/cors.test.ts +34 -0
  532. package/src/middleware/cors.ts +12 -33
  533. package/src/middleware/remote-auth.test.ts +24 -9
  534. package/src/middleware/remote-auth.ts +10 -2
  535. package/src/middleware/telemetry.ts +2 -31
  536. package/src/middleware-runner.test.ts +0 -2
  537. package/src/middleware-runner.ts +5 -74
  538. package/src/permissions.test.ts +30 -0
  539. package/src/permissions.ts +24 -74
  540. package/src/pikku-request.ts +0 -6
  541. package/src/pikku-state.ts +2 -30
  542. package/src/production-barrels-stay-lean.test.ts +110 -0
  543. package/src/remote.test.ts +172 -0
  544. package/src/remote.ts +17 -7
  545. package/src/schema.ts +5 -39
  546. package/src/scopes.ts +7 -48
  547. package/src/services/ai-agent-runner-service.ts +20 -0
  548. package/src/services/ai-embedding-service.ts +3 -25
  549. package/src/services/audit-service.ts +1 -2
  550. package/src/services/content-service.ts +1 -46
  551. package/src/services/credential-service.ts +3 -40
  552. package/src/services/credential-wire-service.test.ts +0 -2
  553. package/src/services/deployment-service.ts +3 -9
  554. package/src/services/gateway-service.ts +0 -15
  555. package/src/services/{http-scenario-actors-converse.test.ts → http-personas-converse.test.ts} +38 -9
  556. package/src/services/{http-scenario-actors.test.ts → http-personas.test.ts} +39 -22
  557. package/src/services/{http-scenario-actors.ts → http-personas.ts} +85 -45
  558. package/src/services/in-memory-queue-service.ts +1 -15
  559. package/src/services/in-memory-trigger-service.ts +1 -18
  560. package/src/services/in-memory-workflow-service.test.ts +0 -13
  561. package/src/services/in-memory-workflow-service.ts +4 -38
  562. package/src/services/index.ts +20 -15
  563. package/src/services/istanbul-coverage-service.ts +2 -8
  564. package/src/services/jwt-service.ts +1 -16
  565. package/src/services/local-content.test.ts +159 -27
  566. package/src/services/local-content.ts +55 -23
  567. package/src/services/local-gateway-service.ts +2 -17
  568. package/src/services/local-secrets.ts +0 -4
  569. package/src/services/logger-console.test.ts +0 -1
  570. package/src/services/logger-console.ts +3 -7
  571. package/src/services/logger.ts +2 -37
  572. package/src/services/meta-service.test.ts +1 -5
  573. package/src/services/meta-service.ts +41 -61
  574. package/src/services/{scenario-actors-service.ts → personas-service.ts} +48 -43
  575. package/src/services/pikku-user-id.ts +0 -4
  576. package/src/services/queue-webhook-service.ts +9 -41
  577. package/src/services/scheduler-service.ts +1 -50
  578. package/src/services/schema-service.ts +1 -24
  579. package/src/services/scope-service.ts +50 -34
  580. package/src/services/scoped-secret-service.ts +0 -4
  581. package/src/services/secret-host-binding.test.ts +138 -0
  582. package/src/services/secret-host-binding.ts +51 -0
  583. package/src/services/secret-service.ts +5 -33
  584. package/src/services/secretless.test.ts +54 -0
  585. package/src/services/secretless.ts +29 -0
  586. package/src/services/stub-tracker.ts +8 -18
  587. package/src/services/system-role-guard.test.ts +93 -0
  588. package/src/services/system-role-guard.ts +71 -0
  589. package/src/services/trigger-service.ts +0 -12
  590. package/src/services/typed-secret-service.ts +1 -7
  591. package/src/services/v8-coverage-service.ts +3 -6
  592. package/src/services/variables-service.ts +1 -8
  593. package/src/services/webhook-service.ts +19 -63
  594. package/src/services/workflow-service.ts +3 -20
  595. package/src/testing/service-tests.ts +0 -26
  596. package/src/time-utils.ts +1 -19
  597. package/src/types/core.types.ts +116 -229
  598. package/src/types/state.types.ts +7 -9
  599. package/src/utils/hmac.ts +4 -10
  600. package/src/utils/safe-fetch.ts +13 -54
  601. package/src/utils.test.ts +11 -2
  602. package/src/utils.ts +6 -15
  603. package/src/wirings/actor-flow/actor-flow.types.ts +1 -34
  604. package/src/wirings/actor-flow/index.ts +0 -9
  605. package/src/wirings/actor-flow/run-conversation.test.ts +11 -6
  606. package/src/wirings/actor-flow/run-conversation.ts +19 -12
  607. package/src/wirings/ai-agent/ai-agent-agui.test.ts +75 -10
  608. package/src/wirings/ai-agent/ai-agent-agui.ts +36 -17
  609. package/src/wirings/ai-agent/ai-agent-helpers.ts +20 -0
  610. package/src/wirings/ai-agent/ai-agent-interrupt.test.ts +842 -0
  611. package/src/wirings/ai-agent/ai-agent-interrupt.ts +399 -0
  612. package/src/wirings/ai-agent/ai-agent-memory.ts +0 -2
  613. package/src/wirings/ai-agent/ai-agent-model-config.ts +1 -9
  614. package/src/wirings/ai-agent/ai-agent-prepare.test.ts +202 -31
  615. package/src/wirings/ai-agent/ai-agent-prepare.ts +82 -138
  616. package/src/wirings/ai-agent/ai-agent-registry.test.ts +191 -6
  617. package/src/wirings/ai-agent/ai-agent-registry.ts +18 -1
  618. package/src/wirings/ai-agent/ai-agent-resume-authorization.test.ts +0 -2
  619. package/src/wirings/ai-agent/ai-agent-runner.test.ts +11 -9
  620. package/src/wirings/ai-agent/ai-agent-runner.ts +67 -33
  621. package/src/wirings/ai-agent/ai-agent-stream.test.ts +205 -103
  622. package/src/wirings/ai-agent/ai-agent-stream.ts +192 -75
  623. package/src/wirings/ai-agent/ai-agent-thread-ownership.test.ts +301 -0
  624. package/src/wirings/ai-agent/ai-agent.types.ts +85 -4
  625. package/src/wirings/ai-agent/index.ts +35 -3
  626. package/src/wirings/ai-agent/voice-input.test.ts +72 -7
  627. package/src/wirings/ai-agent/voice-input.ts +48 -3
  628. package/src/wirings/ai-agent/voice-output.test.ts +422 -0
  629. package/src/wirings/ai-agent/voice-output.ts +216 -56
  630. package/src/wirings/channel/channel-common.ts +15 -20
  631. package/src/wirings/channel/channel-handler.test.ts +50 -0
  632. package/src/wirings/channel/channel-handler.ts +32 -11
  633. package/src/wirings/channel/channel-host-rpc.test.ts +150 -0
  634. package/src/wirings/channel/channel-host-rpc.ts +69 -0
  635. package/src/wirings/channel/channel-middleware-runner.test.ts +0 -1
  636. package/src/wirings/channel/channel-middleware-runner.ts +0 -12
  637. package/src/wirings/channel/channel-rpc-registry.ts +116 -0
  638. package/src/wirings/channel/channel-rpc-responder.ts +117 -0
  639. package/src/wirings/channel/channel-rpc-service.ts +146 -0
  640. package/src/wirings/channel/channel-rpc-validators.ts +65 -0
  641. package/src/wirings/channel/channel-rpc.test.ts +820 -0
  642. package/src/wirings/channel/channel-rpc.ts +5 -0
  643. package/src/wirings/channel/channel-rpc.types.ts +150 -0
  644. package/src/wirings/channel/channel-runner.ts +0 -14
  645. package/src/wirings/channel/channel-store.ts +0 -10
  646. package/src/wirings/channel/channel.types.ts +19 -8
  647. package/src/wirings/channel/define-channel-routes.ts +0 -20
  648. package/src/wirings/channel/eventhub-service.ts +0 -18
  649. package/src/wirings/channel/index.ts +35 -0
  650. package/src/wirings/channel/local/local-channel-handler.ts +3 -1
  651. package/src/wirings/channel/local/local-channel-runner.test.ts +0 -10
  652. package/src/wirings/channel/local/local-channel-runner.ts +3 -1
  653. package/src/wirings/channel/local/local-eventhub-service.test.ts +0 -13
  654. package/src/wirings/channel/local/local-eventhub-service.ts +2 -37
  655. package/src/wirings/channel/log-channels.ts +0 -4
  656. package/src/wirings/channel/pikku-abstract-channel-handler.test.ts +83 -2
  657. package/src/wirings/channel/pikku-abstract-channel-handler.ts +7 -0
  658. package/src/wirings/channel/serverless/serverless-channel-runner.ts +2 -5
  659. package/src/wirings/cli/channel/cli-approval.test.ts +177 -0
  660. package/src/wirings/cli/channel/cli-approval.ts +135 -0
  661. package/src/wirings/cli/channel/cli-channel-runner.ts +4 -26
  662. package/src/wirings/cli/channel/cli-raw-channel-runner.test.ts +169 -0
  663. package/src/wirings/cli/channel/cli-raw-channel-runner.ts +59 -16
  664. package/src/wirings/cli/channel/cli-raw-client-runner.test.ts +480 -0
  665. package/src/wirings/cli/channel/cli-raw-client-runner.ts +155 -0
  666. package/src/wirings/cli/channel/index.ts +9 -0
  667. package/src/wirings/cli/cli-runner.test.ts +0 -1
  668. package/src/wirings/cli/cli-runner.ts +46 -88
  669. package/src/wirings/cli/cli.types.ts +14 -3
  670. package/src/wirings/cli/command-parser.test.ts +0 -4
  671. package/src/wirings/cli/command-parser.ts +11 -91
  672. package/src/wirings/cli/define-cli-commands.ts +1 -17
  673. package/src/wirings/credential/credential.types.ts +0 -12
  674. package/src/wirings/credential/{wire-credential.ts → define-credential.ts} +7 -7
  675. package/src/wirings/credential/index.ts +1 -1
  676. package/src/wirings/credential/validate-credential-definitions.ts +2 -4
  677. package/src/wirings/gateway/gateway-runner.test.ts +1 -21
  678. package/src/wirings/gateway/gateway-runner.ts +8 -110
  679. package/src/wirings/gateway/gateway.types.ts +7 -80
  680. package/src/wirings/http/http-routes.test.ts +0 -3
  681. package/src/wirings/http/http-routes.ts +0 -86
  682. package/src/wirings/http/http-runner.test.ts +0 -1
  683. package/src/wirings/http/http-runner.ts +8 -167
  684. package/src/wirings/http/http.types.ts +15 -62
  685. package/src/wirings/http/log-http-routes.ts +0 -4
  686. package/src/wirings/http/pikku-fetch-http-request.test.ts +2 -10
  687. package/src/wirings/http/pikku-fetch-http-request.ts +0 -58
  688. package/src/wirings/http/pikku-fetch-http-response.test.ts +1 -1
  689. package/src/wirings/http/pikku-fetch-http-response.ts +0 -3
  690. package/src/wirings/http/routers/path-to-regex.test.ts +4 -17
  691. package/src/wirings/http/routers/path-to-regex.ts +2 -13
  692. package/src/wirings/http/web-request.test.ts +33 -2
  693. package/src/wirings/http/web-request.ts +30 -17
  694. package/src/wirings/mcp/mcp-endpoint-registry.test.ts +0 -1
  695. package/src/wirings/mcp/mcp-runner.ts +2 -17
  696. package/src/wirings/mcp/mcp.types.ts +7 -42
  697. package/src/wirings/oauth2/oauth2.types.ts +0 -30
  698. package/src/wirings/persona/define-personas.ts +29 -0
  699. package/src/wirings/persona/index.ts +62 -0
  700. package/src/wirings/persona/persona-email.ts +87 -0
  701. package/src/wirings/persona/persona-environments.test.ts +183 -0
  702. package/src/wirings/persona/persona-environments.ts +138 -0
  703. package/src/wirings/persona/persona-mailbox.ts +156 -0
  704. package/src/wirings/persona/persona.test.ts +220 -0
  705. package/src/wirings/persona/persona.types.ts +131 -0
  706. package/src/wirings/persona/validate-personas.ts +133 -0
  707. package/src/wirings/queue/index.ts +13 -3
  708. package/src/wirings/queue/queue-identity.test.ts +453 -0
  709. package/src/wirings/queue/queue-identity.ts +173 -0
  710. package/src/wirings/queue/queue-runner.ts +12 -31
  711. package/src/wirings/queue/queue.types.ts +19 -89
  712. package/src/wirings/queue/register-queue-helper.ts +0 -14
  713. package/src/wirings/queue/signed-queue-service.ts +59 -0
  714. package/src/wirings/queue/validate-worker-config.ts +2 -28
  715. package/src/wirings/role/define-system-role.ts +33 -0
  716. package/src/wirings/role/index.ts +13 -0
  717. package/src/wirings/role/role.test.ts +104 -0
  718. package/src/wirings/role/role.types.ts +47 -0
  719. package/src/wirings/role/validate-role-definitions.ts +93 -0
  720. package/src/wirings/rpc/addon-auth-tags.test.ts +223 -0
  721. package/src/wirings/rpc/addon-runner.ts +0 -56
  722. package/src/wirings/rpc/addon-scopes.test.ts +225 -0
  723. package/src/wirings/rpc/remote-addon-auth.ts +1 -13
  724. package/src/wirings/rpc/rpc-runner.test.ts +186 -2
  725. package/src/wirings/rpc/rpc-runner.ts +145 -127
  726. package/src/wirings/rpc/rpc-types.ts +11 -6
  727. package/src/wirings/rpc/wire-addon.test.ts +43 -1
  728. package/src/wirings/rpc/wire-addon.ts +99 -0
  729. package/src/wirings/rpc/wire-remote-addon.ts +9 -29
  730. package/src/wirings/scheduler/log-schedulers.ts +0 -4
  731. package/src/wirings/scheduler/scheduler-runner.test.ts +1 -8
  732. package/src/wirings/scheduler/scheduler-runner.ts +0 -2
  733. package/src/wirings/scheduler/scheduler.types.ts +1 -14
  734. package/src/wirings/scope/{wire-scope.ts → define-scope.ts} +5 -6
  735. package/src/wirings/scope/index.ts +1 -1
  736. package/src/wirings/scope/scope.test.ts +1 -2
  737. package/src/wirings/scope/scope.types.ts +7 -9
  738. package/src/wirings/scope/validate-scope-definitions.ts +3 -21
  739. package/src/wirings/secret/index.ts +1 -1
  740. package/src/wirings/secret/secret.types.ts +19 -15
  741. package/src/wirings/secret/validate-secret-definitions.ts +2 -4
  742. package/src/wirings/trigger/trigger-runner.ts +1 -27
  743. package/src/wirings/trigger/trigger.types.ts +1 -82
  744. package/src/wirings/variable/index.ts +1 -1
  745. package/src/wirings/variable/validate-variable-definitions.ts +2 -4
  746. package/src/wirings/variable/variable.types.ts +1 -13
  747. package/src/wirings/virtual-user/index.ts +76 -0
  748. package/src/wirings/virtual-user/run-virtual-user.test.ts +765 -0
  749. package/src/wirings/virtual-user/run-virtual-user.ts +671 -0
  750. package/src/wirings/virtual-user/virtual-user-agents.test.ts +65 -0
  751. package/src/wirings/virtual-user/virtual-user-agents.ts +57 -0
  752. package/src/wirings/virtual-user/virtual-user-catalogue.test.ts +215 -0
  753. package/src/wirings/virtual-user/virtual-user-catalogue.ts +184 -0
  754. package/src/wirings/virtual-user/virtual-user-derive.test.ts +398 -0
  755. package/src/wirings/virtual-user/virtual-user-derive.ts +173 -0
  756. package/src/wirings/virtual-user/virtual-user-dispositions.test.ts +63 -0
  757. package/src/wirings/virtual-user/virtual-user-dispositions.ts +213 -0
  758. package/src/wirings/virtual-user/virtual-user-intents.test.ts +208 -0
  759. package/src/wirings/virtual-user/virtual-user-intents.ts +185 -0
  760. package/src/wirings/virtual-user/virtual-user-rng.test.ts +72 -0
  761. package/src/wirings/virtual-user/virtual-user-rng.ts +50 -0
  762. package/src/wirings/virtual-user/virtual-user-target.ts +47 -0
  763. package/src/wirings/virtual-user/virtual-user.types.ts +219 -0
  764. package/src/wirings/workflow/dsl/workflow-dsl.types.ts +5 -4
  765. package/src/wirings/workflow/dsl/workflow-runner.ts +0 -4
  766. package/src/wirings/workflow/feature.ts +0 -19
  767. package/src/wirings/workflow/graph/graph-node.ts +0 -136
  768. package/src/wirings/workflow/graph/graph-runner.test.ts +20 -19
  769. package/src/wirings/workflow/graph/graph-runner.ts +6 -41
  770. package/src/wirings/workflow/graph/wire-workflow-graph.ts +0 -4
  771. package/src/wirings/workflow/graph/workflow-graph.types.ts +0 -58
  772. package/src/wirings/workflow/index.ts +10 -44
  773. package/src/wirings/workflow/pikku-scenario-service.ts +235 -52
  774. package/src/wirings/workflow/pikku-workflow-service.test.ts +0 -39
  775. package/src/wirings/workflow/pikku-workflow-service.ts +77 -674
  776. package/src/wirings/workflow/run-timeline.test.ts +7 -19
  777. package/src/wirings/workflow/run-timeline.ts +0 -56
  778. package/src/wirings/workflow/scenario-cookie-jar.test.ts +0 -1
  779. package/src/wirings/workflow/scenario-cookie-jar.ts +0 -25
  780. package/src/wirings/workflow/scenario-expectations.test.ts +2 -7
  781. package/src/wirings/workflow/scenario-hooks.test.ts +2 -7
  782. package/src/wirings/workflow/scenario-poll.test.ts +0 -2
  783. package/src/wirings/workflow/scenario-poll.ts +0 -15
  784. package/src/wirings/workflow/scenario-prose.ts +0 -28
  785. package/src/wirings/workflow/scenario-service.test.ts +2 -9
  786. package/src/wirings/workflow/scenario-step-guards.ts +1 -14
  787. package/src/wirings/workflow/scenario-step.test.ts +159 -14
  788. package/src/wirings/workflow/scenario-step.types.ts +82 -6
  789. package/src/wirings/workflow/scenario-surface.test.ts +145 -0
  790. package/src/wirings/workflow/scenario-surface.ts +71 -0
  791. package/src/wirings/workflow/workflow-dispatch-durability.test.ts +14 -15
  792. package/src/wirings/workflow/workflow-dispatch-payload.test.ts +0 -4
  793. package/src/wirings/workflow/workflow-inline-authority.test.ts +169 -0
  794. package/src/wirings/workflow/workflow-invocation-id.test.ts +0 -2
  795. package/src/wirings/workflow/workflow-invocation-id.ts +2 -22
  796. package/src/wirings/workflow/workflow-mirror.test.ts +0 -7
  797. package/src/wirings/workflow/workflow-on-error.test.ts +0 -9
  798. package/src/wirings/workflow/workflow-queue-workers.ts +0 -21
  799. package/src/wirings/workflow/workflow-replay-snapshot.test.ts +8 -7
  800. package/src/wirings/workflow/workflow-retry-policy.test.ts +0 -5
  801. package/src/wirings/workflow/workflow-run-context.test.ts +5 -10
  802. package/src/wirings/workflow/workflow-run-polling.test.ts +0 -5
  803. package/src/wirings/workflow/workflow-step-ordinal.test.ts +19 -4
  804. package/src/wirings/workflow/workflow-step-session.test.ts +0 -7
  805. package/src/wirings/workflow/workflow.types.ts +0 -203
  806. package/tsconfig.tsbuildinfo +1 -1
  807. package/src/middleware/timeout.ts +0 -22
  808. package/src/pikku-response.ts +0 -5
  809. package/src/wirings/mcp/mcp-endpoint-registry.test.d.ts +0 -1
  810. package/src/wirings/workflow/dsl/index.ts +0 -36
  811. package/src/wirings/workflow/graph/index.ts +0 -15
@@ -0,0 +1,42 @@
1
+ ---
2
+ type: decision
3
+ title: Workflows get their own queues by default, and queue names are resolved from queue meta
4
+ description: Per-workflow queues stop one slow step head-of-line-blocking every other workflow; `shared-groups` trades that for one set of pollers
5
+ tags: workflow
6
+ ---
7
+
8
+ # Workflows get their own queues by default, and queue names are resolved from queue meta
9
+
10
+ `WorkflowQueueOptions.queueStrategy` defaults to `'per-workflow'`: each workflow
11
+ gets its own `wf-orchestrator-*` and `wf-step-*` queue. That gives complete
12
+ isolation and is what lets serverless providers deploy one unit per workflow,
13
+ at the cost of one set of pollers per queue — which adds up on pull-based
14
+ backends. `'shared-groups'` puts every workflow on the shared orchestrator and
15
+ step-worker queues and keeps them isolated with a per-group concurrency cap, so
16
+ there is one set of pollers for the whole system. It is only for single-process
17
+ runtimes; a per-unit serverless deploy needs the per-workflow queues to route to
18
+ its units. The options are passed to the service constructor rather than read
19
+ from `config.workflow` because the queues are wired during construction, before
20
+ singleton services — and so before `config` — exist.
21
+
22
+ Under `'shared-groups'`, `getJobGroup` returns a group keyed by workflow name
23
+ (steps group by step function, mirroring how per-step queues split them). It
24
+ returns `undefined` under `'per-workflow'`, because the queue name already
25
+ isolates the workflow and a group would cap it inside its own dedicated queue.
26
+ The group tier repeats the id so a workflow can be given its own limit purely
27
+ from config; note tiers can only lower a limit, never raise it above `default`,
28
+ because the backend applies its pre-fetch exclusion per group using `default`
29
+ alone.
30
+
31
+ `getOrchestratorQueueName` and `getStepWorkerQueueName` read `queue.meta`, which
32
+ is always populated globally, rather than `queue.registrations`, which only
33
+ covers queues this unit consumes. In a per-unit deploy the orchestrator unit
34
+ produces to the per-step queues without consuming them, so registrations would
35
+ miss them. `wireQueueWorkers` warns loudly when workflows exist but no
36
+ `wf-orchestrator-*` queue is in meta: everything still "works" via the shared
37
+ fallback, but the isolation is gone and one slow step head-of-line-blocks every
38
+ other workflow — invisible until a queue starves.
39
+
40
+ **What this rules out:** defaulting to `'shared-groups'`, returning a job group
41
+ under `'per-workflow'`, resolving queue names from `queue.registrations`, or
42
+ dropping the wiring-time warning as noise.
@@ -0,0 +1,33 @@
1
+ ---
2
+ type: decision
3
+ title: A workflow step name repeated in one run gets an ordinal suffix, and the first reach stays bare
4
+ description: `name`, `name#1`, `name#2` keys each reach separately without changing the durable key of any existing run
5
+ tags: workflow
6
+ ---
7
+
8
+ # A workflow step name repeated in one run gets an ordinal suffix, and the first reach stays bare
9
+
10
+ `nextStepKey` in `pikku-workflow-service.ts` turns a logical step name into the
11
+ physical, replay-stable key for the Nth time the run reaches it: the bare name
12
+ for ordinal 0, `name#N` for repeats. Keeping the first reach unsuffixed is what
13
+ makes the scheme backward compatible — existing runs and their persisted step
14
+ rows are untouched — while still letting the same literal step name be invoked
15
+ several times (a loop, a helper called twice) without the rows clobbering each
16
+ other.
17
+
18
+ The counters live in the per-replay `RunContext.replay.ordinals` and are reset
19
+ by `beginReplay` on every orchestrator tick. That reset is what makes the keys
20
+ deterministic: given a deterministic workflow body, the second replay assigns
21
+ exactly the same key to the same reach and therefore finds the cached row rather
22
+ than minting `name#1` for a step that already succeeded. `nextStepKey` also
23
+ records the key it just produced as `replay.lastStep`, which is how the next
24
+ step learns its predecessor (`fromStepName`).
25
+
26
+ Graph runs use the same convention for cycle revisits, and
27
+ `stripInstanceOrdinal` / `executeWorkflowStep` map `node#N` back to the logical
28
+ node id, which is not a literal key in the graph's `nodes` map.
29
+
30
+ **What this rules out:** suffixing the first reach too (it re-keys every step of
31
+ every existing run), advancing the ordinal counters anywhere other than a fresh
32
+ replay pass, deriving step keys from a non-deterministic source, or treating a
33
+ physical step key as a graph node id without stripping the ordinal.
@@ -0,0 +1,32 @@
1
+ ---
2
+ type: decision
3
+ title: A workflow replay reads its steps once and caches only the run's immutable half
4
+ description: The per-replay snapshot collapses O(N^2) step reads to one, but caching mutable run fields would make the replay read a lie
5
+ tags: workflow
6
+ ---
7
+
8
+ # A workflow replay reads its steps once and caches only the run's immutable half
9
+
10
+ A DSL replay walks the workflow body from the top, and each step it passes asks
11
+ for its own row — so a run of N steps costs N reads per replay and O(N^2) over
12
+ its lifetime. `beginReplay` in `pikku-workflow-service.ts` opens a pass by
13
+ calling `listStepStates`, which backends able to answer in a single query
14
+ override; `loadOrCreateStep` then serves every step from that snapshot. This is
15
+ safe because a pass reaches each step key at most once and the steps it replays
16
+ past are `succeeded`, and therefore immutable.
17
+
18
+ `getRunIdentity` caches the run for the same pass, but only for its immutable
19
+ half — which workflow it is, the wire it was started on, its input. Anything
20
+ needing `status`, `output`, `error` or `state` must call `getRun`: those move
21
+ while the run executes and a cached copy would be a lie. The `replay` half of
22
+ `RunContext` is rebuilt from scratch on every orchestrator tick and torn down by
23
+ `endReplay`.
24
+
25
+ `loadOrCreateStep` also handles a concurrent replay creating the row after the
26
+ snapshot was taken: if `create()` throws, it re-reads the step, and only if that
27
+ read also fails does it rethrow the original — because in that case the insert
28
+ failed for its own reasons and that error is the one worth seeing.
29
+
30
+ **What this rules out:** widening the snapshot to cover mutable run fields,
31
+ keeping it across orchestrator ticks, or removing the re-read fallback in
32
+ `loadOrCreateStep` on the grounds that a duplicate insert "cannot happen".
@@ -0,0 +1,31 @@
1
+ ---
2
+ type: decision
3
+ title: Workflow step retries are owned by the workflow, never by the queue
4
+ description: A step's retry count is resolved once and always passed to the queue as `attempts`, so the queue can never apply its own default
5
+ tags: workflow
6
+ ---
7
+
8
+ # Workflow step retries are owned by the workflow, never by the queue
9
+
10
+ A step's retry policy comes from the step itself, falling back to
11
+ `DEFAULT_STEP_RETRIES` (5) when unset. The default is deliberately greater than
12
+ zero so a transient failure — a DB blip, a downstream restart, a deploy — is
13
+ ridden out without the author asking for it; that is safe only because every
14
+ step gets a stable `invocationId` to dedupe on. An explicit `retries: 0` means
15
+ exactly once and must be honoured.
16
+
17
+ `resolveStepJobOptions` in `pikku-workflow-service.ts` therefore ALWAYS emits
18
+ `attempts`, even when it is 1. Backoff defaults to exponential whenever at least
19
+ one retry remains, so retries ride out an outage instead of firing instantly; a
20
+ concrete `retryDelay` (`15000`, `'15s'`) selects fixed backoff, and only the
21
+ literal `'exponential'` selects exponential explicitly. `rpcStep` resolves the
22
+ policy once, before both persistence and dispatch, so the value stored on the
23
+ step row — which drives `retriesExhausted` — is the same number the queue turns
24
+ into `attempts`. `queueGraphNode` in `graph/graph-runner.ts` defaults node
25
+ retries to `DEFAULT_STEP_RETRIES` for the same reason.
26
+
27
+ **What this rules out:** dropping `attempts` when it equals 1 "because that is
28
+ the default anyway", resolving retries separately at the persistence and
29
+ dispatch sites, or letting the queue adapter's own `retry_limit` decide. Any of
30
+ those lets the queue re-run a step the workflow said to run once, or lets the
31
+ engine believe retries are exhausted while the queue keeps retrying.
@@ -0,0 +1,39 @@
1
+ ---
2
+ type: decision
3
+ title: Workflow run capabilities are extensions, not subclasses
4
+ description: Scenario support lives in a separate module behind `setRunExtension` because a bundler drops an unused module but never an unused class member
5
+ tags: workflow
6
+ ---
7
+
8
+ # Workflow run capabilities are extensions, not subclasses
9
+
10
+ `PikkuWorkflowService` names nothing about what a `WorkflowRunExtension` is for.
11
+ An extension is installed with `setRunExtension(create)` and receives a narrow
12
+ `WorkflowRunEngine` handle — `inlineStep`, `updateRunStatus`,
13
+ `onChildWorkflowFailed`, `verifyStepName` — so recording a durable step stays
14
+ available to it without becoming public API on every workflow service a
15
+ production app instantiates.
16
+
17
+ The reason it is not a subclass is bundle size: a bundler drops an unused
18
+ *module* but never an unused class member. Anything declared on
19
+ `PikkuWorkflowService` ships in every server built on Pikku, along with
20
+ everything it imports. `PikkuScenarioService` (`pikku-scenario-service.ts`) is
21
+ the one implementation today — steps, actors, lifecycle hooks, the browser
22
+ provider and the assertion wire members — and scenarios only ever run from
23
+ `pikku scenario run`, so the whole surface stays behind an import that only the
24
+ runner makes. It is not a workflow service in its own right because a scenario
25
+ is not a different kind of run; it is the same durable run with a step
26
+ vocabulary on top. `scenario-service.test.ts` asserts that none of those members
27
+ leak onto the base class.
28
+
29
+ `createScenarioRunner` in the same file bundles the two lines
30
+ `pikku scenario run` needs so no caller has to remember the capability is
31
+ installed rather than inherited. It pairs the extension with
32
+ `InMemoryWorkflowService`, which is the right engine because a scenario run is a
33
+ single external process driving a deployed app over its real transport: there
34
+ is nothing to persist and no second worker to resume it.
35
+
36
+ **What this rules out:** moving scenario steps, actors, hooks or the browser
37
+ provider onto `PikkuWorkflowService`, making `PikkuScenarioService` extend it,
38
+ or widening `WorkflowRunEngine` into "just pass the service" — each one puts the
39
+ scenario runtime, and its transitive imports, into every deployed Pikku server.
@@ -0,0 +1,29 @@
1
+ ---
2
+ type: decision
3
+ title: The workflow run mirror is an observability sink, never a second source of truth
4
+ description: Every mirrored write happens after the authoritative write lands, and a mirror failure can never fail the workflow
5
+ tags: workflow
6
+ ---
7
+
8
+ # The workflow run mirror is an observability sink, never a second source of truth
9
+
10
+ `WorkflowRunMirror` lets an executor shadow every state write to an external
11
+ read store — typically a DB the console UI queries while the run itself lives in
12
+ a Durable Object or Redis. Both properties that make it safe follow from the
13
+ single shape of `PikkuWorkflowService.mirrored()` in
14
+ `pikku-workflow-service.ts`: the mirror is only ever told about a write that has
15
+ already landed in the canonical store, and a mirror that is down or throwing is
16
+ caught, logged at warn, and otherwise invisible to the workflow. Even the log
17
+ call is wrapped, because singleton services may not be initialised yet.
18
+
19
+ Every public write method is a thin `mirrored(() => …Impl(...), (mirror) => …)`
20
+ pair over a `protected abstract` implementation. That pairing is the reason a
21
+ mirror method added to the interface but never wired into the service would
22
+ write nothing at runtime — `workflow-mirror.test.ts` spells out the full method
23
+ list to catch exactly that.
24
+
25
+ **What this rules out:** reading run or step state back from the mirror,
26
+ awaiting the mirror before returning the authoritative result, letting a mirror
27
+ rejection propagate, or writing to the mirror first so the two "stay in sync" —
28
+ any of which turns an index into a second, divergent source of truth that can
29
+ take the workflow down with it.
@@ -0,0 +1,33 @@
1
+ ---
2
+ type: decision
3
+ title: Workflow run polling starts short and backs off to the caller's ceiling
4
+ description: `pollIntervalMs` is a ceiling, not a cadence, and the wait lives in its own method so the schedule can be asserted without the clock
5
+ tags: workflow
6
+ ---
7
+
8
+ # Workflow run polling starts short and backs off to the caller's ceiling
9
+
10
+ `awaitRunEnd` in `pikku-workflow-service.ts` reads a run until it reaches an end
11
+ state, starting at `WORKFLOW_POLL_MIN_MS` (10ms) and multiplying by
12
+ `WORKFLOW_POLL_FACTOR` (1.6) up to the caller's `maxIntervalMs`. A fixed
13
+ interval is wrong at both ends: it makes a workflow that finished in
14
+ milliseconds wait out the whole interval anyway, and it keeps reading a
15
+ long-running one at full rate for its entire life. Backing off returns quick
16
+ runs promptly while a slow run's read cost grows logarithmically rather than
17
+ linearly with duration. So `runToCompletion`'s `pollIntervalMs` is a ceiling,
18
+ not a cadence.
19
+
20
+ An inline sub-workflow uses a lower ceiling, `WORKFLOW_CHILD_POLL_MAX_MS`
21
+ (500ms), because the parent step is blocked on it: every wait there is added
22
+ latency in the middle of a workflow rather than at its edge.
23
+
24
+ `waitBeforeNextRead` exists as its own method purely so the schedule can be
25
+ asserted directly (`workflow-run-polling.test.ts`). Timing a poll loop by the
26
+ clock measures the host's scheduler as much as the policy — `setTimeout(40)`
27
+ routinely returns late on a loaded CI runner — which makes the obvious test both
28
+ slow and flaky.
29
+
30
+ **What this rules out:** replacing the backoff with a fixed `pollIntervalMs`
31
+ sleep, reading `pollIntervalMs` as the first wait, giving child runs the
32
+ top-level ceiling, or inlining `waitBeforeNextRead` back into the loop as a bare
33
+ `setTimeout`.
@@ -0,0 +1,37 @@
1
+ ---
2
+ type: decision
3
+ title: The workflow run timeline is a pure fold over durable history, with the row's status as the authority
4
+ description: No IO in the fold keeps time-travel transport-independent; the terminal event comes from `status`, not from a timestamp every backend populates
5
+ tags: workflow
6
+ ---
7
+
8
+ # The workflow run timeline is a pure fold over durable history, with the row's status as the authority
9
+
10
+ `run-timeline.ts` turns `getRunHistory` — one row per step *attempt*, each with
11
+ lifecycle timestamps — into a flat, chronologically ordered event stream, and
12
+ folds that stream up to any point to recover what the run "knew" then: the same
13
+ step cache a replay would hold, plus the walked path. Both functions are pure,
14
+ with no IO, so they are trivially testable and the same fold works for the
15
+ Redis, Kysely and in-memory stores alike.
16
+
17
+ The terminal event is driven by the row's authoritative `status`, falling back
18
+ to `updatedAt` when the specific stamp is absent, because the lifecycle
19
+ timestamps are not populated by every backend — Kysely leaves
20
+ `succeededAt`/`runningAt` null. The intermediate `scheduled` and `running`
21
+ events are optional enrichment, emitted only when the backend recorded them; the
22
+ `pending` event always exists and is the one that carries provenance
23
+ (`fromStepName`).
24
+
25
+ Ordering is defensive: events sort by timestamp, then by `LIFECYCLE_ORDER`, then
26
+ by original index, so rows sharing an instant stay deterministic across
27
+ backends. In the fold, a retry's `pending` event reopens the step and drops the
28
+ prior result and error.
29
+
30
+ The index conventions are fixed and callers depend on them: `TimelineEvent.seq`
31
+ is a monotonic 0-based position, `attempt` is 1-based, and
32
+ `reconstructStateAt(at)` is inclusive whether `at` is a seq or a `Date` — a
33
+ point before the first event yields the empty initial state with `seq` of `-1`.
34
+
35
+ **What this rules out:** reading state inside the fold, deriving the terminal
36
+ event from `succeededAt`/`failedAt` alone, trusting history to arrive sorted, or
37
+ letting a retry's reopening event keep the previous attempt's outcome.
@@ -0,0 +1,50 @@
1
+ ---
2
+ type: decision
3
+ title: Scenario steps default to no retries, and a whole poll is one durable step
4
+ description: Retrying a failed assertion is wrong for a test primitive; recording the poll as one step means replay returns the outcome, not the loop
5
+ tags: workflow
6
+ ---
7
+
8
+ # Scenario steps default to no retries, and a whole poll is one durable step
9
+
10
+ `ScenarioStepOptions.retries` defaults to 0, unlike an ordinary workflow step
11
+ which inherits `DEFAULT_STEP_RETRIES`. `PikkuScenarioService.scenarioStep`
12
+ applies that default explicitly (`options?.retries ?? 0`). Retrying a failed
13
+ assertion is the wrong behaviour for a test primitive: it converts a real
14
+ failure into a slow flake and hides the very race the scenario was written to
15
+ catch.
16
+
17
+ `expectEventually` is the sanctioned way to wait. The entire poll — invoke,
18
+ check the predicate, sleep, repeat until `within` elapses — runs inside a single
19
+ `inlineStep`, so it is ONE recorded step and a replay returns the cached
20
+ outcome rather than re-polling. `expectError` and `expectService` are the same
21
+ shape: one durable step whose body contains the whole assertion, with a failure
22
+ message that names the step, the target, and what was actually seen.
23
+
24
+ `pollUntil` in `scenario-poll.ts` is the step-level equivalent, and its
25
+ `undefined` convention is deliberate: `undefined` means "not yet" and nothing
26
+ else, because `false`, `0` and `''` are all real answers — a probe asking
27
+ whether something happened reports `false` when it did not. It returns
28
+ `undefined` at the deadline rather than throwing, leaving the error to the
29
+ caller, who is the only one who knows what was being waited for. Its own
30
+ defaults are shorter than the assertion wrappers': `within` 15s, `interval`
31
+ 250ms.
32
+
33
+ `requireActor` and `requireScenarioEnv` (`scenario-step-guards.ts`) exist for
34
+ the mirror-image reason: `actor` and `env` are optional on the step wire because
35
+ a pure assertion step needs neither, so the narrowing happens in one place with
36
+ a message that says what to do, instead of every step file writing its own
37
+ guard. `scenarioStep`'s `verifyStepName` call doubles as the guard for `then`
38
+ being a wire member — an accidental `await scenario` calls it with a resolve
39
+ function, which lands as a loud named error instead of a silent hang.
40
+
41
+ The assertion options are lenient by design so a scenario reads as intent
42
+ rather than as an exact-match harness: `matches` is a substring test when it is
43
+ a string and a full match only when it is a `RegExp`; `calledWith` deep-equals
44
+ the first argument only; `times` means at-least-once unless a count is given;
45
+ `within` defaults to `'30s'` and `interval` to `'1s'`.
46
+
47
+ **What this rules out:** giving scenario steps the workflow retry default,
48
+ recording each poll iteration as its own step, treating a falsy `pollUntil`
49
+ result as "not yet", making `pollUntil` throw on timeout, or narrowing `actor`
50
+ by hand inside individual step files.
@@ -0,0 +1,43 @@
1
+ ---
2
+ type: decision
3
+ title: Scenario lifecycle hooks are a scenario-only affordance and never mask the failure they follow
4
+ description: A durable workflow replays, so a callback that reruns each replay has no honest meaning there
5
+ tags: workflow
6
+ ---
7
+
8
+ # Scenario lifecycle hooks are a scenario-only affordance and never mask the failure they follow
9
+
10
+ `PikkuScenarioService.scenarioHooks` (`pikku-scenario-service.ts`) returns hooks
11
+ only when `workflowMeta.source === 'scenario'`. A plain workflow is durable and
12
+ resumable, so a `before`/`after` callback that reran on every replay would have
13
+ no honest meaning — hooks exist for scenarios because a scenario run is a single
14
+ pass by a single external process.
15
+
16
+ A hook is not a pikku function: it has no id, no meta and no schema, so it
17
+ cannot go through `runPikkuFunc` and the runner records nothing for it. It gets
18
+ exactly what the scenario body gets — the same wire (which is how it reaches the
19
+ app through `actors`) and singleton services composed with this invocation's
20
+ wire services — and nothing else. `ScenarioHookError` keeps the original error
21
+ as `cause`, so the failure that actually happened is never lost behind the label
22
+ saying which phase it happened in.
23
+
24
+ `onAfterRunFunc` returns early on the `interrupted` outcome, because the run is
25
+ suspended or waiting and teardown would fire mid-flight. When the scenario has
26
+ already failed for its own reason, an `after`-hook failure is attached as
27
+ `cause` and logged rather than replacing the headline; only a teardown failure
28
+ after a *passing* scenario fails the run.
29
+
30
+ At the feature level (`workflow.types.ts`, `CoreFeature`), hooks run once around
31
+ the whole group — `before → a → b → c → after` — not per scenario. That is the
32
+ one thing a feature deliberately cannot express: gherkin's `Background:` runs
33
+ per scenario, and per-scenario setup is the scenario's own `before`.
34
+
35
+ `setScenarioEnvironment` on the same service is per-service, not per-run, for a
36
+ related reason: a runner process targets exactly one environment for every
37
+ scenario it executes, so threading it through each run would only create ways
38
+ for two runs in one process to disagree.
39
+
40
+ **What this rules out:** offering hooks on plain workflows, routing a hook
41
+ through `runPikkuFunc` so it gets recorded as a step, running `after` on an
42
+ interrupted run, letting a teardown failure overwrite the scenario's own
43
+ failure, or reinterpreting feature hooks as per-scenario `Background:`.
@@ -0,0 +1,32 @@
1
+ ---
2
+ type: decision
3
+ title: Scenario prose is rendered out of typed calls, not parsed into them
4
+ description: The inversion of cucumber — a readable report with no regex step registry to maintain
5
+ tags: workflow
6
+ ---
7
+
8
+ # Scenario prose is rendered out of typed calls, not parsed into them
9
+
10
+ `scenario-prose.ts` renders the English sentence a reporter shows for a scenario
11
+ step. It is the inversion of cucumber: instead of parsing English into a call
12
+ through a registry of regexes, the call is typed and the English is rendered out
13
+ of it. The readable report survives without anyone maintaining a step-definition
14
+ registry, and a step that no longer exists cannot leave a dangling phrase behind.
15
+
16
+ `renderScenarioProse` fills a step's `template` from the input the step was
17
+ actually called with, so the sentence names the values under test — "sees
18
+ @pikku/addon-todos" rather than "sees an addon in the gallery" three times over.
19
+ A placeholder with no recorded value renders as nothing and the surrounding
20
+ whitespace collapses, so an omitted optional input reads as a shorter sentence
21
+ rather than leaking a literal `{state}` into the report. `template` is
22
+ deliberately distinct from `description`: `description` documents what the step
23
+ does, `template` is what a reader of the report sees, and it falls back to
24
+ `description` when absent.
25
+
26
+ It lives in `@pikku/core` rather than in the CLI so the CLI reporter and the
27
+ console render the same sentence for the same step.
28
+
29
+ **What this rules out:** adding a gherkin parser or regex step registry,
30
+ rendering prose in the CLI reporter or console instead of core (the two would
31
+ drift), collapsing `template` into `description`, or making a missing
32
+ placeholder value render as the raw placeholder.
@@ -0,0 +1,18 @@
1
+ ---
2
+ type: decision
3
+ title: A quarantined scenario states its reason in code, not in a CI invocation
4
+ description: `skip` carries the why next to the scenario it applies to, and naming the scenario explicitly still runs it
5
+ tags: workflow
6
+ ---
7
+
8
+ # A quarantined scenario states its reason in code, not in a CI invocation
9
+
10
+ `WorkflowsMeta`'s `skip` field (`workflow.types.ts`) is a string, not a boolean.
11
+ A scenario held out of a default run has to say why, and stating the reason in
12
+ code keeps the quarantine next to the scenario it applies to rather than buried
13
+ in a CI invocation nobody reads. A skipped scenario is still runnable — naming
14
+ it explicitly with `--flows` runs it regardless — so quarantine is a default,
15
+ not a disablement.
16
+
17
+ **What this rules out:** turning `skip` into a boolean flag, moving the skip
18
+ list into CI configuration, or making a skipped scenario unrunnable.
@@ -0,0 +1,34 @@
1
+ ---
2
+ type: decision
3
+ title: Scenario step targets are string literals so the inspector can read them statically
4
+ description: `step/given/when/then` mirror `do`'s RPC shape because the extractor reads a literal, not an imported symbol
5
+ tags: workflow
6
+ ---
7
+
8
+ # Scenario step targets are string literals so the inspector can read them statically
9
+
10
+ `ScenarioStepInvocation` in `dsl/workflow-dsl.types.ts`, and the `step`,
11
+ `given`, `when` and `then` members of `PikkuScenarioWire`, all take
12
+ `(stepName, stepFunc: string, data?, options?)` — deliberately the same shape as
13
+ `WorkflowWireDoRPC`. The target is a string, not an imported symbol, because
14
+ the Pikku inspector is static analysis: it reads the argument as a literal and
15
+ does not resolve identifiers. Type safety comes back at the edges — the
16
+ generated `TypedScenario` narrows these over `FlattenedScenarioStepMap`.
17
+
18
+ `given`/`when`/`then` are pure sugar over `step`; the phase only changes the
19
+ prose a reporter renders (`scenario-prose.ts`), never what executes.
20
+
21
+ The prose direction is itself the decision: rather than parsing English into a
22
+ call the way cucumber does, `composeStepProse` renders English out of a typed
23
+ call, so a readable report survives without a regex registry paying for it. It
24
+ lives in core so the CLI reporter and the console render the same sentence for
25
+ the same step, and `renderStepTemplate` fills `{placeholders}` from the input
26
+ the step was actually called with — "sees @pikku/addon-todos" rather than the
27
+ same generic sentence three times. A placeholder with no recorded value renders
28
+ as nothing and the surrounding whitespace collapses, so an omitted optional
29
+ input reads as a shorter sentence rather than leaking a literal `{state}`.
30
+
31
+ **What this rules out:** changing `stepFunc` to accept the imported step config,
32
+ reordering the arguments away from `do`'s shape, giving `given`/`when`/`then`
33
+ behaviour of their own, or moving prose rendering into the CLI where the console
34
+ would drift from it.
@@ -0,0 +1,26 @@
1
+ ---
2
+ type: decision
3
+ title: A step's compensation handler runs as a durable step of its own, and never compensates itself
4
+ description: A refund or rollback must not fire twice on replay, so `onError` is recorded as `<step>:onError` with retries disabled
5
+ tags: workflow
6
+ ---
7
+
8
+ # A step's compensation handler runs as a durable step of its own, and never compensates itself
9
+
10
+ `runStepCompensation` in `pikku-workflow-service.ts` invokes a failed step's
11
+ `onError` RPC through `rpcStep` under the name `<stepName>:onError` rather than
12
+ calling it directly. Durability is the point: a compensation handler is
13
+ typically a refund or a rollback, and a bare invoke would fire again on every
14
+ replay that walks past the failed step. Recorded as a step, the second replay
15
+ finds it `succeeded` and returns the cached result.
16
+
17
+ It runs with `{ retries: 0 }` and its own `onError` is deliberately not
18
+ forwarded — a compensation handler cannot itself compensate. `onError` mirrors a
19
+ graph node's: the handler receives `{ error: { message } }`, and the original
20
+ error is still thrown afterwards, so the workflow fails either way. This is
21
+ compensation, not recovery.
22
+
23
+ **What this rules out:** invoking the `onError` RPC inline "since it's just
24
+ cleanup", giving it the workflow's default retry count, chaining a second
25
+ `onError` onto it, or swallowing the original error because the handler
26
+ succeeded.
@@ -0,0 +1,33 @@
1
+ ---
2
+ type: decision
3
+ title: A failed workflow step dispatch is transient infrastructure, not a run failure
4
+ description: Queue-unreachable errors leave the run running and the step pending so the orchestrator replays; marking the run failed loses it
5
+ tags: workflow
6
+ ---
7
+
8
+ # A failed workflow step dispatch is transient infrastructure, not a run failure
9
+
10
+ `WorkflowDispatchException` (`pikku-workflow-service.ts`) means the queue itself
11
+ could not accept the job — pg-boss momentarily down, the transport unreachable —
12
+ not that the step's own logic failed. Everywhere it surfaces (`dispatchStep`,
13
+ `sleepStep`, `startWorkflow`, `orchestrateWorkflow`) the run is left untouched:
14
+ the step stays `pending`, the run stays `running`, and the orchestrator job is
15
+ rethrown so the queue redelivers it and the workflow replays from its snapshot.
16
+
17
+ The ordering inside `rpcStep` and `sleepStep` is load-bearing: dispatch happens
18
+ BEFORE the step is marked `scheduled`. If the step were marked `scheduled`
19
+ first and the dispatch then failed, the next replay would see `scheduled`,
20
+ pause, and wait forever for a job that was never enqueued.
21
+
22
+ Redelivery is always safe because an orchestrator job is idempotent: it replays
23
+ the workflow from the snapshot and every already-completed step returns its
24
+ cached result rather than running again. The per-job `attempts` that
25
+ `resolveStepJobOptions` always emits is what makes redelivery happen at all — it
26
+ overrides a queue configured with `retry_limit 0`, so the workflow's retry
27
+ policy survives a conservative queue configuration.
28
+
29
+ **What this rules out:** folding `WorkflowDispatchException` into the generic
30
+ catch that calls `updateRunStatus(runId, 'failed', ...)`, swallowing it so the
31
+ run silently stalls, or reordering `setStepScheduled` above `dispatchStep` /
32
+ `scheduleSleep` to "keep the status writes together". Each strands or kills a
33
+ run that a redelivered orchestrator tick would have recovered on its own.
@@ -0,0 +1,27 @@
1
+ ---
2
+ type: decision
3
+ title: A workflow step lock is held only to claim the step, never across its execution
4
+ description: Holding the advisory lock — and its pooled connection — across step work exhausted the connection pool and self-deadlocked
5
+ tags: workflow
6
+ ---
7
+
8
+ # A workflow step lock is held only to claim the step, never across its execution
9
+
10
+ `executeWorkflowStep` in `pikku-workflow-service.ts` takes `withStepLock` for an
11
+ atomic check-and-mark-running only: it reads the step, returns `null` if the
12
+ step already `succeeded` or is already `running` (another worker owns it),
13
+ starts a fresh attempt if it `failed`, and otherwise marks it `running`. The
14
+ lock is then released, and the actual work plus result persistence run outside
15
+ it.
16
+
17
+ The guard is what makes that safe — once a step is `running`, any concurrent
18
+ worker returns early. The alternative was tried and failed: holding the advisory
19
+ lock, and therefore its pooled connection, across `executeGraphStep` (network
20
+ I/O plus further pool queries) let concurrent steps exhaust the connection pool
21
+ and self-deadlock.
22
+
23
+ **What this rules out:** widening the `withStepLock` callback to cover RPC
24
+ invocation, child-workflow start, `setStepResult` or `resumeWorkflow` — the
25
+ "obviously safer" refactor that reintroduces the deadlock. If a stronger
26
+ guarantee is ever needed it has to come from the claim itself, not from a longer
27
+ lock hold.
@@ -0,0 +1,34 @@
1
+ ---
2
+ type: decision
3
+ title: A workflow step's recorded `rpcName` is provenance only — nothing dispatches off it
4
+ description: It exists so a reader can join a runtime step row back to the declaration that produced it, especially when the durable name was built in a loop
5
+ tags: workflow
6
+ ---
7
+
8
+ # A workflow step's recorded `rpcName` is provenance only — nothing dispatches off it
9
+
10
+ `insertStepState` and `inlineStep` in `pikku-workflow-service.ts` record the
11
+ name a step was dispatched by: an RPC for a `workflow.do` step, a step function
12
+ for a scenario step, `null` for a closure. Nothing in the engine dispatches off
13
+ that value — it is stored so a reader can join a step back to the function that
14
+ ran it.
15
+
16
+ It earns its keep when the durable step name was built at runtime. A scenario
17
+ step called in a loop reaches the run as, say, `sees @pikku/addon-todos` while
18
+ it was declared as ``sees ${packageName}``; the recorded step-function name is
19
+ then the only way to join that row back to its declaration. `inlineStep` also
20
+ records the `data` the step was called with for the same reason: a reporter
21
+ renders each step's prose from it, so two calls to one step stay
22
+ distinguishable by what they were asked to check.
23
+
24
+ Step lineage is recorded alongside it. `fromStepName` is the predecessor that
25
+ scheduled a step — the walked transition — captured by `rpcStep`/`inlineStep`
26
+ *before* `nextStepKey` advances the lineage, and surfaced to a step as
27
+ `fromInvocationId`. In a cyclic graph `a → b → a → c`, the second `a` therefore
28
+ carries `b`'s id, which is what lets the walked path be reconstructed from the
29
+ chain alone.
30
+
31
+ **What this rules out:** treating the recorded name as the dispatch target,
32
+ dropping it for closure steps "since it is always null", capturing
33
+ `lastStepName` after `nextStepKey` has run, or omitting `data` on inline steps —
34
+ each breaks either the join back to source or the reconstructed path.
@@ -0,0 +1,38 @@
1
+ ---
2
+ type: decision
3
+ title: A suspend or approval `reason` is the step's durable identity, not just a message
4
+ description: The reason is namespaced and used raw as the step key, so it must be derived deterministically across replays
5
+ tags: workflow
6
+ ---
7
+
8
+ # A suspend or approval `reason` is the step's durable identity, not just a message
9
+
10
+ `getSuspendStepName` and `getApprovalStepName` in `pikku-workflow-service.ts`
11
+ derive a step key from the `reason` string — `__workflow_suspend:<reason>` and
12
+ `__workflow_approval:<reason>`. Each distinct reason is therefore its own step
13
+ row, which is what lets one workflow hold several independent suspends
14
+ (wait-for-build, then wait-for-approval) and lets a dynamic reason inside a loop
15
+ work exactly like a dynamic `do()` step name. The two prefixes are separate
16
+ namespaces so a suspend, an approval, and a `do`/`sleep` step of the same name
17
+ cannot collide.
18
+
19
+ Because the reason IS the identity, it must be derived deterministically: the
20
+ same replay must produce the same reason at the same point, or the run mints a
21
+ new suspend instead of finding the one it is waiting on. This is the same
22
+ contract as `do()` and `sleep()` step names.
23
+
24
+ An approval additionally stores its record under a run-state key built by
25
+ `approvalStateKey`, which hex-encodes the step name. The Mongo backend restricts
26
+ state keys to `/^[a-zA-Z0-9_]+$/` while a reason is arbitrary human text, and
27
+ one key per gate means two gates resolving concurrently cannot clobber each
28
+ other through a read-modify-write.
29
+
30
+ One consequence to know about: `approveStep`'s optional `reason` argument
31
+ addresses the *first* reach of a gate only. If a gate is reached again on a
32
+ later loop iteration, `nextStepKey` gives that row a `#N` suffix, and there is
33
+ currently no way for a caller to name it.
34
+
35
+ **What this rules out:** deriving a reason from a timestamp, a random id or
36
+ anything else that varies between replays; sharing one namespace (or one
37
+ run-state key) between suspends and approvals; and storing the reason raw as a
38
+ run-state key, which breaks on any backend that constrains key characters.