@pikku/core 0.12.78 → 0.12.80

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 (341) hide show
  1. package/CHANGELOG.md +234 -0
  2. package/dist/dev/hot-reload.js +1 -6
  3. package/dist/ecosystem.d.ts +27 -0
  4. package/dist/ecosystem.js +26 -0
  5. package/dist/errors/error-handler.js +4 -2
  6. package/dist/function/function-runner.js +20 -42
  7. package/dist/function/functions.types.d.ts +9 -5
  8. package/dist/index.d.ts +4 -9
  9. package/dist/index.js +3 -8
  10. package/dist/middleware/auth-cookie.d.ts +0 -4
  11. package/dist/middleware/auth-cookie.js +38 -2
  12. package/dist/middleware/cors.js +1 -0
  13. package/dist/middleware/index.d.ts +1 -1
  14. package/dist/middleware/index.js +1 -1
  15. package/dist/middleware/remote-auth.js +14 -3
  16. package/dist/permissions.d.ts +8 -10
  17. package/dist/permissions.js +0 -11
  18. package/dist/pikku-state.js +12 -5
  19. package/dist/schema.js +35 -1
  20. package/dist/services/ai-run-state-service.d.ts +7 -1
  21. package/dist/services/in-memory-ai-run-state-service.d.ts +1 -1
  22. package/dist/services/in-memory-ai-run-state-service.js +2 -1
  23. package/dist/services/in-memory-workflow-service.d.ts +10 -0
  24. package/dist/services/in-memory-workflow-service.js +25 -0
  25. package/dist/services/local-content-request-handler.d.ts +21 -0
  26. package/dist/services/local-content-request-handler.js +72 -53
  27. package/dist/services/local-content.d.ts +6 -0
  28. package/dist/services/local-content.js +14 -1
  29. package/dist/services/workflow-service.d.ts +4 -2
  30. package/dist/testing/service-tests/agent-run-service-tests.d.ts +10 -0
  31. package/dist/testing/service-tests/agent-run-service-tests.js +72 -0
  32. package/dist/testing/service-tests/ai-storage-service-tests.d.ts +3 -0
  33. package/dist/testing/service-tests/ai-storage-service-tests.js +226 -0
  34. package/dist/testing/service-tests/channel-store-tests.d.ts +3 -0
  35. package/dist/testing/service-tests/channel-store-tests.js +72 -0
  36. package/dist/testing/service-tests/credential-service-tests.d.ts +3 -0
  37. package/dist/testing/service-tests/credential-service-tests.js +109 -0
  38. package/dist/testing/service-tests/deployment-service-tests.d.ts +3 -0
  39. package/dist/testing/service-tests/deployment-service-tests.js +21 -0
  40. package/dist/testing/service-tests/event-hub-store-tests.d.ts +3 -0
  41. package/dist/testing/service-tests/event-hub-store-tests.js +34 -0
  42. package/dist/testing/service-tests/secret-service-tests.d.ts +3 -0
  43. package/dist/testing/service-tests/secret-service-tests.js +80 -0
  44. package/dist/testing/service-tests/session-store-tests.d.ts +3 -0
  45. package/dist/testing/service-tests/session-store-tests.js +43 -0
  46. package/dist/testing/service-tests/workflow-run-service-tests.d.ts +3 -0
  47. package/dist/testing/service-tests/workflow-run-service-tests.js +42 -0
  48. package/dist/testing/service-tests/workflow-service-tests.d.ts +3 -0
  49. package/dist/testing/service-tests/workflow-service-tests.js +150 -0
  50. package/dist/testing/service-tests.d.ts +6 -0
  51. package/dist/testing/service-tests.js +26 -791
  52. package/dist/types/core.types.d.ts +11 -1
  53. package/dist/types/state.types.d.ts +1 -1
  54. package/dist/utils/node-host-resolver.d.ts +12 -0
  55. package/dist/utils/node-host-resolver.js +16 -0
  56. package/dist/utils/safe-fetch.d.ts +18 -0
  57. package/dist/utils/safe-fetch.js +167 -29
  58. package/dist/wirings/actor-flow/run-conversation.js +1 -6
  59. package/dist/wirings/ai-agent/agent-rpc.d.ts +15 -0
  60. package/dist/wirings/ai-agent/agent-rpc.js +53 -0
  61. package/dist/wirings/ai-agent/ai-agent-agui.js +1 -5
  62. package/dist/wirings/ai-agent/ai-agent-memory.js +3 -1
  63. package/dist/wirings/ai-agent/ai-agent-prepare.js +3 -9
  64. package/dist/wirings/ai-agent/ai-agent-runner.js +28 -107
  65. package/dist/wirings/ai-agent/ai-agent-stream.js +20 -61
  66. package/dist/wirings/ai-agent/ai-agent-turn.d.ts +56 -0
  67. package/dist/wirings/ai-agent/ai-agent-turn.js +81 -0
  68. package/dist/wirings/ai-agent/ai-agent.types.d.ts +1 -1
  69. package/dist/wirings/ai-agent/voice-input.js +1 -6
  70. package/dist/wirings/ai-agent/voice-output.js +2 -12
  71. package/dist/wirings/channel/channel-common.js +1 -0
  72. package/dist/wirings/channel/channel-handler.js +3 -5
  73. package/dist/wirings/channel/channel-rpc-service.d.ts +0 -6
  74. package/dist/wirings/channel/channel-rpc-service.js +0 -8
  75. package/dist/wirings/channel/channel-rpc.types.d.ts +6 -0
  76. package/dist/wirings/channel/channel-rpc.types.js +8 -0
  77. package/dist/wirings/channel/channel-runner.d.ts +1 -3
  78. package/dist/wirings/channel/channel-runner.js +16 -8
  79. package/dist/wirings/channel/channel.types.d.ts +2 -0
  80. package/dist/wirings/channel/pikku-abstract-channel-handler.js +1 -0
  81. package/dist/wirings/channel/serverless/serverless-channel-runner.js +3 -0
  82. package/dist/wirings/cli/channel/cli-channel-runner.js +2 -0
  83. package/dist/wirings/cli/cli-runner.js +4 -2
  84. package/dist/wirings/cli/cli.types.d.ts +0 -8
  85. package/dist/wirings/cli/command-parser.js +13 -0
  86. package/dist/wirings/gateway/gateway-runner.js +41 -5
  87. package/dist/wirings/http/http-routes.js +2 -0
  88. package/dist/wirings/http/http-runner.d.ts +0 -10
  89. package/dist/wirings/http/http-runner.js +6 -13
  90. package/dist/wirings/http/http.types.d.ts +0 -10
  91. package/dist/wirings/http/index.d.ts +1 -1
  92. package/dist/wirings/http/index.js +1 -1
  93. package/dist/wirings/mcp/mcp-runner.d.ts +0 -7
  94. package/dist/wirings/mcp/mcp-runner.js +0 -6
  95. package/dist/wirings/rpc/rpc-runner.d.ts +8 -51
  96. package/dist/wirings/rpc/rpc-runner.js +53 -86
  97. package/dist/wirings/rpc/rpc-types.d.ts +3 -0
  98. package/dist/wirings/secret/validate-secret-definitions.js +2 -0
  99. package/dist/wirings/trigger/pikku-trigger-service.d.ts +0 -4
  100. package/dist/wirings/trigger/trigger-runner.js +1 -0
  101. package/dist/wirings/virtual-user/run-virtual-user.js +11 -11
  102. package/dist/wirings/virtual-user/virtual-user-derive.js +4 -25
  103. package/dist/wirings/virtual-user/virtual-user-dispositions.js +1 -4
  104. package/dist/wirings/workflow/dsl/workflow-dsl.types.d.ts +29 -2
  105. package/dist/wirings/workflow/feature.js +7 -4
  106. package/dist/wirings/workflow/graph/graph-runner.d.ts +1 -2
  107. package/dist/wirings/workflow/graph/graph-runner.js +8 -7
  108. package/dist/wirings/workflow/graph/workflow-graph.types.d.ts +0 -4
  109. package/dist/wirings/workflow/index.d.ts +7 -2
  110. package/dist/wirings/workflow/index.js +5 -1
  111. package/dist/wirings/workflow/pikku-scenario-service.d.ts +17 -3
  112. package/dist/wirings/workflow/pikku-scenario-service.js +38 -10
  113. package/dist/wirings/workflow/pikku-workflow-service.d.ts +44 -111
  114. package/dist/wirings/workflow/pikku-workflow-service.js +86 -404
  115. package/dist/wirings/workflow/workflow-approval.d.ts +39 -0
  116. package/dist/wirings/workflow/workflow-approval.js +114 -0
  117. package/dist/wirings/workflow/workflow-constants.d.ts +26 -0
  118. package/dist/wirings/workflow/workflow-constants.js +35 -0
  119. package/dist/wirings/workflow/workflow-errors.d.ts +58 -0
  120. package/dist/wirings/workflow/workflow-errors.js +112 -0
  121. package/dist/wirings/workflow/workflow-meta-resolver.d.ts +11 -0
  122. package/dist/wirings/workflow/workflow-meta-resolver.js +31 -0
  123. package/dist/wirings/workflow/workflow-queue-routing.d.ts +8 -0
  124. package/dist/wirings/workflow/workflow-queue-routing.js +38 -0
  125. package/dist/wirings/workflow/workflow-queue-wiring.d.ts +20 -0
  126. package/dist/wirings/workflow/workflow-queue-wiring.js +79 -0
  127. package/dist/wirings/workflow/workflow-recovery.d.ts +68 -0
  128. package/dist/wirings/workflow/workflow-recovery.js +101 -0
  129. package/dist/wirings/workflow/workflow-run-engine.types.d.ts +54 -0
  130. package/dist/wirings/workflow/workflow-run-engine.types.js +1 -0
  131. package/dist/wirings/workflow/workflow-run-ownership.d.ts +16 -0
  132. package/dist/wirings/workflow/workflow-run-ownership.js +29 -0
  133. package/dist/wirings/workflow/workflow-suspend.d.ts +12 -0
  134. package/dist/wirings/workflow/workflow-suspend.js +33 -0
  135. package/dist/wirings/workflow/workflow.types.d.ts +7 -0
  136. package/knowledge/decisions/internals/a-non-streaming-agent-run-registers-with-airunstate-too.md +22 -0
  137. package/knowledge/decisions/internals/a-resumed-agent-turn-is-as-interruptible-as-the-first.md +20 -0
  138. package/knowledge/decisions/internals/a-scenario-step-template-is-offered-unfilled.md +21 -0
  139. package/knowledge/decisions/internals/a-virtual-user-decides-whether-to-trust-memory-once-per-turn.md +21 -0
  140. package/knowledge/decisions/internals/a-wall-clock-threshold-is-a-load-test-in-disguise.md +46 -0
  141. package/knowledge/decisions/internals/a-workflow-wire-is-built-from-the-run-not-from-the-rpc-service.md +45 -0
  142. package/knowledge/decisions/internals/agent-context-waits-for-a-tool-result-still-being-written.md +21 -0
  143. package/knowledge/decisions/internals/agent-speech-travels-as-a-custom-agui-event.md +22 -0
  144. package/knowledge/decisions/internals/an-agent-interrupt-is-not-a-failure.md +23 -0
  145. package/knowledge/decisions/internals/an-agent-run-owned-by-another-instance-says-so.md +25 -0
  146. package/knowledge/decisions/internals/an-agent-stream-send-must-return-the-inner-sends-promise.md +22 -0
  147. package/knowledge/decisions/internals/an-empty-text-part-is-omitted-from-an-agent-message.md +20 -0
  148. package/knowledge/decisions/internals/an-empty-transcript-is-not-recorded.md +22 -0
  149. package/knowledge/decisions/internals/an-unref-d-timer-cannot-be-awaited-under-node-test.md +66 -0
  150. package/knowledge/decisions/internals/channel-state-accessors-are-unsound-generics-that-every-implementation-asserts.md +33 -0
  151. package/knowledge/decisions/internals/gateway-listener-middleware-runs-without-an-rpc-on-the-wire.md +43 -0
  152. package/knowledge/decisions/internals/hot-reload-writes-into-the-function-map-captured-at-startup.md +26 -0
  153. package/knowledge/decisions/internals/index.md +38 -26
  154. package/knowledge/decisions/internals/only-exposed-functions-enter-a-virtual-user-catalogue.md +21 -0
  155. package/knowledge/decisions/internals/scenario-given-and-when-are-sugar-but-then-is-not.md +24 -0
  156. package/knowledge/decisions/internals/side-effects-are-an-allowlist-not-a-boolean.md +34 -0
  157. package/knowledge/decisions/internals/speech-synthesis-picks-a-voice-per-sentence-and-warns-once.md +24 -0
  158. package/knowledge/decisions/internals/the-actor-prompt-says-json-because-of-json-object-mode.md +22 -0
  159. package/knowledge/decisions/internals/the-agent-done-event-goes-through-the-middleware-and-is-awaited.md +26 -0
  160. package/knowledge/decisions/internals/the-api-report-pins-members-not-just-names.md +43 -0
  161. package/knowledge/decisions/internals/the-ecosystem-entry-point-carries-the-adapter-surface.md +58 -0
  162. package/knowledge/decisions/internals/the-middleware-resolution-cache-is-deliberately-unbounded.md +40 -0
  163. package/knowledge/decisions/internals/the-per-invocation-rpc-view-is-a-class.md +40 -0
  164. package/knowledge/decisions/internals/the-persona-runtime-is-exported-from-the-persona-entry-point.md +28 -0
  165. package/knowledge/decisions/internals/the-transcript-event-is-sent-ahead-of-the-run.md +25 -0
  166. package/knowledge/decisions/internals/the-virtual-user-catalogue-is-the-only-gate-on-what-may-be-called.md +21 -0
  167. package/knowledge/decisions/internals/the-worker-disposition-is-the-one-that-is-not-testing.md +22 -0
  168. package/knowledge/decisions/internals/thread-history-records-the-transcript-not-the-audio.md +25 -0
  169. package/knowledge/decisions/internals/virtual-user-step-order-comes-from-insertion-order.md +21 -0
  170. package/knowledge/decisions/internals/voice-output-speaks-unless-voice-input-explicitly-says-otherwise.md +24 -0
  171. package/knowledge/decisions/internals/wiring-registries-erase-the-generics-their-wire-functions-capture.md +35 -0
  172. package/knowledge/decisions/security/a-dropped-audit-write-is-always-logged.md +4 -2
  173. package/knowledge/decisions/security/a-graph-run-starts-at-an-entry-node-the-graph-declared.md +33 -0
  174. package/knowledge/decisions/security/a-permission-gets-a-wire-it-cannot-reply-on.md +33 -0
  175. package/knowledge/decisions/security/a-step-runs-the-function-the-workflow-dispatched-it-with.md +37 -0
  176. package/knowledge/decisions/security/a-virtual-user-is-never-offered-a-step-that-would-forge-its-own-oracle.md +28 -0
  177. package/knowledge/decisions/security/a-workflow-run-is-read-and-approved-by-its-owner.md +34 -0
  178. package/knowledge/decisions/security/an-agent-approval-is-claimed-before-the-tool-runs.md +33 -0
  179. package/knowledge/decisions/security/an-upload-is-counted-as-it-arrives-not-buffered-then-measured.md +24 -0
  180. package/knowledge/decisions/security/gateway-handlers-run-through-the-function-runner-gate.md +13 -3
  181. package/knowledge/decisions/security/index.md +7 -0
  182. package/knowledge/questions/channel-middleware-accepts-bare-factories-that-nothing-resolves.md +35 -0
  183. package/knowledge/questions/index.md +2 -1
  184. package/knowledge/questions/unauthorized-channel-replies-escape-the-declared-out-type.md +44 -0
  185. package/package.json +14 -3
  186. package/scripts/generate-api-report.d.mts +1 -0
  187. package/scripts/generate-api-report.mjs +89 -0
  188. package/scripts/generate-api-report.mts +218 -0
  189. package/src/api-report.test.ts +69 -0
  190. package/src/column-form.ts +1 -4
  191. package/src/crypto-utils.test.ts +15 -2
  192. package/src/dev/hot-reload.ts +2 -7
  193. package/src/ecosystem.ts +40 -0
  194. package/src/errors/error-handler.ts +5 -3
  195. package/src/errors/error.test.ts +4 -1
  196. package/src/function/function-runner.test.ts +1 -1
  197. package/src/function/function-runner.ts +28 -39
  198. package/src/function/functions.types.ts +15 -9
  199. package/src/handle-error.ts +1 -1
  200. package/src/index.ts +3 -22
  201. package/src/middleware/auth-cookie.test.ts +50 -0
  202. package/src/middleware/auth-cookie.ts +38 -2
  203. package/src/middleware/cors.ts +2 -1
  204. package/src/middleware/index.ts +1 -1
  205. package/src/middleware/remote-auth.test.ts +43 -0
  206. package/src/middleware/remote-auth.ts +17 -3
  207. package/src/middleware-runner.ts +4 -2
  208. package/src/no-any-casts.test.ts +57 -0
  209. package/src/permissions.test.ts +3 -1
  210. package/src/permissions.ts +22 -24
  211. package/src/pikku-state.ts +17 -6
  212. package/src/public-surface.json +578 -0
  213. package/src/public-surface.json.README +25 -0
  214. package/src/public-surface.test.ts +105 -0
  215. package/src/removed-legacy-exports.test.ts +62 -0
  216. package/src/schema.test.ts +78 -0
  217. package/src/schema.ts +36 -1
  218. package/src/services/ai-run-state-service.ts +7 -1
  219. package/src/services/audit-service.ts +2 -2
  220. package/src/services/in-memory-ai-run-state-service.ts +3 -2
  221. package/src/services/in-memory-workflow-service.ts +25 -0
  222. package/src/services/index.ts +1 -4
  223. package/src/services/local-content-request-handler.test.ts +43 -5
  224. package/src/services/local-content-request-handler.ts +103 -74
  225. package/src/services/local-content.ts +15 -1
  226. package/src/services/local-email-service.ts +5 -1
  227. package/src/services/system-role-guard.test.ts +4 -1
  228. package/src/services/workflow-service.ts +9 -2
  229. package/src/side-effects-are-declared.test.ts +84 -0
  230. package/src/source-files-stay-composable.test.ts +41 -0
  231. package/src/testing/service-tests/agent-run-service-tests.ts +98 -0
  232. package/src/testing/service-tests/ai-storage-service-tests.ts +286 -0
  233. package/src/testing/service-tests/channel-store-tests.ts +98 -0
  234. package/src/testing/service-tests/credential-service-tests.ts +143 -0
  235. package/src/testing/service-tests/deployment-service-tests.ts +33 -0
  236. package/src/testing/service-tests/event-hub-store-tests.ts +48 -0
  237. package/src/testing/service-tests/secret-service-tests.ts +105 -0
  238. package/src/testing/service-tests/session-store-tests.ts +62 -0
  239. package/src/testing/service-tests/workflow-run-service-tests.ts +59 -0
  240. package/src/testing/service-tests/workflow-service-tests.ts +308 -0
  241. package/src/testing/service-tests.ts +31 -1111
  242. package/src/types/core.types.ts +15 -1
  243. package/src/types/state.types.ts +1 -1
  244. package/src/utils/node-host-resolver.ts +20 -0
  245. package/src/utils/safe-fetch.test.ts +143 -1
  246. package/src/utils/safe-fetch.ts +200 -25
  247. package/src/wirings/actor-flow/run-conversation.ts +1 -6
  248. package/src/wirings/ai-agent/agent-rpc.ts +120 -0
  249. package/src/wirings/ai-agent/ai-agent-agui.ts +1 -5
  250. package/src/wirings/ai-agent/ai-agent-interrupt.test.ts +2 -1
  251. package/src/wirings/ai-agent/ai-agent-memory.ts +8 -1
  252. package/src/wirings/ai-agent/ai-agent-prepare.ts +10 -12
  253. package/src/wirings/ai-agent/ai-agent-resume-authorization.test.ts +1 -0
  254. package/src/wirings/ai-agent/ai-agent-runner.test.ts +82 -2
  255. package/src/wirings/ai-agent/ai-agent-runner.ts +49 -120
  256. package/src/wirings/ai-agent/ai-agent-stream.test.ts +2 -0
  257. package/src/wirings/ai-agent/ai-agent-stream.ts +36 -63
  258. package/src/wirings/ai-agent/ai-agent-thread-ownership.test.ts +1 -1
  259. package/src/wirings/ai-agent/ai-agent-turn.ts +121 -0
  260. package/src/wirings/ai-agent/ai-agent.types.ts +1 -1
  261. package/src/wirings/ai-agent/voice-input.ts +1 -6
  262. package/src/wirings/ai-agent/voice-output.ts +2 -12
  263. package/src/wirings/channel/channel-common.ts +5 -2
  264. package/src/wirings/channel/channel-handler-shapes.test.ts +72 -0
  265. package/src/wirings/channel/channel-handler.ts +7 -7
  266. package/src/wirings/channel/channel-rpc-service.ts +0 -14
  267. package/src/wirings/channel/channel-rpc.test.ts +25 -4
  268. package/src/wirings/channel/channel-rpc.types.ts +14 -0
  269. package/src/wirings/channel/channel-runner.ts +33 -20
  270. package/src/wirings/channel/channel.types.ts +2 -0
  271. package/src/wirings/channel/local/local-channel-runner.ts +1 -1
  272. package/src/wirings/channel/pikku-abstract-channel-handler.ts +2 -1
  273. package/src/wirings/channel/serverless/serverless-channel-runner.ts +6 -3
  274. package/src/wirings/cli/channel/cli-channel-runner.ts +3 -1
  275. package/src/wirings/cli/channel/cli-raw-client-runner.ts +4 -1
  276. package/src/wirings/cli/cli-runner.ts +7 -4
  277. package/src/wirings/cli/cli.types.ts +0 -39
  278. package/src/wirings/cli/command-parser.test.ts +19 -0
  279. package/src/wirings/cli/command-parser.ts +17 -1
  280. package/src/wirings/gateway/gateway-authorization.test.ts +131 -0
  281. package/src/wirings/gateway/gateway-channel-meta.test.ts +44 -0
  282. package/src/wirings/gateway/gateway-runner.ts +62 -22
  283. package/src/wirings/http/http-routes.ts +4 -1
  284. package/src/wirings/http/http-runner.ts +11 -26
  285. package/src/wirings/http/http.types.ts +0 -15
  286. package/src/wirings/http/index.ts +1 -7
  287. package/src/wirings/http/pikku-fetch-http-request.ts +2 -2
  288. package/src/wirings/http/web-request.ts +1 -1
  289. package/src/wirings/mcp/mcp-runner.ts +2 -16
  290. package/src/wirings/persona/persona-environments.test.ts +14 -3
  291. package/src/wirings/persona/persona.test.ts +13 -3
  292. package/src/wirings/persona/validate-personas.ts +5 -1
  293. package/src/wirings/queue/queue-runner.ts +1 -1
  294. package/src/wirings/rpc/addon-auth-tags.test.ts +1 -5
  295. package/src/wirings/rpc/rpc-runner.ts +58 -136
  296. package/src/wirings/rpc/rpc-types.ts +7 -0
  297. package/src/wirings/rpc/wire-addon.ts +3 -1
  298. package/src/wirings/secret/validate-secret-definitions.test.ts +69 -0
  299. package/src/wirings/secret/validate-secret-definitions.ts +2 -0
  300. package/src/wirings/trigger/pikku-trigger-service.ts +0 -5
  301. package/src/wirings/trigger/trigger-runner.ts +7 -5
  302. package/src/wirings/virtual-user/run-virtual-user.test.ts +18 -9
  303. package/src/wirings/virtual-user/run-virtual-user.ts +28 -15
  304. package/src/wirings/virtual-user/virtual-user-agents.test.ts +4 -1
  305. package/src/wirings/virtual-user/virtual-user-derive.ts +4 -25
  306. package/src/wirings/virtual-user/virtual-user-dispositions.test.ts +7 -2
  307. package/src/wirings/virtual-user/virtual-user-dispositions.ts +1 -4
  308. package/src/wirings/virtual-user/virtual-user-intents.test.ts +13 -3
  309. package/src/wirings/workflow/dsl/workflow-dsl.types.ts +35 -2
  310. package/src/wirings/workflow/feature.ts +7 -4
  311. package/src/wirings/workflow/graph/graph-node.ts +1 -1
  312. package/src/wirings/workflow/graph/graph-runner.test.ts +4 -2
  313. package/src/wirings/workflow/graph/graph-runner.ts +13 -16
  314. package/src/wirings/workflow/graph/workflow-graph.types.ts +0 -5
  315. package/src/wirings/workflow/index.ts +12 -4
  316. package/src/wirings/workflow/pikku-scenario-service.ts +54 -25
  317. package/src/wirings/workflow/pikku-workflow-service.test.ts +2 -2
  318. package/src/wirings/workflow/pikku-workflow-service.ts +208 -630
  319. package/src/wirings/workflow/scenario-hooks.test.ts +51 -0
  320. package/src/wirings/workflow/workflow-approval.ts +185 -0
  321. package/src/wirings/workflow/workflow-child-run-session.test.ts +89 -0
  322. package/src/wirings/workflow/workflow-constants.ts +47 -0
  323. package/src/wirings/workflow/workflow-dispatch-durability.test.ts +1 -1
  324. package/src/wirings/workflow/workflow-dispatch-relay.test.ts +128 -0
  325. package/src/wirings/workflow/workflow-errors.ts +128 -0
  326. package/src/wirings/workflow/workflow-inline-authority.test.ts +12 -6
  327. package/src/wirings/workflow/workflow-meta-resolver.ts +45 -0
  328. package/src/wirings/workflow/workflow-missing-meta.test.ts +92 -0
  329. package/src/wirings/workflow/workflow-queue-routing.ts +85 -0
  330. package/src/wirings/workflow/workflow-queue-wiring.ts +149 -0
  331. package/src/wirings/workflow/workflow-recovery.ts +162 -0
  332. package/src/wirings/workflow/workflow-retry-policy.test.ts +1 -1
  333. package/src/wirings/workflow/workflow-run-authority.test.ts +215 -0
  334. package/src/wirings/workflow/workflow-run-engine.types.ts +91 -0
  335. package/src/wirings/workflow/workflow-run-ownership.ts +36 -0
  336. package/src/wirings/workflow/workflow-suspend.ts +61 -0
  337. package/src/wirings/workflow/workflow.types.ts +7 -0
  338. package/src/wirings-stay-decoupled.test.ts +123 -0
  339. package/tsconfig.json +1 -1
  340. package/tsconfig.tsbuildinfo +1 -1
  341. package/src/internal.ts +0 -10
