@pikku/core 0.12.79 → 0.12.82

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 (479) hide show
  1. package/CHANGELOG.md +473 -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/errors/index.d.ts +1 -1
  7. package/dist/errors/index.js +1 -1
  8. package/dist/function/function-runner.js +22 -47
  9. package/dist/function/functions.types.d.ts +9 -5
  10. package/dist/function/index.d.ts +1 -1
  11. package/dist/index.d.ts +14 -19
  12. package/dist/index.js +6 -11
  13. package/dist/middleware/auth-cookie.d.ts +0 -4
  14. package/dist/middleware/auth-cookie.js +38 -2
  15. package/dist/middleware/cors.js +1 -0
  16. package/dist/middleware/index.d.ts +1 -1
  17. package/dist/middleware/index.js +1 -1
  18. package/dist/middleware/remote-auth.js +14 -3
  19. package/dist/permissions.d.ts +8 -10
  20. package/dist/permissions.js +0 -11
  21. package/dist/pikku-state.js +16 -5
  22. package/dist/schema.js +35 -1
  23. package/dist/services/ai-agent-runner-service.d.ts +7 -0
  24. package/dist/services/ai-run-state-service.d.ts +17 -1
  25. package/dist/services/in-memory-ai-run-state-service.d.ts +6 -2
  26. package/dist/services/in-memory-ai-run-state-service.js +11 -1
  27. package/dist/services/in-memory-workflow-service.js +2 -0
  28. package/dist/services/index.d.ts +15 -15
  29. package/dist/services/index.js +5 -5
  30. package/dist/services/local-content-request-handler.d.ts +21 -0
  31. package/dist/services/local-content-request-handler.js +72 -53
  32. package/dist/services/local-content.d.ts +6 -0
  33. package/dist/services/local-content.js +14 -1
  34. package/dist/services/scoped-credential-service.d.ts +21 -0
  35. package/dist/services/scoped-credential-service.js +53 -0
  36. package/dist/services/workflow-service.d.ts +4 -2
  37. package/dist/testing/service-tests/agent-run-service-tests.d.ts +10 -0
  38. package/dist/testing/service-tests/agent-run-service-tests.js +72 -0
  39. package/dist/testing/service-tests/ai-storage-service-tests.d.ts +3 -0
  40. package/dist/testing/service-tests/ai-storage-service-tests.js +302 -0
  41. package/dist/testing/service-tests/channel-store-tests.d.ts +3 -0
  42. package/dist/testing/service-tests/channel-store-tests.js +72 -0
  43. package/dist/testing/service-tests/credential-service-tests.d.ts +3 -0
  44. package/dist/testing/service-tests/credential-service-tests.js +109 -0
  45. package/dist/testing/service-tests/deployment-service-tests.d.ts +3 -0
  46. package/dist/testing/service-tests/deployment-service-tests.js +21 -0
  47. package/dist/testing/service-tests/event-hub-store-tests.d.ts +3 -0
  48. package/dist/testing/service-tests/event-hub-store-tests.js +34 -0
  49. package/dist/testing/service-tests/secret-service-tests.d.ts +3 -0
  50. package/dist/testing/service-tests/secret-service-tests.js +80 -0
  51. package/dist/testing/service-tests/session-store-tests.d.ts +3 -0
  52. package/dist/testing/service-tests/session-store-tests.js +43 -0
  53. package/dist/testing/service-tests/workflow-run-service-tests.d.ts +3 -0
  54. package/dist/testing/service-tests/workflow-run-service-tests.js +42 -0
  55. package/dist/testing/service-tests/workflow-service-tests.d.ts +3 -0
  56. package/dist/testing/service-tests/workflow-service-tests.js +150 -0
  57. package/dist/testing/service-tests.d.ts +6 -0
  58. package/dist/testing/service-tests.js +26 -791
  59. package/dist/types/core.types.d.ts +11 -3
  60. package/dist/types/state.types.d.ts +14 -1
  61. package/dist/wirings/actor-flow/index.d.ts +1 -1
  62. package/dist/wirings/actor-flow/run-conversation.js +1 -6
  63. package/dist/wirings/ai-agent/agent-rpc.d.ts +15 -0
  64. package/dist/wirings/ai-agent/agent-rpc.js +53 -0
  65. package/dist/wirings/ai-agent/ai-agent-agui.js +1 -5
  66. package/dist/wirings/ai-agent/ai-agent-finalize.d.ts +58 -0
  67. package/dist/wirings/ai-agent/ai-agent-finalize.js +138 -0
  68. package/dist/wirings/ai-agent/ai-agent-interrupt.js +1 -0
  69. package/dist/wirings/ai-agent/ai-agent-memory.d.ts +2 -8
  70. package/dist/wirings/ai-agent/ai-agent-memory.js +37 -18
  71. package/dist/wirings/ai-agent/ai-agent-model-config.d.ts +7 -0
  72. package/dist/wirings/ai-agent/ai-agent-model-config.js +44 -1
  73. package/dist/wirings/ai-agent/ai-agent-prepare.js +5 -9
  74. package/dist/wirings/ai-agent/ai-agent-runner.js +89 -147
  75. package/dist/wirings/ai-agent/ai-agent-stream.js +109 -97
  76. package/dist/wirings/ai-agent/ai-agent-turn.d.ts +57 -0
  77. package/dist/wirings/ai-agent/ai-agent-turn.js +82 -0
  78. package/dist/wirings/ai-agent/ai-agent.types.d.ts +47 -2
  79. package/dist/wirings/ai-agent/index.d.ts +8 -7
  80. package/dist/wirings/ai-agent/index.js +5 -4
  81. package/dist/wirings/ai-agent/voice-input.js +1 -6
  82. package/dist/wirings/ai-agent/voice-output.js +2 -12
  83. package/dist/wirings/ai-scorer/ai-scorer-grade.d.ts +26 -0
  84. package/dist/wirings/ai-scorer/ai-scorer-grade.js +33 -0
  85. package/dist/wirings/ai-scorer/ai-scorer-judge.d.ts +17 -0
  86. package/dist/wirings/ai-scorer/ai-scorer-judge.js +92 -0
  87. package/dist/wirings/ai-scorer/ai-scorer-live.d.ts +15 -0
  88. package/dist/wirings/ai-scorer/ai-scorer-live.js +38 -0
  89. package/dist/wirings/ai-scorer/ai-scorer-registry.d.ts +18 -0
  90. package/dist/wirings/ai-scorer/ai-scorer-registry.js +46 -0
  91. package/dist/wirings/ai-scorer/ai-scorer-sampling.d.ts +8 -0
  92. package/dist/wirings/ai-scorer/ai-scorer-sampling.js +31 -0
  93. package/dist/wirings/ai-scorer/ai-scorer-snapshots.d.ts +10 -0
  94. package/dist/wirings/ai-scorer/ai-scorer-snapshots.js +40 -0
  95. package/dist/wirings/ai-scorer/ai-scorer-worker.d.ts +15 -0
  96. package/dist/wirings/ai-scorer/ai-scorer-worker.js +58 -0
  97. package/dist/wirings/ai-scorer/ai-scorer.d.ts +39 -0
  98. package/dist/wirings/ai-scorer/ai-scorer.js +40 -0
  99. package/dist/wirings/ai-scorer/ai-scorer.types.d.ts +90 -0
  100. package/dist/wirings/ai-scorer/ai-scorer.types.js +4 -0
  101. package/dist/wirings/ai-scorer/index.d.ts +6 -0
  102. package/dist/wirings/ai-scorer/index.js +5 -0
  103. package/dist/wirings/channel/channel-common.js +1 -0
  104. package/dist/wirings/channel/channel-handler.js +3 -5
  105. package/dist/wirings/channel/channel-rpc-service.d.ts +0 -6
  106. package/dist/wirings/channel/channel-rpc-service.js +0 -8
  107. package/dist/wirings/channel/channel-rpc.types.d.ts +6 -0
  108. package/dist/wirings/channel/channel-rpc.types.js +8 -0
  109. package/dist/wirings/channel/channel-runner.d.ts +1 -3
  110. package/dist/wirings/channel/channel-runner.js +16 -8
  111. package/dist/wirings/channel/channel.types.d.ts +2 -0
  112. package/dist/wirings/channel/index.d.ts +5 -6
  113. package/dist/wirings/channel/index.js +3 -4
  114. package/dist/wirings/channel/local/local-channel-runner.js +8 -1
  115. package/dist/wirings/channel/pikku-abstract-channel-handler.js +1 -0
  116. package/dist/wirings/channel/serverless/serverless-channel-runner.js +3 -0
  117. package/dist/wirings/cli/channel/cli-channel-runner.js +2 -0
  118. package/dist/wirings/cli/channel/cli-raw-channel-runner.js +9 -1
  119. package/dist/wirings/cli/channel/index.d.ts +1 -2
  120. package/dist/wirings/cli/channel/index.js +0 -1
  121. package/dist/wirings/cli/cli-runner.js +17 -3
  122. package/dist/wirings/cli/cli.types.d.ts +0 -8
  123. package/dist/wirings/cli/command-parser.js +13 -0
  124. package/dist/wirings/credential/index.d.ts +1 -1
  125. package/dist/wirings/gateway/gateway-runner.js +9 -2
  126. package/dist/wirings/gateway/index.d.ts +1 -1
  127. package/dist/wirings/http/http-routes.js +2 -0
  128. package/dist/wirings/http/http-runner.d.ts +0 -10
  129. package/dist/wirings/http/http-runner.js +14 -15
  130. package/dist/wirings/http/http.types.d.ts +0 -10
  131. package/dist/wirings/http/index.d.ts +2 -3
  132. package/dist/wirings/http/index.js +1 -1
  133. package/dist/wirings/mcp/index.d.ts +1 -1
  134. package/dist/wirings/mcp/mcp-runner.d.ts +15 -7
  135. package/dist/wirings/mcp/mcp-runner.js +18 -11
  136. package/dist/wirings/persona/index.d.ts +3 -4
  137. package/dist/wirings/persona/index.js +2 -3
  138. package/dist/wirings/queue/index.d.ts +1 -3
  139. package/dist/wirings/queue/index.js +1 -3
  140. package/dist/wirings/rpc/addon-runner.d.ts +4 -0
  141. package/dist/wirings/rpc/addon-runner.js +19 -3
  142. package/dist/wirings/rpc/rpc-runner.d.ts +8 -51
  143. package/dist/wirings/rpc/rpc-runner.js +55 -86
  144. package/dist/wirings/rpc/rpc-types.d.ts +7 -0
  145. package/dist/wirings/rpc/wire-addon.d.ts +13 -0
  146. package/dist/wirings/rpc/wire-addon.js +4 -0
  147. package/dist/wirings/scheduler/index.d.ts +1 -1
  148. package/dist/wirings/secret/validate-secret-definitions.js +2 -2
  149. package/dist/wirings/trigger/index.d.ts +1 -1
  150. package/dist/wirings/trigger/pikku-trigger-service.d.ts +0 -4
  151. package/dist/wirings/trigger/trigger-runner.js +1 -0
  152. package/dist/wirings/virtual-user/index.d.ts +5 -6
  153. package/dist/wirings/virtual-user/index.js +2 -4
  154. package/dist/wirings/virtual-user/run-virtual-user.js +11 -11
  155. package/dist/wirings/virtual-user/virtual-user-derive.js +4 -25
  156. package/dist/wirings/virtual-user/virtual-user-dispositions.js +1 -4
  157. package/dist/wirings/workflow/dsl/workflow-dsl.types.d.ts +100 -3
  158. package/dist/wirings/workflow/feature.js +7 -4
  159. package/dist/wirings/workflow/graph/graph-runner.d.ts +1 -2
  160. package/dist/wirings/workflow/graph/graph-runner.js +8 -7
  161. package/dist/wirings/workflow/graph/workflow-graph.types.d.ts +0 -4
  162. package/dist/wirings/workflow/index.d.ts +12 -7
  163. package/dist/wirings/workflow/index.js +7 -3
  164. package/dist/wirings/workflow/pikku-scenario-service.d.ts +17 -3
  165. package/dist/wirings/workflow/pikku-scenario-service.js +64 -10
  166. package/dist/wirings/workflow/pikku-workflow-service.d.ts +28 -147
  167. package/dist/wirings/workflow/pikku-workflow-service.js +85 -493
  168. package/dist/wirings/workflow/scenario-step.types.d.ts +8 -0
  169. package/dist/wirings/workflow/workflow-approval-audit.d.ts +16 -0
  170. package/dist/wirings/workflow/workflow-approval-audit.js +40 -0
  171. package/dist/wirings/workflow/workflow-approval-policy.d.ts +20 -0
  172. package/dist/wirings/workflow/workflow-approval-policy.js +48 -0
  173. package/dist/wirings/workflow/workflow-approval.d.ts +67 -0
  174. package/dist/wirings/workflow/workflow-approval.js +177 -0
  175. package/dist/wirings/workflow/workflow-constants.d.ts +26 -0
  176. package/dist/wirings/workflow/workflow-constants.js +35 -0
  177. package/dist/wirings/workflow/workflow-errors.d.ts +58 -0
  178. package/dist/wirings/workflow/workflow-errors.js +112 -0
  179. package/dist/wirings/workflow/workflow-meta-resolver.d.ts +11 -0
  180. package/dist/wirings/workflow/workflow-meta-resolver.js +31 -0
  181. package/dist/wirings/workflow/workflow-queue-routing.d.ts +8 -0
  182. package/dist/wirings/workflow/workflow-queue-routing.js +38 -0
  183. package/dist/wirings/workflow/workflow-queue-wiring.d.ts +20 -0
  184. package/dist/wirings/workflow/workflow-queue-wiring.js +79 -0
  185. package/dist/wirings/workflow/workflow-recovery.d.ts +68 -0
  186. package/dist/wirings/workflow/workflow-recovery.js +101 -0
  187. package/dist/wirings/workflow/workflow-run-engine.types.d.ts +54 -0
  188. package/dist/wirings/workflow/workflow-run-ownership.d.ts +17 -0
  189. package/dist/wirings/workflow/workflow-run-ownership.js +30 -0
  190. package/dist/wirings/workflow/workflow-suspend.d.ts +12 -0
  191. package/dist/wirings/workflow/workflow-suspend.js +33 -0
  192. package/dist/wirings/workflow/workflow.types.d.ts +8 -1
  193. package/knowledge/decisions/internals/a-non-streaming-agent-run-registers-with-airunstate-too.md +22 -0
  194. package/knowledge/decisions/internals/a-resumed-agent-turn-is-as-interruptible-as-the-first.md +20 -0
  195. package/knowledge/decisions/internals/a-scenario-step-template-is-offered-unfilled.md +21 -0
  196. package/knowledge/decisions/internals/a-virtual-user-decides-whether-to-trust-memory-once-per-turn.md +21 -0
  197. package/knowledge/decisions/internals/a-wall-clock-threshold-is-a-load-test-in-disguise.md +46 -0
  198. package/knowledge/decisions/internals/a-workflow-wire-is-built-from-the-run-not-from-the-rpc-service.md +45 -0
  199. package/knowledge/decisions/internals/addon-pikku-meta-ships-at-the-package-root-or-under-dist.md +32 -0
  200. package/knowledge/decisions/internals/agent-context-waits-for-a-tool-result-still-being-written.md +21 -0
  201. package/knowledge/decisions/internals/agent-speech-travels-as-a-custom-agui-event.md +22 -0
  202. package/knowledge/decisions/internals/an-addon-scope-root-loses-to-a-root-the-host-already-declares.md +39 -0
  203. package/knowledge/decisions/internals/an-agent-interrupt-is-not-a-failure.md +23 -0
  204. package/knowledge/decisions/internals/an-agent-run-owned-by-another-instance-says-so.md +25 -0
  205. package/knowledge/decisions/internals/an-agent-stream-send-must-return-the-inner-sends-promise.md +22 -0
  206. package/knowledge/decisions/internals/an-empty-text-part-is-omitted-from-an-agent-message.md +20 -0
  207. package/knowledge/decisions/internals/an-empty-transcript-is-not-recorded.md +22 -0
  208. package/knowledge/decisions/internals/an-unref-d-timer-cannot-be-awaited-under-node-test.md +66 -0
  209. package/knowledge/decisions/internals/channel-state-accessors-are-unsound-generics-that-every-implementation-asserts.md +33 -0
  210. package/knowledge/decisions/internals/gateway-listener-middleware-runs-without-an-rpc-on-the-wire.md +43 -0
  211. package/knowledge/decisions/internals/hot-reload-writes-into-the-function-map-captured-at-startup.md +26 -0
  212. package/knowledge/decisions/internals/index.md +42 -3
  213. package/knowledge/decisions/internals/only-exposed-functions-enter-a-virtual-user-catalogue.md +21 -0
  214. package/knowledge/decisions/internals/scenario-given-and-when-are-sugar-but-then-is-not.md +24 -0
  215. package/knowledge/decisions/internals/side-effects-are-an-allowlist-not-a-boolean.md +34 -0
  216. package/knowledge/decisions/internals/speech-synthesis-picks-a-voice-per-sentence-and-warns-once.md +24 -0
  217. package/knowledge/decisions/internals/the-actor-prompt-says-json-because-of-json-object-mode.md +22 -0
  218. package/knowledge/decisions/internals/the-agent-done-event-goes-through-the-middleware-and-is-awaited.md +26 -0
  219. package/knowledge/decisions/internals/the-api-report-pins-members-not-just-names.md +43 -0
  220. package/knowledge/decisions/internals/the-ecosystem-entry-point-carries-the-adapter-surface.md +58 -0
  221. package/knowledge/decisions/internals/the-middleware-resolution-cache-is-deliberately-unbounded.md +40 -0
  222. package/knowledge/decisions/internals/the-per-invocation-rpc-view-is-a-class.md +40 -0
  223. package/knowledge/decisions/internals/the-persona-runtime-is-exported-from-the-persona-entry-point.md +28 -0
  224. package/knowledge/decisions/internals/the-transcript-event-is-sent-ahead-of-the-run.md +25 -0
  225. package/knowledge/decisions/internals/the-virtual-user-catalogue-is-the-only-gate-on-what-may-be-called.md +21 -0
  226. package/knowledge/decisions/internals/the-worker-disposition-is-the-one-that-is-not-testing.md +22 -0
  227. package/knowledge/decisions/internals/thread-history-records-the-transcript-not-the-audio.md +25 -0
  228. package/knowledge/decisions/internals/validate-runs-checks-by-precondition.md +115 -0
  229. package/knowledge/decisions/internals/virtual-user-step-order-comes-from-insertion-order.md +21 -0
  230. package/knowledge/decisions/internals/voice-output-speaks-unless-voice-input-explicitly-says-otherwise.md +24 -0
  231. package/knowledge/decisions/internals/wiring-registries-erase-the-generics-their-wire-functions-capture.md +35 -0
  232. package/knowledge/decisions/security/a-dropped-audit-write-is-always-logged.md +4 -2
  233. package/knowledge/decisions/security/a-function-never-receives-the-secret-service.md +37 -0
  234. package/knowledge/decisions/security/a-graph-run-starts-at-an-entry-node-the-graph-declared.md +33 -0
  235. package/knowledge/decisions/security/a-permission-gets-a-wire-it-cannot-reply-on.md +33 -0
  236. package/knowledge/decisions/security/a-step-runs-the-function-the-workflow-dispatched-it-with.md +37 -0
  237. package/knowledge/decisions/security/a-virtual-user-is-never-offered-a-step-that-would-forge-its-own-oracle.md +28 -0
  238. package/knowledge/decisions/security/a-workflow-run-is-read-and-approved-by-its-owner.md +50 -0
  239. package/knowledge/decisions/security/an-agent-approval-is-claimed-before-the-tool-runs.md +33 -0
  240. package/knowledge/decisions/security/an-approval-answer-outlives-the-run-it-answered.md +59 -0
  241. package/knowledge/decisions/security/an-upload-is-counted-as-it-arrives-not-buffered-then-measured.md +24 -0
  242. package/knowledge/decisions/security/index.md +9 -0
  243. package/knowledge/questions/channel-middleware-accepts-bare-factories-that-nothing-resolves.md +35 -0
  244. package/knowledge/questions/index.md +2 -1
  245. package/knowledge/questions/unauthorized-channel-replies-escape-the-declared-out-type.md +44 -0
  246. package/package.json +15 -3
  247. package/scripts/generate-api-report.d.mts +1 -0
  248. package/scripts/generate-api-report.mjs +89 -0
  249. package/scripts/generate-api-report.mts +343 -0
  250. package/src/api-report.test.ts +69 -0
  251. package/src/column-form.ts +1 -4
  252. package/src/crypto-utils.test.ts +15 -2
  253. package/src/dev/hot-reload.ts +2 -7
  254. package/src/ecosystem.ts +40 -0
  255. package/src/errors/error-handler.ts +5 -3
  256. package/src/errors/error.test.ts +4 -1
  257. package/src/errors/index.ts +1 -1
  258. package/src/function/function-runner.test.ts +53 -1
  259. package/src/function/function-runner.ts +33 -48
  260. package/src/function/functions.types.ts +15 -9
  261. package/src/function/index.ts +0 -2
  262. package/src/handle-error.ts +1 -1
  263. package/src/index.ts +3 -57
  264. package/src/middleware/auth-cookie.test.ts +50 -0
  265. package/src/middleware/auth-cookie.ts +38 -2
  266. package/src/middleware/cors.ts +2 -1
  267. package/src/middleware/index.ts +1 -1
  268. package/src/middleware/remote-auth.test.ts +43 -0
  269. package/src/middleware/remote-auth.ts +17 -3
  270. package/src/middleware-runner.ts +4 -2
  271. package/src/no-any-casts.test.ts +57 -0
  272. package/src/permissions.test.ts +3 -1
  273. package/src/permissions.ts +22 -24
  274. package/src/pikku-state.ts +22 -6
  275. package/src/public-surface.json +554 -0
  276. package/src/public-surface.json.README +25 -0
  277. package/src/public-surface.test.ts +105 -0
  278. package/src/removed-legacy-exports.test.ts +62 -0
  279. package/src/schema.test.ts +78 -0
  280. package/src/schema.ts +36 -1
  281. package/src/services/ai-agent-runner-service.ts +12 -1
  282. package/src/services/ai-run-state-service.ts +18 -1
  283. package/src/services/audit-service.ts +2 -2
  284. package/src/services/in-memory-ai-run-state-service.ts +16 -2
  285. package/src/services/in-memory-workflow-service.ts +2 -0
  286. package/src/services/index.ts +4 -47
  287. package/src/services/local-content-request-handler.test.ts +43 -5
  288. package/src/services/local-content-request-handler.ts +103 -74
  289. package/src/services/local-content.ts +15 -1
  290. package/src/services/local-email-service.ts +5 -1
  291. package/src/services/scoped-credential-service.test.ts +86 -0
  292. package/src/services/scoped-credential-service.ts +63 -0
  293. package/src/services/system-role-guard.test.ts +4 -1
  294. package/src/services/workflow-service.ts +9 -2
  295. package/src/side-effects-are-declared.test.ts +84 -0
  296. package/src/source-files-stay-composable.test.ts +41 -0
  297. package/src/testing/service-tests/agent-run-service-tests.ts +98 -0
  298. package/src/testing/service-tests/ai-storage-service-tests.ts +379 -0
  299. package/src/testing/service-tests/channel-store-tests.ts +98 -0
  300. package/src/testing/service-tests/credential-service-tests.ts +143 -0
  301. package/src/testing/service-tests/deployment-service-tests.ts +33 -0
  302. package/src/testing/service-tests/event-hub-store-tests.ts +48 -0
  303. package/src/testing/service-tests/secret-service-tests.ts +105 -0
  304. package/src/testing/service-tests/session-store-tests.ts +62 -0
  305. package/src/testing/service-tests/workflow-run-service-tests.ts +59 -0
  306. package/src/testing/service-tests/workflow-service-tests.ts +308 -0
  307. package/src/testing/service-tests.ts +31 -1111
  308. package/src/types/core.types.ts +14 -3
  309. package/src/types/state.types.ts +17 -1
  310. package/src/wirings/actor-flow/index.ts +0 -3
  311. package/src/wirings/actor-flow/run-conversation.ts +1 -6
  312. package/src/wirings/ai-agent/agent-rpc.ts +120 -0
  313. package/src/wirings/ai-agent/ai-agent-agui.ts +1 -5
  314. package/src/wirings/ai-agent/ai-agent-finalize.test.ts +186 -0
  315. package/src/wirings/ai-agent/ai-agent-finalize.ts +197 -0
  316. package/src/wirings/ai-agent/ai-agent-interrupt.test.ts +2 -1
  317. package/src/wirings/ai-agent/ai-agent-interrupt.ts +1 -0
  318. package/src/wirings/ai-agent/ai-agent-memory.ts +62 -39
  319. package/src/wirings/ai-agent/ai-agent-model-config.test.ts +72 -3
  320. package/src/wirings/ai-agent/ai-agent-model-config.ts +49 -1
  321. package/src/wirings/ai-agent/ai-agent-prepare.ts +12 -12
  322. package/src/wirings/ai-agent/ai-agent-resume-authorization.test.ts +1 -0
  323. package/src/wirings/ai-agent/ai-agent-runner.test.ts +82 -2
  324. package/src/wirings/ai-agent/ai-agent-runner.ts +120 -160
  325. package/src/wirings/ai-agent/ai-agent-stream-output-hooks.test.ts +353 -0
  326. package/src/wirings/ai-agent/ai-agent-stream.test.ts +2 -0
  327. package/src/wirings/ai-agent/ai-agent-stream.ts +152 -117
  328. package/src/wirings/ai-agent/ai-agent-thread-ownership.test.ts +1 -1
  329. package/src/wirings/ai-agent/ai-agent-turn.test.ts +67 -0
  330. package/src/wirings/ai-agent/ai-agent-turn.ts +122 -0
  331. package/src/wirings/ai-agent/ai-agent.types.ts +65 -5
  332. package/src/wirings/ai-agent/index.ts +2 -16
  333. package/src/wirings/ai-agent/voice-input.ts +1 -6
  334. package/src/wirings/ai-agent/voice-output.ts +2 -12
  335. package/src/wirings/ai-scorer/ai-scorer-grade.test.ts +106 -0
  336. package/src/wirings/ai-scorer/ai-scorer-grade.ts +55 -0
  337. package/src/wirings/ai-scorer/ai-scorer-judge.test.ts +143 -0
  338. package/src/wirings/ai-scorer/ai-scorer-judge.ts +120 -0
  339. package/src/wirings/ai-scorer/ai-scorer-live.test.ts +174 -0
  340. package/src/wirings/ai-scorer/ai-scorer-live.ts +56 -0
  341. package/src/wirings/ai-scorer/ai-scorer-registry.ts +63 -0
  342. package/src/wirings/ai-scorer/ai-scorer-sampling.test.ts +34 -0
  343. package/src/wirings/ai-scorer/ai-scorer-sampling.ts +36 -0
  344. package/src/wirings/ai-scorer/ai-scorer-snapshots.test.ts +49 -0
  345. package/src/wirings/ai-scorer/ai-scorer-snapshots.ts +46 -0
  346. package/src/wirings/ai-scorer/ai-scorer-worker.test.ts +122 -0
  347. package/src/wirings/ai-scorer/ai-scorer-worker.ts +69 -0
  348. package/src/wirings/ai-scorer/ai-scorer.ts +76 -0
  349. package/src/wirings/ai-scorer/ai-scorer.types.ts +107 -0
  350. package/src/wirings/ai-scorer/index.ts +24 -0
  351. package/src/wirings/channel/channel-common.ts +5 -2
  352. package/src/wirings/channel/channel-handler-shapes.test.ts +72 -0
  353. package/src/wirings/channel/channel-handler.ts +7 -7
  354. package/src/wirings/channel/channel-rpc-service.ts +0 -14
  355. package/src/wirings/channel/channel-rpc.test.ts +25 -4
  356. package/src/wirings/channel/channel-rpc.types.ts +14 -0
  357. package/src/wirings/channel/channel-runner.ts +33 -20
  358. package/src/wirings/channel/channel.types.ts +2 -0
  359. package/src/wirings/channel/index.ts +1 -20
  360. package/src/wirings/channel/local/local-channel-runner.test.ts +68 -0
  361. package/src/wirings/channel/local/local-channel-runner.ts +9 -2
  362. package/src/wirings/channel/pikku-abstract-channel-handler.ts +2 -1
  363. package/src/wirings/channel/serverless/serverless-channel-runner.ts +6 -3
  364. package/src/wirings/cli/channel/cli-channel-runner.ts +3 -1
  365. package/src/wirings/cli/channel/cli-raw-channel-runner.test.ts +23 -0
  366. package/src/wirings/cli/channel/cli-raw-channel-runner.ts +12 -1
  367. package/src/wirings/cli/channel/cli-raw-client-runner.ts +4 -1
  368. package/src/wirings/cli/channel/index.ts +0 -7
  369. package/src/wirings/cli/cli-runner.test.ts +68 -0
  370. package/src/wirings/cli/cli-runner.ts +25 -5
  371. package/src/wirings/cli/cli.types.ts +0 -39
  372. package/src/wirings/cli/command-parser.test.ts +19 -0
  373. package/src/wirings/cli/command-parser.ts +17 -1
  374. package/src/wirings/credential/index.ts +0 -1
  375. package/src/wirings/gateway/gateway-channel-meta.test.ts +44 -0
  376. package/src/wirings/gateway/gateway-runner.ts +27 -19
  377. package/src/wirings/gateway/index.ts +0 -3
  378. package/src/wirings/http/http-routes.ts +4 -1
  379. package/src/wirings/http/http-runner.test.ts +66 -0
  380. package/src/wirings/http/http-runner.ts +21 -28
  381. package/src/wirings/http/http.types.ts +0 -15
  382. package/src/wirings/http/index.ts +2 -8
  383. package/src/wirings/http/pikku-fetch-http-request.ts +2 -2
  384. package/src/wirings/http/web-request.ts +1 -1
  385. package/src/wirings/mcp/index.ts +0 -1
  386. package/src/wirings/mcp/mcp-runner.test.ts +181 -0
  387. package/src/wirings/mcp/mcp-runner.ts +37 -21
  388. package/src/wirings/persona/index.ts +0 -8
  389. package/src/wirings/persona/persona-environments.test.ts +14 -3
  390. package/src/wirings/persona/persona.test.ts +13 -3
  391. package/src/wirings/persona/validate-personas.ts +5 -1
  392. package/src/wirings/queue/index.ts +0 -14
  393. package/src/wirings/queue/queue-runner.ts +1 -1
  394. package/src/wirings/rpc/addon-auth-tags.test.ts +1 -5
  395. package/src/wirings/rpc/addon-runner.ts +34 -3
  396. package/src/wirings/rpc/addon-secrets.test.ts +261 -0
  397. package/src/wirings/rpc/rpc-runner.test.ts +2 -0
  398. package/src/wirings/rpc/rpc-runner.ts +60 -136
  399. package/src/wirings/rpc/rpc-types.ts +11 -0
  400. package/src/wirings/rpc/wire-addon.ts +20 -1
  401. package/src/wirings/scheduler/index.ts +0 -1
  402. package/src/wirings/secret/validate-secret-definitions.test.ts +22 -0
  403. package/src/wirings/secret/validate-secret-definitions.ts +2 -2
  404. package/src/wirings/trigger/index.ts +0 -1
  405. package/src/wirings/trigger/pikku-trigger-service.ts +0 -5
  406. package/src/wirings/trigger/trigger-runner.ts +7 -5
  407. package/src/wirings/virtual-user/index.ts +0 -16
  408. package/src/wirings/virtual-user/run-virtual-user.test.ts +18 -9
  409. package/src/wirings/virtual-user/run-virtual-user.ts +28 -15
  410. package/src/wirings/virtual-user/virtual-user-agents.test.ts +4 -1
  411. package/src/wirings/virtual-user/virtual-user-derive.ts +4 -25
  412. package/src/wirings/virtual-user/virtual-user-dispositions.test.ts +7 -2
  413. package/src/wirings/virtual-user/virtual-user-dispositions.ts +1 -4
  414. package/src/wirings/virtual-user/virtual-user-intents.test.ts +13 -3
  415. package/src/wirings/workflow/dsl/workflow-dsl.types.ts +117 -4
  416. package/src/wirings/workflow/feature.ts +7 -4
  417. package/src/wirings/workflow/graph/graph-node.ts +1 -1
  418. package/src/wirings/workflow/graph/graph-runner.test.ts +76 -2
  419. package/src/wirings/workflow/graph/graph-runner.ts +13 -16
  420. package/src/wirings/workflow/graph/workflow-graph.types.ts +0 -5
  421. package/src/wirings/workflow/index.ts +14 -24
  422. package/src/wirings/workflow/pikku-scenario-service.ts +101 -27
  423. package/src/wirings/workflow/pikku-workflow-service.test.ts +15 -14
  424. package/src/wirings/workflow/pikku-workflow-service.ts +214 -743
  425. package/src/wirings/workflow/scenario-expectations.test.ts +75 -0
  426. package/src/wirings/workflow/scenario-hooks.test.ts +52 -0
  427. package/src/wirings/workflow/scenario-step.types.ts +8 -0
  428. package/src/wirings/workflow/workflow-approval-audit.ts +47 -0
  429. package/src/wirings/workflow/workflow-approval-policy.test.ts +524 -0
  430. package/src/wirings/workflow/workflow-approval-policy.ts +68 -0
  431. package/src/wirings/workflow/workflow-approval.ts +289 -0
  432. package/src/wirings/workflow/workflow-child-run-session.test.ts +89 -0
  433. package/src/wirings/workflow/workflow-constants.ts +47 -0
  434. package/src/wirings/workflow/workflow-dispatch-durability.test.ts +1 -1
  435. package/src/wirings/workflow/workflow-errors.ts +128 -0
  436. package/src/wirings/workflow/workflow-inline-authority.test.ts +12 -6
  437. package/src/wirings/workflow/workflow-meta-resolver.ts +45 -0
  438. package/src/wirings/workflow/workflow-missing-meta.test.ts +92 -0
  439. package/src/wirings/workflow/workflow-queue-routing.ts +85 -0
  440. package/src/wirings/workflow/workflow-queue-wiring.ts +149 -0
  441. package/src/wirings/workflow/workflow-recovery.ts +162 -0
  442. package/src/wirings/workflow/workflow-retry-policy.test.ts +1 -1
  443. package/src/wirings/workflow/workflow-run-authority.test.ts +212 -0
  444. package/src/wirings/workflow/workflow-run-engine.types.ts +91 -0
  445. package/src/wirings/workflow/workflow-run-ownership.ts +37 -0
  446. package/src/wirings/workflow/workflow-suspend.ts +61 -0
  447. package/src/wirings/workflow/workflow.types.ts +7 -9
  448. package/src/wirings-stay-decoupled.test.ts +127 -0
  449. package/tsconfig.json +1 -1
  450. package/tsconfig.tsbuildinfo +1 -1
  451. package/dist/internal.d.ts +0 -3
  452. package/dist/internal.js +0 -2
  453. package/dist/middleware/timeout.d.ts +0 -9
  454. package/dist/middleware/timeout.js +0 -15
  455. package/dist/pikku-response.d.ts +0 -6
  456. package/dist/pikku-response.js +0 -6
  457. package/dist/services/gopass-secrets.d.ts +0 -15
  458. package/dist/services/gopass-secrets.js +0 -76
  459. package/dist/services/http-scenario-actors.d.ts +0 -75
  460. package/dist/services/http-scenario-actors.js +0 -195
  461. package/dist/services/http-user-flow-actors.d.ts +0 -67
  462. package/dist/services/http-user-flow-actors.js +0 -193
  463. package/dist/services/scenario-actors-service.d.ts +0 -127
  464. package/dist/services/scenario-actors-service.js +0 -40
  465. package/dist/services/user-flow-actors-service.d.ts +0 -39
  466. package/dist/wirings/credential/wire-credential.d.ts +0 -48
  467. package/dist/wirings/credential/wire-credential.js +0 -47
  468. package/dist/wirings/oauth2/oauth2-client.d.ts +0 -47
  469. package/dist/wirings/oauth2/oauth2-client.js +0 -263
  470. package/dist/wirings/oauth2/oauth2-routes.d.ts +0 -35
  471. package/dist/wirings/oauth2/oauth2-routes.js +0 -146
  472. package/dist/wirings/scope/wire-scope.d.ts +0 -33
  473. package/dist/wirings/scope/wire-scope.js +0 -32
  474. package/dist/wirings/workflow/dsl/index.d.ts +0 -5
  475. package/dist/wirings/workflow/dsl/index.js +0 -4
  476. package/dist/wirings/workflow/graph/index.d.ts +0 -5
  477. package/dist/wirings/workflow/graph/index.js +0 -4
  478. package/src/internal.ts +0 -10
  479. /package/dist/{services/user-flow-actors-service.js → wirings/workflow/workflow-run-engine.types.js} +0 -0