@@ -4,6 +4,12 @@ import { pikkuState } from '../../pikku-state.js';
4
4
  import { PikkuError, addError } from '../../errors/error-handler.js';
5
5
  import { parseVersionedId } from '../../version.js';
6
6
  import { resolveRemoteAddonToken } from './remote-addon-auth.js';
7
+ import { createAgentRPC } from '../ai-agent/agent-rpc.js';
8
+ /**
9
+ * The session for a wire: read through `getSession` when a runner attached one,
10
+ * otherwise whatever was placed on the wire directly.
11
+ */
12
+ const resolveWireSession = async (wire) => typeof wire.getSession === 'function' ? await wire.getSession() : wire.session;
7
13
  export class RPCNotFoundError extends PikkuError {
8
14
  rpcName;
9
15
  constructor(rpcName) {
@@ -36,9 +42,6 @@ addError(RemoteAddonRequestError, {
36
42
  status: 502,
37
43
  message: 'Remote addon request failed.',
38
44
  });
39
- import { runAIAgent, resumeAIAgentSync } from '../ai-agent/ai-agent-runner.js';
40
- import { streamAIAgent, resumeAIAgent, interruptAIAgent, } from '../ai-agent/ai-agent-stream.js';
41
- import { wrapChannelWithAGUI } from '../ai-agent/ai-agent-agui.js';
42
45
  export const resolveNamespace = (namespacedFunction) => {
43
46
  const colonIndex = namespacedFunction.indexOf(':');
44
47
  if (colonIndex === -1) {
@@ -142,9 +145,7 @@ export class ContextAwareRPCService {
142
145
  catch (e) {
143
146
  if (e instanceof RPCNotFoundError) {
144
147
  if (this.services.deploymentService) {
145
- const session = this.wire.getSession && typeof this.wire.getSession === 'function'
146
- ? await this.wire.getSession()
147
- : this.wire.session;
148
+ const session = await resolveWireSession(this.wire);
148
149
  return this.services.deploymentService.invoke(funcName, data, session, this.wire.traceId);
149
150
  }
150
151
  }
@@ -276,9 +277,7 @@ export class ContextAwareRPCService {
276
277
  }
277
278
  catch (e) {
278
279
  if (e instanceof RPCNotFoundError && this.services.deploymentService) {
279
- const session = this.wire.getSession && typeof this.wire.getSession === 'function'
280
- ? await this.wire.getSession()
281
- : this.wire.session;
280
+ const session = await resolveWireSession(this.wire);
282
281
  return this.services.deploymentService.invoke(rpcName, data, session, this.wire.traceId);
283
282
  }
284
283
  throw e;
@@ -308,78 +307,22 @@ export class ContextAwareRPCService {
308
307
  };
309
308
  return this.services.workflowService.startWorkflow(workflowName, input, wire, this, options);
310
309
  }
310
+ /**
311
+ * The agent facade, built on access.
312
+ *
313
+ * The implementation lives in `ai-agent/agent-rpc.ts` so the agent surface is
314
+ * one file rather than a wing of this one; a getter rather than a field so a
315
+ * request that never touches an agent never builds it.
316
+ */
311
317
  get agent() {
312
- return {
313
- run: async (agentName, input) => {
314
- const result = await runAIAgent(agentName, input, {
315
- sessionService: this.options.sessionService,
316
- getCredential: this.wire.getCredential?.bind(this.wire),
317
- });
318
- return {
319
- runId: result.runId,
320
- result: result.object ?? result.text,
321
- usage: result.usage,
322
- ...(result.status === 'suspended' && {
323
- status: 'suspended',
324
- pendingApprovals: result.pendingApprovals,
325
- }),
326
- };
327
- },
328
- stream: async (agentName, input, options) => {
329
- const channel = this.wire.channel;
330
- if (!channel)
331
- throw new Error('No channel available for streaming');
332
- let currentRunId;
333
- await streamAIAgent(agentName, input, wrapChannelWithAGUI(channel, {
334
- threadId: input.threadId,
335
- getRunId: () => currentRunId,
336
- }), {
337
- sessionService: this.options.sessionService,
338
- getCredential: this.wire.getCredential?.bind(this.wire),
339
- }, undefined, {
340
- ...options,
341
- onRunCreated: (runId) => {
342
- currentRunId = runId;
343
- options?.onRunCreated?.(runId);
344
- },
345
- });
346
- },
347
- resume: async (runId, input, options) => {
348
- const channel = this.wire.channel;
349
- if (!channel)
350
- throw new Error('No channel available for streaming');
351
- await resumeAIAgent({ runId, ...input }, wrapChannelWithAGUI(channel, { runId }), {
352
- sessionService: this.options.sessionService,
353
- getCredential: this.wire.getCredential?.bind(this.wire),
354
- }, options);
355
- },
356
- interrupt: async (runId, reason) => {
357
- return interruptAIAgent({ runId, ...(reason ? { reason } : {}) }, { sessionService: this.options.sessionService });
358
- },
359
- approve: async (runId, approvals, expectedAgentName) => {
360
- const result = await resumeAIAgentSync(runId, approvals, {
361
- sessionService: this.options.sessionService,
362
- }, expectedAgentName);
363
- return {
364
- runId: result.runId,
365
- result: result.object ?? result.text,
366
- usage: result.usage,
367
- ...(result.status === 'suspended' && {
368
- status: 'suspended',
369
- pendingApprovals: result.pendingApprovals,
370
- }),
371
- };
372
- },
373
- };
318
+ return createAgentRPC(this.wire, this.options);
374
319
  }
375
320
  async remote(funcName, data) {
376
321
  if (!this.services.deploymentService) {
377
322
  throw new Error(`No DeploymentService configured for remote RPC: ${funcName}. ` +
378
323
  `Set up a DeploymentService to enable remote function calls.`);
379
324
  }
380
- const session = this.wire.getSession && typeof this.wire.getSession === 'function'
381
- ? await this.wire.getSession()
382
- : this.wire.session;
325
+ const session = await resolveWireSession(this.wire);
383
326
  return this.services.deploymentService.invoke(funcName, data, session, this.wire.traceId);
384
327
  }
385
328
  }
@@ -390,18 +333,42 @@ export class PikkuRPCService {
390
333
  ? requiresAuthOrOptions
391
334
  : { requiresAuth: requiresAuthOrOptions };
392
335
  const serviceRPC = new ContextAwareRPCService(services, wire, options, packageName);
393
- return {
394
- depth,
395
- global: false,
396
- invoke: serviceRPC.rpc.bind(serviceRPC),
397
- remote: serviceRPC.remote.bind(serviceRPC),
398
- exposed: serviceRPC.rpcExposed.bind(serviceRPC),
399
- startWorkflow: serviceRPC.startWorkflow.bind(serviceRPC),
400
- get agent() {
401
- return serviceRPC.agent;
402
- },
403
- rpcWithWire: serviceRPC.rpcWithWire.bind(serviceRPC),
404
- };
336
+ // `TypedRPC` is named by the caller from its generated RPC map. Nothing
337
+ // concrete can satisfy a type the caller has not chosen yet.
338
+ return new ContextRPCView(serviceRPC, depth);
339
+ }
340
+ }
341
+ /**
342
+ * The per-invocation `wire.rpc`.
343
+ *
344
+ * A class rather than an object literal because `agent` has to stay lazy — most
345
+ * requests never touch it — and an accessor declared on an object literal is
346
+ * defined per instance, which drops the literal off V8's fast construction
347
+ * path. Measured at ~1.15µs per request as a literal against ~0.47µs here, on
348
+ * the hot path every request takes.
349
+ *
350
+ * knowledge: decisions/internals/the-per-invocation-rpc-view-is-a-class.md
351
+ */
352
+ class ContextRPCView {
353
+ serviceRPC;
354
+ depth;
355
+ global = false;
356
+ invoke;
357
+ remote;
358
+ exposed;
359
+ startWorkflow;
360
+ rpcWithWire;
361
+ constructor(serviceRPC, depth) {
362
+ this.serviceRPC = serviceRPC;
363
+ this.depth = depth;
364
+ this.invoke = serviceRPC.rpc.bind(serviceRPC);
365
+ this.remote = serviceRPC.remote.bind(serviceRPC);
366
+ this.exposed = serviceRPC.rpcExposed.bind(serviceRPC);
367
+ this.startWorkflow = serviceRPC.startWorkflow.bind(serviceRPC);
368
+ this.rpcWithWire = serviceRPC.rpcWithWire.bind(serviceRPC);
369
+ }
370
+ get agent() {
371
+ return this.serviceRPC.agent;
405
372
  }
406
373
  }
407
374
  export const rpcService = new PikkuRPCService();
@@ -1,3 +1,4 @@
1
+ import type { PikkuRawWire } from '../../types/core.types.js';
1
2
  import type { AgentInterruptResult } from '../ai-agent/ai-agent-interrupt.js';
2
3
  export type PikkuRPC<Invoke extends (...args: any[]) => any = (...args: any[]) => any, Remote extends (...args: any[]) => any = (...args: any[]) => any, startWorkflow extends (...args: any[]) => any = (...args: any[]) => any, AgentRun extends (...args: any[]) => any = (...args: any[]) => any, AgentStream extends (...args: any[]) => any = (...args: any[]) => any> = {
3
4
  depth: number;
@@ -5,6 +6,8 @@ export type PikkuRPC<Invoke extends (...args: any[]) => any = (...args: any[]) =
5
6
  invoke: Invoke;
6
7
  remote: Remote;
7
8
  exposed: (name: string, data: any) => Promise<any>;
9
+ /** Invoke an RPC with explicit wire fields merged over the caller's. */
10
+ rpcWithWire: <In = any, Out = any>(rpcName: string, data: In, wire: PikkuRawWire) => Promise<Out>;
8
11
  startWorkflow: startWorkflow;
9
12
  agent: {
10
13
  run: AgentRun;
@@ -34,6 +34,7 @@ export function validateAndBuildSecretDefinitionsMeta(definitions, schemaLookup)
34
34
  schema: def.schema,
35
35
  oauth2: def.oauth2,
36
36
  rotationPeriod: def.rotationPeriod,
37
+ allowedHosts: def.allowedHosts,
37
38
  docsUrl: def.docsUrl,
38
39
  sourceFile: def.sourceFile,
39
40
  };
@@ -50,6 +51,7 @@ export function validateAndBuildSecretDefinitionsMeta(definitions, schemaLookup)
50
51
  schema: def.schema,
51
52
  oauth2: def.oauth2,
52
53
  rotationPeriod: def.rotationPeriod,
54
+ allowedHosts: def.allowedHosts,
53
55
  docsUrl: def.docsUrl,
54
56
  sourceFile: def.sourceFile,
55
57
  };
@@ -5,10 +5,6 @@ export type TriggerTarget = {
5
5
  targetName: string;
6
6
  startNode?: string;
7
7
  };
8
- export type TriggerSourceInfo = {
9
- name: string;
10
- input: unknown;
11
- };
12
8
  export declare abstract class PikkuTriggerService implements TriggerService {
13
9
  protected activeTriggers: Map<string, TriggerInstance>;
14
10
  abstract start(): Promise<void>;
@@ -24,6 +24,7 @@ export const wireTriggerSource = (source) => {
24
24
  if (triggerSources.has(source.name)) {
25
25
  throw new Error(`Trigger source already exists: ${source.name}`);
26
26
  }
27
+ // knowledge: decisions/internals/wiring-registries-erase-the-generics-their-wire-functions-capture.md
27
28
  triggerSources.set(source.name, source);
28
29
  addFunction(triggerSourceMeta.pikkuFuncId, source.func, triggerSourceMeta.packageName);
29
30
  };
@@ -97,7 +97,9 @@ export const rememberIds = (body, into, depth = 0) => {
97
97
  return into;
98
98
  }
99
99
  for (const [key, value] of Object.entries(body)) {
100
- if (typeof value === 'string' && MEMORABLE.test(key) && value.length < 200) {
100
+ if (typeof value === 'string' &&
101
+ MEMORABLE.test(key) &&
102
+ value.length < 200) {
101
103
  into[key] = value;
102
104
  }
103
105
  else if (typeof value === 'object') {
@@ -203,7 +205,8 @@ export const runVirtualUser = async (params) => {
203
205
  stoppedBy = 'budget-duration';
204
206
  break;
205
207
  }
206
- if (budget?.mutations !== undefined && tally.mutations >= budget.mutations) {
208
+ if (budget?.mutations !== undefined &&
209
+ tally.mutations >= budget.mutations) {
207
210
  stoppedBy = 'budget-mutations';
208
211
  break;
209
212
  }
@@ -243,10 +246,7 @@ export const runVirtualUser = async (params) => {
243
246
  if (tick.move === 'resume' || tick.move === 'suspend') {
244
247
  messages.push(msg('user', `You were pulled away and have come back to this. Things may have changed since.`));
245
248
  }
246
- // Whether this user trusts its notes this turn or goes and looks again.
247
- // `stale` almost always trusts them, a newcomer has none to trust, and an
248
- // auditor checks nearly everything — which is the whole difference between
249
- // those runs, expressed as one roll rather than as prose.
249
+ // knowledge: decisions/internals/a-virtual-user-decides-whether-to-trust-memory-once-per-turn.md
250
250
  if (Object.keys(memory).length && !rng.chance(profile.reReadRate)) {
251
251
  messages.push(msg('user', `Things you have noted down: ${Object.entries(memory)
252
252
  .slice(-25)
@@ -365,10 +365,7 @@ export const runVirtualUser = async (params) => {
365
365
  steps.push(record);
366
366
  continue;
367
367
  }
368
- // What may be called is decided once, when the catalogue is narrowed: a
369
- // read-only disposition is never offered a mutation and an approval-gated
370
- // endpoint is never offered at all, so `index` is the gate and there is no
371
- // second one here to drift out of step with it.
368
+ // knowledge: decisions/internals/the-virtual-user-catalogue-is-the-only-gate-on-what-may-be-called.md
372
369
  const mutating = !isReadOnly(entry);
373
370
  const args = readArgs(action.args);
374
371
  const repeats = profile.repeatRate > 0 && rng.chance(profile.repeatRate) ? 2 : 1;
@@ -379,7 +376,10 @@ export const runVirtualUser = async (params) => {
379
376
  response = await target.call(entry.name, args);
380
377
  }
381
378
  catch (error) {
382
- record.findingKinds = [...(record.findingKinds ?? []), 'transport-error'];
379
+ record.findingKinds = [
380
+ ...(record.findingKinds ?? []),
381
+ 'transport-error',
382
+ ];
383
383
  addFinding({
384
384
  kind: 'transport-error',
385
385
  detail: `${entry.name} threw: ${error.message}`,
@@ -18,24 +18,10 @@ const topLevelKeys = (schema) => {
18
18
  export const deriveCatalogue = (functions, schemas = {}) => {
19
19
  const entries = [];
20
20
  for (const [name, meta] of Object.entries(functions ?? {})) {
21
- // Scenario bodies and their steps are never network-callable — they are
22
- // held out of every deployed unit — so offering one would only ever waste
23
- // a turn on a 404.
24
- //
25
- // For a platform or addon step this is not an efficiency argument but the
26
- // oracle: a virtual user's findings are worth something only because it
27
- // cannot manufacture the outcomes it is meant to discover. One that could
28
- // invoke "Stripe's webhook arrives" would forge its own payment success,
29
- // and every finding downstream of that is worthless. Same class of argument
30
- // as `allowApprovalRequired` defaulting to false — and enforced here, at
31
- // derivation, rather than by convention.
21
+ // knowledge: decisions/security/a-virtual-user-is-never-offered-a-step-that-would-forge-its-own-oracle.md
32
22
  if (meta.scenario || meta.scenarioStep || meta.scenarioStepKind)
33
23
  continue;
34
- // `expose: true` is what puts a function on the rpc transport, and that is
35
- // what the shipped target calls over. Absent is not permissive: an unexposed
36
- // function 404s, and offering one spends a step to learn nothing about the
37
- // product. Measured on the e2e app, 34 of its 72 functions are in exactly
38
- // that state — nearly half a catalogue that cannot be called.
24
+ // knowledge: decisions/internals/only-exposed-functions-enter-a-virtual-user-catalogue.md
39
25
  if (meta.expose !== true)
40
26
  continue;
41
27
  const inputSchema = meta.inputSchemaName
@@ -76,10 +62,7 @@ const scenarioSteps = (meta) => {
76
62
  ? [{ stepFunc: step.stepFunc, phase: step.phase, actor: step.actor }]
77
63
  : []);
78
64
  }
79
- // Insertion order is the order the CLI wrote the nodes in, which is the order
80
- // the scenario declares them. Following `next` instead would buy an ordering
81
- // that is already true and cost a traversal that has to decide what a branch
82
- // means to a reader who will never take one.
65
+ // knowledge: decisions/internals/virtual-user-step-order-comes-from-insertion-order.md
83
66
  return Object.values(meta.nodes ?? {}).flatMap((node) => {
84
67
  const { rpcName, scenarioStepPhase, actor } = (node ?? {});
85
68
  if (!rpcName || !scenarioStepPhase)
@@ -115,11 +98,7 @@ export const deriveIntents = (workflows, functions = {}) => {
115
98
  continue;
116
99
  steps.push(composeStepProse({
117
100
  phase: step.phase,
118
- // The template goes in unfilled, braces and all. A reporter fills its
119
- // placeholders from a run that happened; there is no run yet, and
120
- // "invites {email}" tells a user to pick someone where the filled-in
121
- // form would have told it whom — which is the scenario's answer, not
122
- // the user's own.
101
+ // knowledge: decisions/internals/a-scenario-step-template-is-offered-unfilled.md
123
102
  description: template ?? description ?? '',
124
103
  actor: step.actor,
125
104
  }));
@@ -78,10 +78,7 @@ export const DISPOSITIONS = {
78
78
  reReadRate: 0.2,
79
79
  invertedOracle: true,
80
80
  },
81
- // The only disposition that is not testing anything. It is doing the job, so
82
- // it abandons rarely, runs cool, and is told to stop and say so rather than
83
- // guess — the failure mode here is a wrong action nobody asked for, not a
84
- // missed bug.
81
+ // knowledge: decisions/internals/the-worker-disposition-is-the-one-that-is-not-testing.md
85
82
  accountable: {
86
83
  ...REALISTIC,
87
84
  instructions: [
@@ -288,7 +288,7 @@ export interface ReturnStepMeta {
288
288
  spread?: string[];
289
289
  }
290
290
  /**
291
- * Inline step metadata (legacy support)
291
+ * Inline step metadata
292
292
  */
293
293
  export interface InlineStepMeta {
294
294
  /** Inline form - local execution */
@@ -473,7 +473,34 @@ export interface PikkuWorkflowWire {
473
473
  /** Suspend workflow until a human records a decision against this gate */
474
474
  approval: WorkflowWireApproval;
475
475
  }
476
- export interface PikkuScenarioWire extends PikkuWorkflowWire {
476
+ /**
477
+ * What a scenario has accumulated so far, shared by its body and its hooks.
478
+ *
479
+ * Typed as a partial of the scenario's own output because a scenario can fail
480
+ * at any step: the shape describes what a *completed* run produces, and the
481
+ * context holds however much of it this run reached. A scenario declaring no
482
+ * object output gets an open record rather than `Partial<never>`.
483
+ *
484
+ * `any` collapses to `any`, not to the union of both branches. `PikkuWire`'s
485
+ * default depends on that: the wire type is also used as a generic *constraint*,
486
+ * and a constraint carrying a concrete context would reject every wire whose
487
+ * scenario output differs from it.
488
+ */
489
+ export type ScenarioContext<Out = unknown> = 0 extends 1 & Out ? any : Out extends object ? Partial<Out> : Record<string, unknown>;
490
+ export interface PikkuScenarioWire<Out = unknown> extends PikkuWorkflowWire {
491
+ /**
492
+ * Scratch the body writes and the `before`/`after` hooks read.
493
+ *
494
+ * A hook is a separate function, so it cannot see the body's locals — which
495
+ * is why teardown had nowhere to learn the ids the body minted. Assigning
496
+ * them here (`scenario.context.projectId = project.projectId`) hands them to
497
+ * `after`, which runs in a `finally` and so cleans up on a failed run too.
498
+ *
499
+ * Deliberately *not* a world: it is scoped to one run, and scenario steps
500
+ * cannot reach it. Steps stay pure functions of their declared inputs, so the
501
+ * ladder in the report remains a complete account of what a step saw.
502
+ */
503
+ context: ScenarioContext<Out>;
477
504
  /**
478
505
  * Durable polling step: invoke `rpcName` (as an actor when `options.actor`
479
506
  * is set) until `predicate` passes or `options.within` elapses.
@@ -13,8 +13,10 @@ export const resolveFeatureScenarios = (features, registrations) => {
13
13
  const scenarios = feature.scenarios ?? [];
14
14
  for (let index = 0; index < scenarios.length; index++) {
15
15
  const entry = scenarios[index];
16
- const paired = typeof entry === 'object' && entry !== null && 'scenario' in entry;
17
- const config = paired ? entry.scenario : entry;
16
+ const pairing = typeof entry === 'object' && entry !== null && 'scenario' in entry
17
+ ? entry
18
+ : undefined;
19
+ const config = pairing ? pairing.scenario : entry;
18
20
  const scenarioName = nameByConfig.get(config);
19
21
  if (!scenarioName) {
20
22
  unresolved.push({ featureId, index });
@@ -24,10 +26,11 @@ export const resolveFeatureScenarios = (features, registrations) => {
24
26
  featureId,
25
27
  featureName: feature.name ?? featureId,
26
28
  scenarioName,
27
- data: paired ? entry.data : undefined,
29
+ data: pairing?.data,
28
30
  tags: [
29
31
  ...new Set([
30
- ...(registrations.get(scenarioName)?.func?.tags ?? []),
32
+ ...(registrations.get(scenarioName)?.func
33
+ ?.tags ?? []),
31
34
  ...(feature.tags ?? []),
32
35
  ]),
33
36
  ],
@@ -1,4 +1,4 @@
1
- import { type PikkuWorkflowService } from '../pikku-workflow-service.js';
1
+ import type { PikkuWorkflowService } from '../pikku-workflow-service.js';
2
2
  import type { WorkflowRuntimeMeta, WorkflowRunWire } from '../workflow.types.js';
3
3
  export declare class ChildWorkflowStartedException extends Error {
4
4
  parentRunId: string;
@@ -9,7 +9,6 @@ export declare class ChildWorkflowStartedException extends Error {
9
9
  }
10
10
  export declare function continueGraph(workflowService: PikkuWorkflowService, runId: string, graphName: string, overrideMeta?: WorkflowRuntimeMeta): Promise<void>;
11
11
  export declare function executeGraphStep(workflowService: PikkuWorkflowService, rpcService: any, runId: string, stepId: string, nodeId: string, rpcName: string, data: any, graphName: string): Promise<any>;
12
- export declare function onGraphNodeComplete(workflowService: PikkuWorkflowService, runId: string, graphName: string): Promise<void>;
13
12
  export declare function runFromMeta(workflowService: PikkuWorkflowService, runId: string, meta: WorkflowRuntimeMeta, _rpcService: any): Promise<void>;
14
13
  export declare function runWorkflowGraph(workflowService: PikkuWorkflowService, graphName: string, triggerInput: any, rpcService?: any, inline?: boolean, startNode?: string, wire?: WorkflowRunWire, overrideMeta?: WorkflowRuntimeMeta): Promise<{
15
14
  runId: string;
@@ -1,4 +1,5 @@
1
- import { WorkflowAsyncException, WorkflowSuspendedException, DEFAULT_STEP_RETRIES, } from '../pikku-workflow-service.js';
1
+ import { WorkflowAsyncException, WorkflowSuspendedException, } from '../workflow-errors.js';
2
+ import { DEFAULT_STEP_RETRIES } from '../workflow-constants.js';
2
3
  import { pikkuState, getSingletonServices } from '../../../pikku-state.js';
3
4
  import { RPCNotFoundError } from '../../rpc/rpc-runner.js';
4
5
  export class ChildWorkflowStartedException extends Error {
@@ -491,9 +492,6 @@ export async function executeGraphStep(workflowService, rpcService, runId, stepI
491
492
  throw error;
492
493
  }
493
494
  }
494
- export async function onGraphNodeComplete(workflowService, runId, graphName) {
495
- await continueGraph(workflowService, runId, graphName);
496
- }
497
495
  export async function runFromMeta(workflowService, runId, meta, _rpcService) {
498
496
  await continueGraph(workflowService, runId, meta.name, meta);
499
497
  }
@@ -610,9 +608,12 @@ export async function runWorkflowGraph(workflowService, graphName, triggerInput,
610
608
  throw new Error(`Workflow graph '${graphName}' not found`);
611
609
  }
612
610
  const nodes = meta.nodes;
613
- const entryNodes = startNode
614
- ? [startNode]
615
- : (meta.entryNodeIds ?? []);
611
+ const declaredEntryNodes = meta.entryNodeIds ?? [];
612
+ // knowledge: decisions/security/a-graph-run-starts-at-an-entry-node-the-graph-declared.md
613
+ if (startNode && !declaredEntryNodes.includes(startNode)) {
614
+ throw new Error(`Workflow graph '${graphName}': '${startNode}' is not an entry node`);
615
+ }
616
+ const entryNodes = startNode ? [startNode] : declaredEntryNodes;
616
617
  validateGraphReferences(graphName, nodes, entryNodes);
617
618
  if (entryNodes.length === 0) {
618
619
  throw new Error(`Workflow graph '${graphName}': no entry nodes found in meta or startNode`);
@@ -5,10 +5,6 @@ export interface RefValue {
5
5
  }
6
6
  export declare const createRef: (nodeId: string, path?: string) => RefValue;
7
7
  export declare const isRef: (value: unknown) => value is RefValue;
8
- export interface InputRef {
9
- nodeId: string;
10
- path?: string;
11
- }
12
8
  export type RefFn<NodeIds extends string = string> = (nodeId: NodeIds, path?: string) => RefValue;
13
9
  export type NextConfig<NodeIds extends string = string> = NodeIds | NodeIds[] | Record<string, NodeIds | NodeIds[]>;
14
10
  export interface GraphNodeConfig<NodeIds extends string = string> {
@@ -1,6 +1,11 @@
1
- export { PikkuWorkflowService, WorkflowCancelledException, WorkflowSuspendedException, WorkflowDispatchException, WorkflowNotFoundError, WorkflowRunNotFoundError, WorkflowApprovalResolvedError, DEFAULT_STEP_RETRIES, } from './pikku-workflow-service.js';
2
- export type { RunLifecycleContext, WorkflowRunEngine, WorkflowRunExtension, } from './pikku-workflow-service.js';
1
+ export { PikkuWorkflowService } from './pikku-workflow-service.js';
2
+ export { WorkflowCancelledException, WorkflowSuspendedException, WorkflowDispatchException, WorkflowNotFoundError, WorkflowRunNotFoundError, WorkflowApprovalResolvedError, WorkflowStepFunctionMismatchError, } from './workflow-errors.js';
3
+ export { DEFAULT_STEP_RETRIES } from './workflow-constants.js';
4
+ export { assertWorkflowRunOwner, WorkflowRunForbiddenError, } from './workflow-run-ownership.js';
5
+ export type { RunLifecycleContext, WorkflowRunEngine, WorkflowRunExtension, } from './workflow-run-engine.types.js';
3
6
  export { deriveInvocationId, uuidv5 } from './workflow-invocation-id.js';
7
+ export { isRef } from './graph/workflow-graph.types.js';
8
+ export type { RefValue } from './graph/workflow-graph.types.js';
4
9
  export { buildRunTimeline, reconstructStateAt, reconstructFinalState, } from './run-timeline.js';
5
10
  export type { RunTimeline, RunTimelineEvent, ReconstructedRunState, ReconstructedStep, RunPhase, } from './run-timeline.js';
6
11
  export { addWorkflow } from './dsl/workflow-runner.js';
@@ -1,5 +1,9 @@
1
- export { PikkuWorkflowService, WorkflowCancelledException, WorkflowSuspendedException, WorkflowDispatchException, WorkflowNotFoundError, WorkflowRunNotFoundError, WorkflowApprovalResolvedError, DEFAULT_STEP_RETRIES, } from './pikku-workflow-service.js';
1
+ export { PikkuWorkflowService } from './pikku-workflow-service.js';
2
+ export { WorkflowCancelledException, WorkflowSuspendedException, WorkflowDispatchException, WorkflowNotFoundError, WorkflowRunNotFoundError, WorkflowApprovalResolvedError, WorkflowStepFunctionMismatchError, } from './workflow-errors.js';
3
+ export { DEFAULT_STEP_RETRIES } from './workflow-constants.js';
4
+ export { assertWorkflowRunOwner, WorkflowRunForbiddenError, } from './workflow-run-ownership.js';
2
5
  export { deriveInvocationId, uuidv5 } from './workflow-invocation-id.js';
6
+ export { isRef } from './graph/workflow-graph.types.js';
3
7
  export { buildRunTimeline, reconstructStateAt, reconstructFinalState, } from './run-timeline.js';
4
8
  export { addWorkflow } from './dsl/workflow-runner.js';
5
9
  export { addFeature, resolveFeatureScenarios } from './feature.js';
@@ -1,7 +1,7 @@
1
1
  import { PikkuError } from '../../errors/error-handler.js';
2
2
  import { InMemoryWorkflowService } from '../../services/in-memory-workflow-service.js';
3
- import { type RunLifecycleContext, type WorkflowRunEngine, type WorkflowRunExtension } from './pikku-workflow-service.js';
4
- import type { PikkuWire } from '../../types/core.types.js';
3
+ import type { RunLifecycleContext, WorkflowRunEngine, WorkflowRunExtension } from './workflow-run-engine.types.js';
4
+ import type { PikkuRawWire } from '../../types/core.types.js';
5
5
  import type { ScenarioPersonas } from '../../services/personas-service.js';
6
6
  import type { ScenarioBrowserProvider, ScenarioEnvironment, ScenarioSurface } from './scenario-step.types.js';
7
7
  import type { PikkuWorkflowWire, WorkflowQueueOptions } from './workflow.types.js';
@@ -126,6 +126,7 @@ export declare class ScenarioNoWitness extends PikkuError {
126
126
  export declare class PikkuScenarioService implements WorkflowRunExtension {
127
127
  private readonly engine;
128
128
  private runActors;
129
+ private runContexts;
129
130
  private scenarioBrowserProvider?;
130
131
  private scenarioEnvironment?;
131
132
  private runSurface;
@@ -154,7 +155,20 @@ export declare class PikkuScenarioService implements WorkflowRunExtension {
154
155
  actors?: ScenarioPersonas;
155
156
  }): Promise<void>;
156
157
  detachRunContext(runId: string): void;
157
- decorateRunWire(wire: PikkuWire, context: {
158
+ /**
159
+ * What the run has accumulated, for a reporter that wants to say what a
160
+ * failed scenario left behind. Undefined for a plain workflow.
161
+ */
162
+ getRunContext(runId: string): Record<string, unknown> | undefined;
163
+ /**
164
+ * The run's context, created on demand.
165
+ *
166
+ * `attachRunContext` seeds it for an ordinary scenario run, but the wire is
167
+ * also built on paths that skip attach; without this the body would be
168
+ * writing to an `undefined` and teardown would read nothing.
169
+ */
170
+ private contextForRun;
171
+ decorateRunWire(wire: PikkuRawWire, context: {
158
172
  runId: string;
159
173
  workflowMeta: any;
160
174
  workflowWire: PikkuWorkflowWire;