@@ -31,23 +31,9 @@ export const deriveCatalogue = (
31
31
  ): ApiCatalogueEntry[] => {
32
32
  const entries: ApiCatalogueEntry[] = []
33
33
  for (const [name, meta] of Object.entries(functions ?? {})) {
34
- // Scenario bodies and their steps are never network-callable — they are
35
- // held out of every deployed unit — so offering one would only ever waste
36
- // a turn on a 404.
37
- //
38
- // For a platform or addon step this is not an efficiency argument but the
39
- // oracle: a virtual user's findings are worth something only because it
40
- // cannot manufacture the outcomes it is meant to discover. One that could
41
- // invoke "Stripe's webhook arrives" would forge its own payment success,
42
- // and every finding downstream of that is worthless. Same class of argument
43
- // as `allowApprovalRequired` defaulting to false — and enforced here, at
44
- // derivation, rather than by convention.
34
+ // knowledge: decisions/security/a-virtual-user-is-never-offered-a-step-that-would-forge-its-own-oracle.md
45
35
  if (meta.scenario || meta.scenarioStep || meta.scenarioStepKind) continue
46
- // `expose: true` is what puts a function on the rpc transport, and that is
47
- // what the shipped target calls over. Absent is not permissive: an unexposed
48
- // function 404s, and offering one spends a step to learn nothing about the
49
- // product. Measured on the e2e app, 34 of its 72 functions are in exactly
50
- // that state — nearly half a catalogue that cannot be called.
36
+ // knowledge: decisions/internals/only-exposed-functions-enter-a-virtual-user-catalogue.md
51
37
  if (meta.expose !== true) continue
52
38
 
53
39
  const inputSchema = meta.inputSchemaName
@@ -103,10 +89,7 @@ const scenarioSteps = (meta: {
103
89
  )
104
90
  }
105
91
 
106
- // Insertion order is the order the CLI wrote the nodes in, which is the order
107
- // the scenario declares them. Following `next` instead would buy an ordering
108
- // that is already true and cost a traversal that has to decide what a branch
109
- // means to a reader who will never take one.
92
+ // knowledge: decisions/internals/virtual-user-step-order-comes-from-insertion-order.md
110
93
  return Object.values(meta.nodes ?? {}).flatMap((node) => {
111
94
  const { rpcName, scenarioStepPhase, actor } = (node ?? {}) as {
112
95
  rpcName?: string
@@ -149,11 +132,7 @@ export const deriveIntents = (
149
132
  steps.push(
150
133
  composeStepProse({
151
134
  phase: step.phase,
152
- // The template goes in unfilled, braces and all. A reporter fills its
153
- // placeholders from a run that happened; there is no run yet, and
154
- // "invites {email}" tells a user to pick someone where the filled-in
155
- // form would have told it whom — which is the scenario's answer, not
156
- // the user's own.
135
+ // knowledge: decisions/internals/a-scenario-step-template-is-offered-unfilled.md
157
136
  description: template ?? description ?? '',
158
137
  actor: step.actor,
159
138
  })
@@ -1,6 +1,9 @@
1
1
  import { strict as assert } from 'assert'
2
2
  import { describe, test } from 'node:test'
3
- import { DISPOSITIONS, dispositionProfile } from './virtual-user-dispositions.js'
3
+ import {
4
+ DISPOSITIONS,
5
+ dispositionProfile,
6
+ } from './virtual-user-dispositions.js'
4
7
 
5
8
  describe('resolving a disposition profile', () => {
6
9
  test('an unknown disposition falls back to realistic rather than throwing', () => {
@@ -46,7 +49,9 @@ describe('resolving a disposition profile', () => {
46
49
  const profile = dispositionProfile('careless', {
47
50
  instructions: 'Paste ids from other tabs.',
48
51
  })
49
- assert.ok(profile.instructions.startsWith(DISPOSITIONS.careless.instructions))
52
+ assert.ok(
53
+ profile.instructions.startsWith(DISPOSITIONS.careless.instructions)
54
+ )
50
55
  assert.ok(profile.instructions.endsWith('Paste ids from other tabs.'))
51
56
  })
52
57
 
@@ -154,10 +154,7 @@ export const DISPOSITIONS: Readonly<
154
154
  invertedOracle: true,
155
155
  },
156
156
 
157
- // The only disposition that is not testing anything. It is doing the job, so
158
- // it abandons rarely, runs cool, and is told to stop and say so rather than
159
- // guess — the failure mode here is a wrong action nobody asked for, not a
160
- // missed bug.
157
+ // knowledge: decisions/internals/the-worker-disposition-is-the-one-that-is-not-testing.md
161
158
  accountable: {
162
159
  ...REALISTIC,
163
160
  instructions: [
@@ -3,7 +3,10 @@ import assert from 'node:assert/strict'
3
3
 
4
4
  import { IntentStack, intentsForPersona } from './virtual-user-intents.js'
5
5
  import { createRng } from './virtual-user-rng.js'
6
- import { DISPOSITIONS, type DispositionProfile } from './virtual-user-dispositions.js'
6
+ import {
7
+ DISPOSITIONS,
8
+ type DispositionProfile,
9
+ } from './virtual-user-dispositions.js'
7
10
  import type { IntentSource } from './virtual-user.types.js'
8
11
 
9
12
  const profileWith = (
@@ -182,7 +185,11 @@ describe('intents for an actor', () => {
182
185
  const catalogue: IntentSource[] = [
183
186
  { id: 'a', title: 'invite a teammate', personas: ['orgAdmin'] },
184
187
  { id: 'b', title: 'read the dashboard' },
185
- { id: 'c', title: 'deploy a stage', personas: ['platformAdmin', 'orgAdmin'] },
188
+ {
189
+ id: 'c',
190
+ title: 'deploy a stage',
191
+ personas: ['platformAdmin', 'orgAdmin'],
192
+ },
186
193
  ]
187
194
 
188
195
  test('an actor gets what names it, plus anything that names nobody', () => {
@@ -201,7 +208,10 @@ describe('intents for an actor', () => {
201
208
 
202
209
  test('an empty actor list means everyone', () => {
203
210
  assert.deepEqual(
204
- intentsForPersona([{ id: 'x', title: 'anything', personas: [] }], 'nobody'),
211
+ intentsForPersona(
212
+ [{ id: 'x', title: 'anything', personas: [] }],
213
+ 'nobody'
214
+ ),
205
215
  [{ id: 'x', title: 'anything', personas: [] }]
206
216
  )
207
217
  })
@@ -65,6 +65,20 @@ export interface WorkflowExpectServiceOptions extends WorkflowStepOptions {
65
65
  times?: number
66
66
  }
67
67
 
68
+ /** Options for workflow.expectScore() */
69
+ export interface WorkflowExpectScoreOptions extends WorkflowStepOptions {
70
+ /** Fail below this score. Defaults to 0.5 — a scorer that answered at all. */
71
+ atLeast?: number
72
+ /** Fail above this score, for a scorer where high is the failure. */
73
+ atMost?: number
74
+ /**
75
+ * The known-correct answer, which is what a reference-based judge grades
76
+ * against. Live traffic has no answer key, so a judge declared
77
+ * `requiresReference` is only ever reachable from a scenario.
78
+ */
79
+ reference?: string
80
+ }
81
+
68
82
  /**
69
83
  * Type signature for workflow.do() RPC form - used by inspector
70
84
  */
@@ -114,12 +128,55 @@ export type WorkflowWireSleep = (
114
128
  */
115
129
  export type WorkflowWireSuspend = (reason: string) => Promise<void>
116
130
 
131
+ /**
132
+ * Who is allowed to answer an approval gate, relative to the user who started
133
+ * the run.
134
+ *
135
+ * - `any` — anyone the approve entrypoint lets through. The gate is a pause for
136
+ * a decision, not an authorization boundary.
137
+ * - `owner` — only the user who started the run, so a gate can be used to
138
+ * confirm an action against the same person who requested it.
139
+ * - `not-initiator` — anyone *except* the user who started the run: four-eyes.
140
+ * A run with no recorded initiator has nobody to exclude, so this degrades to
141
+ * requiring a signed-in decider.
142
+ */
143
+ export type WorkflowApprovalApprovers = 'any' | 'owner' | 'not-initiator'
144
+
145
+ /**
146
+ * Who may answer an approval gate. Declared on the gate rather than the
147
+ * entrypoint because it is a property of the decision being made, and because
148
+ * one workflow can hold several gates with different requirements.
149
+ */
150
+ export interface WorkflowApprovalPolicy {
151
+ /**
152
+ * Defaults to `any`. See {@link WorkflowApprovalApprovers}.
153
+ */
154
+ approvers?: WorkflowApprovalApprovers
155
+ /**
156
+ * Additionally require the decider's session to hold this scope, so
157
+ * "a second pair of eyes" can be narrowed to "a second pair of *senior*
158
+ * eyes". Combines with `approvers` — both must pass.
159
+ */
160
+ approverScope?: string
161
+ }
162
+
163
+ /**
164
+ * The decider, reduced to the two facts a policy can be expressed in terms of.
165
+ * Recorded alongside the decision so the gate can be judged on replay — the
166
+ * session itself is long gone by then — and carried into the settled outcome so
167
+ * the answer keeps its provenance.
168
+ */
169
+ export interface ApprovalDecider {
170
+ userId?: string
171
+ scopes?: string[]
172
+ }
173
+
117
174
  /**
118
175
  * Options for workflow.approval().
119
176
  */
120
177
  export interface WorkflowApprovalOptions<
121
178
  TSchema extends StandardSchemaV1 = StandardSchemaV1,
122
- > {
179
+ > extends WorkflowApprovalPolicy {
123
180
  /**
124
181
  * Schema the decision payload is validated against. This is a VALUE, not a
125
182
  * type generic: the payload arrives from an untrusted caller over the approve
@@ -142,7 +199,18 @@ export interface WorkflowApprovalOptions<
142
199
  * carried in `data` and is the application's business, not the framework's.
143
200
  */
144
201
  export type ApprovalOutcome<T> =
145
- | { status: 'decided'; data: T }
202
+ | {
203
+ status: 'decided'
204
+ data: T
205
+ /**
206
+ * Who answered, and when. Run state holds the decision only while the gate
207
+ * is open — it is overwritten by the next write and cleared outright when a
208
+ * decision is refused — so the settled answer carries its own provenance
209
+ * into the step result, which is append-only.
210
+ */
211
+ decidedBy?: ApprovalDecider
212
+ decidedAt?: string
213
+ }
146
214
  | { status: 'expired' }
147
215
 
148
216
  /**
@@ -311,7 +379,7 @@ export interface ReturnStepMeta {
311
379
  }
312
380
 
313
381
  /**
314
- * Inline step metadata (legacy support)
382
+ * Inline step metadata
315
383
  */
316
384
  export interface InlineStepMeta {
317
385
  /** Inline form - local execution */
@@ -416,6 +484,10 @@ export interface ApprovalStepMeta {
416
484
  outputVar?: string
417
485
  /** Expiry duration, when one was given */
418
486
  expiry?: string | number
487
+ /** Who may answer the gate, when the default was not used */
488
+ approvers?: WorkflowApprovalApprovers
489
+ /** Scope the decider must hold, when one was required */
490
+ approverScope?: string
419
491
  }
420
492
 
421
493
  /**
@@ -528,7 +600,36 @@ export interface PikkuWorkflowWire {
528
600
  approval: WorkflowWireApproval
529
601
  }
530
602
 
531
- export interface PikkuScenarioWire extends PikkuWorkflowWire {
603
+ /**
604
+ * What a scenario has accumulated so far, shared by its body and its hooks.
605
+ *
606
+ * `Partial` because a scenario can fail at any step: `Out` describes what a
607
+ * *completed* run produces, and the context holds however much of it this run
608
+ * reached.
609
+ *
610
+ * The `0 extends 1 & Out` branch keeps `any` collapsing to `any` rather than to
611
+ * the union of the other two. `PikkuWire` and `PikkuFunctionConfig` both use
612
+ * this type as a generic *constraint*, and a constraint carrying a concrete
613
+ * context would reject every wire whose scenario output differs from it.
614
+ */
615
+ export type ScenarioContext<Out = unknown> = 0 extends 1 & Out
616
+ ? any
617
+ : Out extends object
618
+ ? Partial<Out>
619
+ : Record<string, unknown>
620
+
621
+ export interface PikkuScenarioWire<Out = unknown> extends PikkuWorkflowWire {
622
+ /**
623
+ * Scratch the body writes and the `before`/`after` hooks read. A hook is a
624
+ * separate function and cannot see the body's locals, so this is how teardown
625
+ * learns the ids the body minted.
626
+ *
627
+ * Deliberately *not* a world: it is scoped to one run, and scenario steps
628
+ * cannot reach it. Steps stay pure functions of their declared inputs, so the
629
+ * ladder in the report remains a complete account of what a step saw.
630
+ */
631
+ context: ScenarioContext<Out>
632
+
532
633
  /**
533
634
  * Durable polling step: invoke `rpcName` (as an actor when `options.actor`
534
635
  * is set) until `predicate` passes or `options.within` elapses.
@@ -556,6 +657,18 @@ export interface PikkuScenarioWire extends PikkuWorkflowWire {
556
657
  options?: WorkflowExpectServiceOptions
557
658
  ) => Promise<void>
558
659
 
660
+ /**
661
+ * Grade-assertion step: runs one declared scorer against a finished agent run
662
+ * and asserts the score. Returns the grade, so a scenario can report the
663
+ * reason a judge gave rather than only that it fell short.
664
+ */
665
+ expectScore: (
666
+ stepName: string,
667
+ runId: string,
668
+ scorerName: string,
669
+ options?: WorkflowExpectScoreOptions
670
+ ) => Promise<{ score: number; reason?: string }>
671
+
559
672
  /**
560
673
  * Run a registered scenario step, as the setup the scenario starts from.
561
674
  *
@@ -32,9 +32,11 @@ export const resolveFeatureScenarios = (
32
32
  const scenarios = feature.scenarios ?? []
33
33
  for (let index = 0; index < scenarios.length; index++) {
34
34
  const entry = scenarios[index]!
35
- const paired =
35
+ const pairing =
36
36
  typeof entry === 'object' && entry !== null && 'scenario' in entry
37
- const config = paired ? (entry as any).scenario : entry
37
+ ? (entry as { scenario: unknown; data?: unknown })
38
+ : undefined
39
+ const config = pairing ? pairing.scenario : entry
38
40
  const scenarioName = nameByConfig.get(config)
39
41
  if (!scenarioName) {
40
42
  unresolved.push({ featureId, index })
@@ -44,10 +46,11 @@ export const resolveFeatureScenarios = (
44
46
  featureId,
45
47
  featureName: feature.name ?? featureId,
46
48
  scenarioName,
47
- data: paired ? (entry as any).data : undefined,
49
+ data: pairing?.data,
48
50
  tags: [
49
51
  ...new Set([
50
- ...((registrations.get(scenarioName)?.func as any)?.tags ?? []),
52
+ ...((registrations.get(scenarioName)?.func as { tags?: string[] })
53
+ ?.tags ?? []),
51
54
  ...(feature.tags ?? []),
52
55
  ]),
53
56
  ],
@@ -65,7 +65,7 @@ export function createGraph<RPCMap extends Record<string, RPCHandler>>() {
65
65
  for (const [nodeId, def] of Object.entries(nodes) as [string, any][]) {
66
66
  result[nodeId] = {
67
67
  func: funcMap[nodeId] as string,
68
- input: def?.input as any,
68
+ input: def?.input,
69
69
  next: def?.next,
70
70
  onError: def?.onError,
71
71
  retries: def?.retries,
@@ -10,7 +10,7 @@ import {
10
10
  import type { WorkflowRuntimeMeta } from '../workflow.types.js'
11
11
  import { pikkuState } from '../../../pikku-state.js'
12
12
  import { RPCNotFoundError } from '../../rpc/rpc-runner.js'
13
- import { DEFAULT_STEP_RETRIES } from '../pikku-workflow-service.js'
13
+ import { DEFAULT_STEP_RETRIES } from '../workflow-constants.js'
14
14
 
15
15
  describe('graph-runner bugs', () => {
16
16
  test('continueGraph should NOT mark workflow completed while nodes are still running', async () => {
@@ -54,6 +54,78 @@ describe('graph-runner bugs', () => {
54
54
  assert.equal(run?.status, 'running')
55
55
  })
56
56
 
57
+ test('startWorkflow rejects a startNode that is not a declared entry node', async () => {
58
+ const ws = new InMemoryWorkflowService()
59
+ const metaState = pikkuState(null, 'workflows', 'meta')
60
+ metaState['testStartNodeGate'] = {
61
+ name: 'testStartNodeGate',
62
+ pikkuFuncId: 'testStartNodeGate',
63
+ source: 'graph',
64
+ entryNodeIds: ['gate'],
65
+ graphHash: 'startnode-hash',
66
+ nodes: {
67
+ // 'charge' reads only trigger input, so it is dependency-free and would
68
+ // otherwise be directly startable — skipping the 'gate' entry node.
69
+ gate: { nodeId: 'gate', rpcName: 'checkEligibility', next: 'charge' },
70
+ charge: { nodeId: 'charge', rpcName: 'capturePayment' },
71
+ },
72
+ }
73
+
74
+ await assert.rejects(
75
+ () =>
76
+ ws.startWorkflow(
77
+ 'testStartNodeGate',
78
+ {},
79
+ { type: 'test' },
80
+ { rpcWithWire: async () => ({}) },
81
+ { startNode: 'charge', inline: true }
82
+ ),
83
+ /not a declared entry node/
84
+ )
85
+ assert.equal(
86
+ [...(ws as any).runs.keys()].length,
87
+ 0,
88
+ 'no run should be created for a non-entry startNode'
89
+ )
90
+ delete metaState['testStartNodeGate']
91
+ })
92
+
93
+ test('startWorkflow accepts a startNode that is a declared entry node', async () => {
94
+ const ws = new InMemoryWorkflowService()
95
+ const metaState = pikkuState(null, 'workflows', 'meta')
96
+ // 'b' is a declared entry but carries an unknown input ref, so it clears the
97
+ // startNode gate and then throws at reference validation — proving the gate
98
+ // admitted it, without dispatching real work.
99
+ metaState['testStartNodeAllowed'] = {
100
+ name: 'testStartNodeAllowed',
101
+ pikkuFuncId: 'testStartNodeAllowed',
102
+ source: 'graph',
103
+ entryNodeIds: ['a', 'b'],
104
+ graphHash: 'startnode-ok-hash',
105
+ nodes: {
106
+ a: { nodeId: 'a', rpcName: 'doA' },
107
+ b: {
108
+ nodeId: 'b',
109
+ rpcName: 'doB',
110
+ input: { dep: { $ref: 'missingNode', path: 'value' } },
111
+ },
112
+ },
113
+ }
114
+ await assert.rejects(
115
+ () =>
116
+ ws.startWorkflow(
117
+ 'testStartNodeAllowed',
118
+ {},
119
+ { type: 'test' },
120
+ { rpcWithWire: async () => ({}) },
121
+ { startNode: 'b', inline: true }
122
+ ),
123
+ /references unknown node 'missingNode'/,
124
+ 'a declared entry node must pass the startNode gate (reaching ref validation)'
125
+ )
126
+ delete metaState['testStartNodeAllowed']
127
+ })
128
+
57
129
  test('runWorkflowGraph should throw and not create a run for unknown input refs', async () => {
58
130
  const ws = new InMemoryWorkflowService()
59
131
 
@@ -828,7 +900,9 @@ describe('graph-runner bugs', () => {
828
900
  name: 'testRetries',
829
901
  pikkuFuncId: 'testRetries',
830
902
  source: 'graph',
831
- entryNodeIds: ['a'],
903
+ // All three are dependency-free, so all three are entry nodes — a
904
+ // `startNode` may only name one the graph declared.
905
+ entryNodeIds: ['a', 'b', 'c'],
832
906
  graphHash: 'retries-hash',
833
907
  nodes: {
834
908
  a: {
@@ -1,9 +1,9 @@
1
+ import type { PikkuWorkflowService } from '../pikku-workflow-service.js'
1
2
  import {
2
- type PikkuWorkflowService,
3
3
  WorkflowAsyncException,
4
4
  WorkflowSuspendedException,
5
- DEFAULT_STEP_RETRIES,
6
- } from '../pikku-workflow-service.js'
5
+ } from '../workflow-errors.js'
6
+ import { DEFAULT_STEP_RETRIES } from '../workflow-constants.js'
7
7
  import type { GraphWireState, PikkuGraphWire } from './workflow-graph.types.js'
8
8
  import { pikkuState, getSingletonServices } from '../../../pikku-state.js'
9
9
  import type { WorkflowRuntimeMeta, WorkflowRunWire } from '../workflow.types.js'
@@ -211,7 +211,7 @@ function isDataRef(value: unknown): value is { $ref: string; path?: string } {
211
211
  typeof value === 'object' &&
212
212
  value !== null &&
213
213
  '$ref' in value &&
214
- typeof (value as any).$ref === 'string'
214
+ typeof (value as { $ref?: unknown }).$ref === 'string'
215
215
  )
216
216
  }
217
217
 
@@ -227,7 +227,7 @@ function isTemplate(value: unknown): value is TemplateValue {
227
227
  typeof value === 'object' &&
228
228
  value !== null &&
229
229
  '$template' in value &&
230
- typeof (value as any).$template === 'object'
230
+ typeof (value as { $template?: unknown }).$template === 'object'
231
231
  )
232
232
  }
233
233
 
@@ -695,14 +695,6 @@ export async function executeGraphStep(
695
695
  }
696
696
  }
697
697
 
698
- export async function onGraphNodeComplete(
699
- workflowService: PikkuWorkflowService,
700
- runId: string,
701
- graphName: string
702
- ): Promise<void> {
703
- await continueGraph(workflowService, runId, graphName)
704
- }
705
-
706
698
  export async function runFromMeta(
707
699
  workflowService: PikkuWorkflowService,
708
700
  runId: string,
@@ -928,9 +920,14 @@ export async function runWorkflowGraph(
928
920
  }
929
921
 
930
922
  const nodes = meta.nodes
931
- const entryNodes: string[] = startNode
932
- ? [startNode]
933
- : (meta.entryNodeIds ?? [])
923
+ const declaredEntryNodes = meta.entryNodeIds ?? []
924
+ // knowledge: decisions/security/a-graph-run-starts-at-an-entry-node-the-graph-declared.md
925
+ if (startNode && !declaredEntryNodes.includes(startNode)) {
926
+ throw new Error(
927
+ `Workflow graph '${graphName}': '${startNode}' is not an entry node`
928
+ )
929
+ }
930
+ const entryNodes: string[] = startNode ? [startNode] : declaredEntryNodes
934
931
  validateGraphReferences(graphName, nodes, entryNodes)
935
932
 
936
933
  if (entryNodes.length === 0) {
@@ -16,11 +16,6 @@ export const isRef = (value: unknown): value is RefValue =>
16
16
  '__isRef' in value &&
17
17
  (value as RefValue).__isRef === true
18
18
 
19
- export interface InputRef {
20
- nodeId: string
21
- path?: string
22
- }
23
-
24
19
  export type RefFn<NodeIds extends string = string> = (
25
20
  nodeId: NodeIds,
26
21
  path?: string
@@ -1,20 +1,28 @@
1
+ export { PikkuWorkflowService } from './pikku-workflow-service.js'
1
2
  export {
2
- PikkuWorkflowService,
3
3
  WorkflowCancelledException,
4
4
  WorkflowSuspendedException,
5
5
  WorkflowDispatchException,
6
6
  WorkflowNotFoundError,
7
7
  WorkflowRunNotFoundError,
8
8
  WorkflowApprovalResolvedError,
9
- DEFAULT_STEP_RETRIES,
10
- } from './pikku-workflow-service.js'
9
+ WorkflowStepFunctionMismatchError,
10
+ } from './workflow-errors.js'
11
+ export { DEFAULT_STEP_RETRIES } from './workflow-constants.js'
12
+ export {
13
+ assertWorkflowRunOwner,
14
+ WorkflowRunForbiddenError,
15
+ } from './workflow-run-ownership.js'
16
+ export { WorkflowApprovalForbiddenError } from './workflow-approval-policy.js'
11
17
  export type {
12
- RunLifecycleContext,
13
18
  WorkflowRunEngine,
14
19
  WorkflowRunExtension,
15
- } from './pikku-workflow-service.js'
20
+ } from './workflow-run-engine.types.js'
16
21
  export { deriveInvocationId, uuidv5 } from './workflow-invocation-id.js'
17
22
 
23
+ export { isRef } from './graph/workflow-graph.types.js'
24
+ export type { RefValue } from './graph/workflow-graph.types.js'
25
+
18
26
  export {
19
27
  buildRunTimeline,
20
28
  reconstructStateAt,
@@ -22,10 +30,7 @@ export {
22
30
  } from './run-timeline.js'
23
31
  export type {
24
32
  RunTimeline,
25
- RunTimelineEvent,
26
33
  ReconstructedRunState,
27
- ReconstructedStep,
28
- RunPhase,
29
34
  } from './run-timeline.js'
30
35
 
31
36
  export { addWorkflow } from './dsl/workflow-runner.js'
@@ -37,12 +42,6 @@ export {
37
42
  type PikkuWorkflowGraphConfig,
38
43
  type PikkuWorkflowGraphResult,
39
44
  } from './graph/wire-workflow-graph.js'
40
-
41
- export {
42
- pikkuWorkflowWorkerFunc,
43
- pikkuWorkflowOrchestratorFunc,
44
- pikkuWorkflowSleeperFunc,
45
- } from './workflow-queue-workers.js'
46
45
  export type {
47
46
  WorkflowStepInput as WorkflowStepQueueInput,
48
47
  PikkuWorkflowOrchestratorInput,
@@ -65,34 +64,25 @@ export type {
65
64
  WorkflowRunMirror,
66
65
  CoreWorkflow,
67
66
  CoreFeature,
68
- CoreFeatureScenario,
69
67
  FeatureMeta,
70
- FeatureMetaEntry,
71
68
  FeaturesMeta,
72
69
  FeaturePlanEntry,
73
70
  PikkuWorkflow,
74
71
  ContextVariable,
75
72
  WorkflowContext,
76
73
  WorkflowsMeta,
77
- WorkflowRuntimeMeta,
78
74
  WorkflowsRuntimeMeta,
79
75
  } from './workflow.types.js'
80
76
 
81
77
  export type {
82
78
  WorkflowStepOptions,
83
79
  WorkflowWireDoRPC,
84
- WorkflowWireDoInline,
85
- WorkflowWireSleep,
86
- WorkflowWireSuspend,
87
- WorkflowWireApproval,
88
80
  WorkflowApprovalOptions,
89
81
  ApprovalOutcome,
90
82
  InputSource,
91
83
  OutputBinding,
92
84
  RpcStepMeta,
93
- SimpleCondition,
94
85
  Condition,
95
- BranchCase,
96
86
  BranchStepMeta,
97
87
  ParallelGroupStepMeta,
98
88
  FanoutStepMeta,
@@ -127,7 +117,7 @@ export type {
127
117
  export { SCENARIO_SURFACES } from './scenario-step.types.js'
128
118
 
129
119
  // Which of a step's bindings run: one for an action, every witness for a `then`
130
- export { resolveScenarioSurfaces, witnessesAgree } from './scenario-surface.js'
120
+ export { resolveScenarioSurfaces } from './scenario-surface.js'
131
121
 
132
122
  export { requireActor, requireScenarioEnv } from './scenario-step-guards.js'
133
123