@pikku/core 0.12.84 → 0.12.88

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 (577) hide show
  1. package/CHANGELOG.md +503 -0
  2. package/dist/bootstrap-compat/root.d.ts +20 -0
  3. package/dist/classification/column-form.d.ts +32 -0
  4. package/dist/classification/column-form.js +42 -0
  5. package/dist/classification/data-classification.d.ts +64 -0
  6. package/dist/classification/index.d.ts +13 -0
  7. package/dist/classification/index.js +2 -0
  8. package/dist/classification/secret-value.d.ts +56 -0
  9. package/dist/classification/secret-value.js +46 -0
  10. package/dist/crypto-utils.d.ts +1 -1
  11. package/dist/dev/hot-reload.d.ts +1 -1
  12. package/dist/dev/hot-reload.js +1 -1
  13. package/dist/ecosystem/agent-scorer.d.ts +1 -1
  14. package/dist/ecosystem/agent.d.ts +4 -1
  15. package/dist/ecosystem/channel/local.d.ts +2 -0
  16. package/dist/ecosystem/channel/local.js +2 -0
  17. package/dist/ecosystem/channel/serverless.d.ts +9 -0
  18. package/dist/ecosystem/channel/serverless.js +1 -0
  19. package/dist/ecosystem/channel.d.ts +3 -0
  20. package/dist/ecosystem/channel.js +1 -0
  21. package/dist/ecosystem/cli.d.ts +1 -1
  22. package/dist/ecosystem/cli.js +1 -1
  23. package/dist/ecosystem/credential.d.ts +1 -0
  24. package/dist/ecosystem/crypto-utils.d.ts +8 -0
  25. package/dist/ecosystem/crypto-utils.js +1 -0
  26. package/dist/ecosystem/dev.d.ts +1 -0
  27. package/dist/ecosystem/dev.js +1 -0
  28. package/{src/ecosystem/testing.ts → dist/ecosystem/errors.d.ts} +3 -3
  29. package/dist/ecosystem/errors.js +2 -0
  30. package/dist/ecosystem/function.d.ts +1 -1
  31. package/dist/ecosystem/function.js +1 -1
  32. package/dist/ecosystem/http.d.ts +3 -1
  33. package/dist/ecosystem/http.js +3 -1
  34. package/dist/ecosystem/mcp.d.ts +1 -1
  35. package/dist/ecosystem/mcp.js +1 -1
  36. package/dist/ecosystem/middleware.d.ts +2 -1
  37. package/dist/ecosystem/middleware.js +2 -1
  38. package/dist/ecosystem/persona.d.ts +4 -2
  39. package/dist/ecosystem/persona.js +2 -1
  40. package/dist/ecosystem/queue.d.ts +4 -2
  41. package/dist/ecosystem/queue.js +3 -1
  42. package/dist/ecosystem/role.d.ts +1 -0
  43. package/dist/ecosystem/rpc.d.ts +2 -0
  44. package/dist/ecosystem/rpc.js +1 -0
  45. package/dist/ecosystem/scenario.d.ts +8 -2
  46. package/dist/ecosystem/scenario.js +5 -1
  47. package/dist/ecosystem/scheduler.d.ts +2 -1
  48. package/dist/ecosystem/scheduler.js +2 -1
  49. package/dist/ecosystem/schema.d.ts +2 -1
  50. package/dist/ecosystem/schema.js +1 -1
  51. package/dist/ecosystem/scope.d.ts +2 -0
  52. package/dist/ecosystem/scope.js +1 -0
  53. package/dist/ecosystem/secret.d.ts +2 -0
  54. package/dist/ecosystem/secret.js +1 -0
  55. package/dist/ecosystem/services/local-content.d.ts +2 -1
  56. package/dist/ecosystem/services/local-content.js +1 -1
  57. package/dist/ecosystem/services/local-meta.d.ts +1 -0
  58. package/dist/ecosystem/services/local-meta.js +1 -0
  59. package/dist/ecosystem/services.d.ts +11 -3
  60. package/dist/ecosystem/services.js +7 -1
  61. package/dist/ecosystem/trigger.d.ts +1 -0
  62. package/dist/ecosystem/trigger.js +1 -1
  63. package/dist/ecosystem/types.d.ts +6 -3
  64. package/dist/ecosystem/types.js +3 -2
  65. package/dist/ecosystem/variable.d.ts +1 -0
  66. package/dist/ecosystem/virtual-user.d.ts +3 -0
  67. package/dist/ecosystem/virtual-user.js +3 -0
  68. package/dist/ecosystem/workflow.d.ts +2 -0
  69. package/dist/ecosystem/workflow.js +2 -0
  70. package/dist/errors/index.d.ts +8 -0
  71. package/dist/errors/index.js +1 -0
  72. package/dist/errors/serialized-error.d.ts +7 -0
  73. package/dist/function/function-meta.types.d.ts +139 -0
  74. package/dist/function/function-runner.d.ts +3 -2
  75. package/dist/function/function-runner.js +31 -11
  76. package/dist/function/functions.types.d.ts +10 -2
  77. package/dist/function/index.d.ts +8 -2
  78. package/dist/function/index.js +4 -1
  79. package/dist/function/pikku-request.d.ts +8 -0
  80. package/dist/function/pikku-request.js +15 -0
  81. package/dist/middleware/auth-apikey.d.ts +2 -10
  82. package/dist/middleware/auth-apikey.js +1 -1
  83. package/dist/middleware/auth-bearer.d.ts +2 -10
  84. package/dist/middleware/auth-bearer.js +1 -1
  85. package/dist/middleware/auth-cookie.d.ts +2 -10
  86. package/dist/middleware/auth-cookie.js +1 -1
  87. package/dist/middleware/cors.d.ts +5 -11
  88. package/dist/middleware/cors.js +13 -6
  89. package/dist/middleware/index.d.ts +3 -0
  90. package/dist/middleware/index.js +1 -0
  91. package/dist/middleware/middleware-factories.d.ts +9 -0
  92. package/dist/middleware/middleware-factories.js +19 -0
  93. package/dist/middleware/middleware.types.d.ts +27 -0
  94. package/dist/middleware/middleware.types.js +1 -0
  95. package/dist/middleware/remote-auth.d.ts +2 -2
  96. package/dist/middleware/remote-auth.js +1 -1
  97. package/dist/middleware/telemetry.d.ts +4 -20
  98. package/dist/middleware/telemetry.js +1 -1
  99. package/dist/middleware-runner.d.ts +2 -1
  100. package/dist/pikku-state.d.ts +2 -1
  101. package/dist/services/audit-service.d.ts +1 -1
  102. package/dist/services/credential-wire-service.js +1 -1
  103. package/dist/services/email-service.d.ts +1 -1
  104. package/dist/services/file-scenario-run-store.d.ts +58 -0
  105. package/dist/services/file-scenario-run-store.js +214 -0
  106. package/dist/services/in-memory-workflow-service.d.ts +1 -1
  107. package/dist/services/index.d.ts +5 -1
  108. package/dist/services/index.js +3 -0
  109. package/dist/services/local-secrets.d.ts +1 -1
  110. package/dist/services/local-secrets.js +1 -1
  111. package/dist/services/logger.d.ts +7 -7
  112. package/dist/services/meta-service.d.ts +2 -1
  113. package/dist/services/scoped-secret-service.d.ts +1 -1
  114. package/dist/services/secret-service.d.ts +1 -1
  115. package/dist/services/typed-secret-service.d.ts +16 -2
  116. package/dist/services/typed-secret-service.js +5 -0
  117. package/dist/services/webhook-service.d.ts +1 -1
  118. package/dist/services/workflow-service.d.ts +2 -1
  119. package/dist/testing/index.d.ts +1 -0
  120. package/dist/testing/index.js +1 -0
  121. package/dist/types/core.types.d.ts +30 -180
  122. package/dist/types/core.types.js +1 -19
  123. package/dist/types/index.d.ts +2 -0
  124. package/dist/types/index.js +1 -0
  125. package/dist/types/state.types.d.ts +3 -1
  126. package/dist/utils.d.ts +13 -0
  127. package/dist/utils.js +2 -0
  128. package/dist/wirings/addon/addon-runner.d.ts +17 -0
  129. package/dist/wirings/addon/addon-runner.js +150 -0
  130. package/dist/wirings/addon/index.d.ts +6 -0
  131. package/dist/wirings/addon/index.js +3 -0
  132. package/dist/wirings/addon/remote-addon-auth.d.ts +16 -0
  133. package/dist/wirings/addon/remote-addon-auth.js +39 -0
  134. package/dist/wirings/addon/wire-addon.d.ts +80 -0
  135. package/dist/wirings/addon/wire-addon.js +120 -0
  136. package/dist/wirings/addon/wire-remote-addon.d.ts +22 -0
  137. package/dist/wirings/addon/wire-remote-addon.js +11 -0
  138. package/dist/wirings/agent/agent-memory.d.ts +11 -0
  139. package/dist/wirings/agent/agent-memory.js +17 -2
  140. package/dist/wirings/agent/agent-prepare.d.ts +3 -3
  141. package/dist/wirings/agent/agent-prepare.js +1 -1
  142. package/dist/wirings/agent/agent-runner.js +2 -3
  143. package/dist/wirings/agent/agent-stream.js +117 -93
  144. package/dist/wirings/agent/agent-utils.d.ts +13 -0
  145. package/dist/wirings/agent/agent-utils.js +16 -0
  146. package/dist/wirings/agent/agent.types.d.ts +12 -10
  147. package/dist/wirings/agent/index.d.ts +1 -1
  148. package/dist/wirings/agent/voice-input.js +1 -1
  149. package/dist/wirings/agent/voice-output.js +1 -1
  150. package/dist/wirings/agent-scorer/agent-scorer-judge.d.ts +13 -0
  151. package/dist/wirings/agent-scorer/agent-scorer-judge.js +52 -0
  152. package/dist/wirings/agent-scorer/agent-scorer.d.ts +7 -1
  153. package/dist/wirings/agent-scorer/agent-scorer.js +1 -0
  154. package/dist/wirings/agent-scorer/agent-scorer.types.d.ts +20 -0
  155. package/dist/wirings/agent-scorer/index.d.ts +1 -1
  156. package/dist/wirings/channel/channel-common.d.ts +2 -1
  157. package/dist/wirings/channel/channel-middleware-runner.d.ts +2 -1
  158. package/dist/wirings/channel/channel.types.d.ts +1 -1
  159. package/dist/wirings/channel/serverless/index.d.ts +1 -0
  160. package/dist/wirings/cli/channel/index.d.ts +1 -1
  161. package/dist/wirings/cli/cli-runner.d.ts +2 -1
  162. package/dist/wirings/cli/cli-runner.js +4 -1
  163. package/dist/wirings/cli/cli.types.d.ts +21 -2
  164. package/dist/wirings/cli/command-parser.js +157 -23
  165. package/dist/wirings/gateway/gateway.types.d.ts +2 -1
  166. package/dist/wirings/http/http-runner.d.ts +1 -1
  167. package/dist/wirings/http/http-runner.js +1 -1
  168. package/dist/wirings/http/http.types.d.ts +2 -2
  169. package/dist/wirings/http/index.d.ts +1 -0
  170. package/dist/wirings/http/index.js +1 -0
  171. package/dist/wirings/http/pikku-fetch-http-request.js +1 -1
  172. package/dist/wirings/http/pikku-fetch-http-response.js +2 -2
  173. package/dist/wirings/mcp/mcp.types.d.ts +1 -1
  174. package/dist/wirings/persona/index.d.ts +1 -1
  175. package/dist/wirings/persona/index.js +1 -1
  176. package/dist/wirings/persona/persona.types.d.ts +14 -0
  177. package/dist/wirings/queue/queue.types.d.ts +1 -1
  178. package/dist/wirings/queue/signed-queue-service.d.ts +1 -1
  179. package/dist/wirings/rpc/index.d.ts +0 -6
  180. package/dist/wirings/rpc/index.js +0 -3
  181. package/dist/wirings/rpc/remote-addon-auth.d.ts +1 -1
  182. package/dist/wirings/rpc/remote-addon-auth.js +1 -1
  183. package/dist/wirings/rpc/rpc-runner.js +2 -2
  184. package/dist/wirings/rpc/wire-addon.d.ts +1 -1
  185. package/dist/wirings/scheduler/scheduler.types.d.ts +2 -1
  186. package/dist/wirings/scope/index.d.ts +1 -0
  187. package/dist/wirings/scope/index.js +1 -0
  188. package/dist/wirings/secret/secret.types.d.ts +10 -0
  189. package/dist/wirings/secret/validate-secret-definitions.js +2 -0
  190. package/dist/wirings/variable/validate-variable-definitions.js +2 -0
  191. package/dist/wirings/variable/variable.types.d.ts +15 -0
  192. package/dist/wirings/virtual-user/index.d.ts +1 -1
  193. package/dist/wirings/virtual-user/index.js +1 -1
  194. package/dist/wirings/virtual-user/prepare-virtual-user-run.d.ts +1 -1
  195. package/dist/wirings/virtual-user/virtual-user-derive.d.ts +1 -1
  196. package/dist/wirings/workflow/graph/graph-node.d.ts +9 -2
  197. package/dist/wirings/workflow/graph/graph-node.js +2 -0
  198. package/dist/wirings/workflow/graph/graph-runner.d.ts +1 -0
  199. package/dist/wirings/workflow/graph/graph-runner.js +283 -31
  200. package/dist/wirings/workflow/graph/workflow-graph.types.d.ts +16 -1
  201. package/dist/wirings/workflow/index.d.ts +2 -0
  202. package/dist/wirings/workflow/index.js +1 -0
  203. package/dist/wirings/workflow/pikku-scenario-service.d.ts +15 -5
  204. package/dist/wirings/workflow/pikku-scenario-service.js +44 -16
  205. package/dist/wirings/workflow/pikku-workflow-service.d.ts +22 -7
  206. package/dist/wirings/workflow/pikku-workflow-service.js +28 -29
  207. package/dist/wirings/workflow/run-timeline.d.ts +1 -1
  208. package/dist/wirings/workflow/scenario-run.types.d.ts +171 -0
  209. package/dist/wirings/workflow/scenario-run.types.js +1 -0
  210. package/dist/wirings/workflow/scenario-step-guards.d.ts +1 -2
  211. package/dist/wirings/workflow/scenario-step-guards.js +0 -8
  212. package/dist/wirings/workflow/scenario-step.types.d.ts +27 -12
  213. package/dist/wirings/workflow/workflow-approval.js +2 -1
  214. package/dist/wirings/workflow/workflow-run-engine.types.d.ts +2 -1
  215. package/dist/wirings/workflow/workflow-step-claim.d.ts +16 -0
  216. package/dist/wirings/workflow/workflow-step-claim.js +27 -0
  217. package/dist/wirings/workflow/workflow.types.d.ts +2 -1
  218. package/knowledge/decisions/index.md +2 -0
  219. package/knowledge/decisions/internals/a-scenario-step-template-is-offered-unfilled.md +1 -1
  220. package/knowledge/decisions/internals/a-secret-that-fails-to-decrypt-fails-the-whole-read.md +1 -1
  221. package/knowledge/decisions/internals/a-virtual-user-run-is-not-a-workflow-and-not-a-queued-job.md +2 -2
  222. package/knowledge/decisions/internals/a-wall-clock-threshold-is-a-load-test-in-disguise.md +6 -6
  223. package/knowledge/decisions/internals/a-workflow-wire-is-built-from-the-run-not-from-the-rpc-service.md +1 -1
  224. package/knowledge/decisions/internals/addon-singleton-services-are-cached-per-namespace-not-per-package.md +1 -1
  225. package/knowledge/decisions/internals/addon-workflow-names-are-prefixed-with-the-consumer-namespace.md +1 -1
  226. package/knowledge/decisions/internals/an-addon-scope-root-loses-to-a-root-the-host-already-declares.md +1 -1
  227. package/knowledge/decisions/internals/channel-message-handlers-accept-three-config-shapes.md +1 -1
  228. package/knowledge/decisions/internals/channel-middleware-caches-only-statically-resolved-middleware.md +1 -1
  229. package/knowledge/decisions/internals/channel-user-id-is-persisted-after-onconnect-middleware-runs.md +1 -1
  230. package/knowledge/decisions/internals/cli-option-names-are-camelcase-in-state-and-kebab-on-the-command-line.md +1 -1
  231. package/knowledge/decisions/internals/cli-parse-errors-are-routed-by-message-prefix.md +1 -1
  232. package/knowledge/decisions/internals/cli-unknown-long-options-warn-instead-of-failing.md +1 -1
  233. package/knowledge/decisions/internals/core-column-form-is-an-axis-of-its-own.md +4 -4
  234. package/knowledge/decisions/internals/core-data-classification-brand-is-an-optional-property.md +1 -1
  235. package/knowledge/decisions/internals/core-function-runner-restores-the-wire-fields-it-overwrites.md +3 -3
  236. package/knowledge/decisions/internals/core-hot-reload-merges-generated-meta-never-replaces-it.md +2 -2
  237. package/knowledge/decisions/internals/core-hot-reload-owns-its-module-registry.md +2 -2
  238. package/knowledge/decisions/internals/core-middleware-order-is-scope-then-priority.md +1 -1
  239. package/knowledge/decisions/internals/core-schema-defaults-apply-on-every-transport.md +1 -1
  240. package/knowledge/decisions/internals/core-scopes-are-an-and-gate-separate-from-permissions.md +3 -3
  241. package/knowledge/decisions/internals/gateway-adapters-resolve-lazily-and-are-promise-cached.md +1 -1
  242. package/knowledge/decisions/internals/gateway-listener-middleware-runs-without-an-rpc-on-the-wire.md +4 -4
  243. package/knowledge/decisions/internals/gateway-wiring-is-a-meta-wiring-over-http-and-channels.md +1 -1
  244. package/knowledge/decisions/internals/http-router-matches-normalized-paths-but-returns-registered-ones.md +1 -1
  245. package/knowledge/decisions/internals/in-a-scenario-a-4xx-is-data-not-an-exception.md +1 -1
  246. package/knowledge/decisions/internals/in-memory-workflow-history-aliases-the-live-step-object.md +1 -1
  247. package/knowledge/decisions/internals/index.md +4 -2
  248. package/knowledge/decisions/internals/istanbul-statement-counts-attach-to-the-start-line-only.md +1 -1
  249. package/knowledge/decisions/internals/one-project-shape-check-two-validators.md +1 -1
  250. package/knowledge/decisions/internals/queue-jobs-always-carry-an-explicit-attempts-count.md +1 -1
  251. package/knowledge/decisions/internals/remote-addons-dispatch-over-http-instead-of-local-meta.md +1 -1
  252. package/knowledge/decisions/internals/rpc-names-resolve-through-package-scope-before-root.md +1 -1
  253. package/knowledge/decisions/internals/scenario-given-and-when-are-sugar-but-then-is-not.md +2 -2
  254. package/knowledge/decisions/internals/scenarios-live-in-files-named-for-them.md +5 -5
  255. package/knowledge/decisions/internals/the-actor-prompt-says-json-because-of-json-object-mode.md +1 -1
  256. package/knowledge/decisions/internals/the-api-report-pins-members-not-just-names.md +1 -1
  257. package/knowledge/decisions/internals/the-ecosystem-entry-point-carries-the-adapter-surface.md +43 -19
  258. package/knowledge/decisions/internals/the-persona-runtime-is-exported-from-the-persona-entry-point.md +1 -1
  259. package/knowledge/decisions/internals/the-worker-disposition-is-the-one-that-is-not-testing.md +1 -1
  260. package/knowledge/decisions/internals/validate-runs-checks-by-precondition.md +3 -3
  261. package/knowledge/decisions/internals/virtual-user-step-order-comes-from-insertion-order.md +1 -1
  262. package/knowledge/decisions/internals/webhook-delivery-history-records-every-attempt-best-effort.md +1 -1
  263. package/knowledge/decisions/internals/wiring-registries-erase-the-generics-their-wire-functions-capture.md +1 -1
  264. package/knowledge/decisions/internals/workflow-approval-expiry-is-decided-from-a-recorded-deadline.md +1 -1
  265. package/knowledge/decisions/internals/workflow-core-never-imports-a-browser-driver.md +1 -1
  266. package/knowledge/decisions/internals/workflow-inline-runs-report-their-run-id-before-they-can-fail.md +1 -1
  267. package/knowledge/decisions/internals/workflow-invocation-id-is-the-dedupe-key-not-step-id.md +1 -1
  268. package/knowledge/decisions/internals/workflow-run-capabilities-are-extensions-not-subclasses.md +1 -1
  269. package/knowledge/decisions/internals/workflow-run-timeline-is-a-pure-fold-over-durable-history.md +1 -1
  270. package/knowledge/decisions/internals/workflow-scenario-assertions-never-retry-and-record-one-step.md +9 -5
  271. package/knowledge/decisions/internals/workflow-scenario-hooks-are-a-scenario-only-affordance.md +1 -1
  272. package/knowledge/decisions/internals/workflow-step-rpc-name-is-provenance-only.md +2 -2
  273. package/knowledge/decisions/internals/workflow-suspend-and-approval-reasons-are-durable-step-identities.md +1 -1
  274. package/knowledge/decisions/security/a-function-never-receives-the-secret-service.md +1 -1
  275. package/knowledge/decisions/security/a-scaffold-flag-says-a-surface-exists-not-who-may-call-it.md +47 -0
  276. package/knowledge/decisions/security/a-step-runs-the-function-the-workflow-dispatched-it-with.md +1 -1
  277. package/knowledge/decisions/security/a-virtual-user-is-never-offered-a-step-that-would-forge-its-own-oracle.md +2 -3
  278. package/knowledge/decisions/security/actor-sign-in-is-proven-by-set-cookie-not-a-non-empty-jar.md +1 -1
  279. package/knowledge/decisions/security/addon-auth-and-tags-only-tighten.md +1 -1
  280. package/knowledge/decisions/security/agent-gate-requires-a-session-only-when-auth-is-true.md +1 -1
  281. package/knowledge/decisions/security/an-approval-answer-outlives-the-run-it-answered.md +3 -3
  282. package/knowledge/decisions/security/console-addon-privileged-functions-gate-themselves.md +4 -3
  283. package/knowledge/decisions/security/core-safe-fetch-blocks-ssrf-by-host-literal-not-dns.md +1 -1
  284. package/knowledge/decisions/security/gateway-middleware-sessions-must-be-bridged-onto-the-wire.md +2 -2
  285. package/knowledge/decisions/security/global-permissions-and-function-permissions-are-independent-gates.md +1 -1
  286. package/knowledge/decisions/security/http-request-bodies-are-bounded-before-they-are-buffered.md +2 -2
  287. package/knowledge/decisions/security/index.md +4 -1
  288. package/knowledge/decisions/security/mcp-internal-error-details-are-double-gated-on-production.md +1 -1
  289. package/knowledge/decisions/security/queue-jobs-carry-the-producers-pikku-user-id.md +3 -3
  290. package/knowledge/decisions/security/remote-addon-tokens-are-client-credentials-not-mesh-trust.md +1 -1
  291. package/knowledge/decisions/security/scaffold-features-are-authenticated-unless-opted-out.md +8 -2
  292. package/knowledge/decisions/security/self-authentication-is-declared-not-detected.md +1 -1
  293. package/knowledge/decisions/security/signed-content-urls-bind-the-request-path.md +2 -2
  294. package/knowledge/index.md +3 -1
  295. package/knowledge/questions/channel-middleware-accepts-bare-factories-that-nothing-resolves.md +1 -1
  296. package/knowledge/questions/index.md +2 -0
  297. package/package.json +14 -13
  298. package/run-tests.sh +0 -0
  299. package/scripts/generate-api-report.d.mts +1 -1
  300. package/scripts/generate-api-report.mjs +67 -60
  301. package/scripts/generate-api-report.mts +11 -45
  302. package/src/api-report.test.ts +70 -1
  303. package/src/app-alias-surface.test.ts +179 -0
  304. package/src/app-leaf-surface.test.ts +217 -0
  305. package/src/bootstrap-compat/root.ts +20 -0
  306. package/src/{column-form.test.ts → classification/column-form.test.ts} +2 -2
  307. package/src/{data-classification.ts → classification/data-classification.ts} +1 -5
  308. package/src/classification/index.ts +38 -0
  309. package/src/{secret-value.test.ts → classification/secret-value.test.ts} +2 -2
  310. package/src/{secret-value.ts → classification/secret-value.ts} +1 -6
  311. package/src/crypto-utils.ts +1 -1
  312. package/src/dev/hot-reload.ts +1 -1
  313. package/src/dev/reload-meta.test.ts +1 -1
  314. package/src/errors/index.ts +9 -0
  315. package/src/errors/serialized-error.ts +9 -0
  316. package/src/factory-functions.test.ts +4 -2
  317. package/src/function/function-meta.types.ts +151 -0
  318. package/src/function/function-runner.test.ts +4 -2
  319. package/src/function/function-runner.ts +47 -14
  320. package/src/function/functions.types.ts +11 -7
  321. package/src/function/index.ts +24 -5
  322. package/src/function/list.types.ts +1 -3
  323. package/src/middleware/auth-apikey.ts +4 -2
  324. package/src/middleware/auth-bearer.test.ts +1 -1
  325. package/src/middleware/auth-bearer.ts +4 -2
  326. package/src/middleware/auth-cookie.ts +4 -1
  327. package/src/middleware/cors.test.ts +126 -5
  328. package/src/middleware/cors.ts +19 -8
  329. package/src/middleware/index.ts +16 -0
  330. package/src/middleware/middleware-factories.ts +62 -0
  331. package/src/middleware/middleware.types.ts +61 -0
  332. package/src/middleware/remote-auth.test.ts +1 -1
  333. package/src/middleware/remote-auth.ts +1 -1
  334. package/src/middleware/telemetry.ts +4 -2
  335. package/src/middleware-runner.test.ts +2 -3
  336. package/src/middleware-runner.ts +2 -2
  337. package/src/no-root-barrel.test.ts +114 -0
  338. package/src/permissions.ts +2 -4
  339. package/src/pikku-state.ts +3 -1
  340. package/src/public-surface.json +56 -241
  341. package/src/public-surface.json.README +3 -3
  342. package/src/public-surface.test.ts +5 -5
  343. package/src/remote.test.ts +1 -1
  344. package/src/removed-legacy-exports.test.ts +5 -1
  345. package/src/services/audit-service.ts +1 -1
  346. package/src/services/credential-wire-service.ts +1 -1
  347. package/src/services/email-service.ts +2 -4
  348. package/src/services/file-scenario-run-store.test.ts +349 -0
  349. package/src/services/file-scenario-run-store.ts +282 -0
  350. package/src/services/in-memory-workflow-service.ts +1 -1
  351. package/src/services/index.ts +13 -1
  352. package/src/services/local-content-request-handler.ts +1 -2
  353. package/src/services/local-secrets.ts +1 -1
  354. package/src/services/logger.ts +7 -7
  355. package/src/services/meta-service.ts +2 -2
  356. package/src/services/queue-webhook-service.test.ts +1 -1
  357. package/src/services/scoped-secret-service.ts +1 -1
  358. package/src/services/secret-service.ts +1 -1
  359. package/src/services/typed-secret-service.test.ts +52 -0
  360. package/src/services/typed-secret-service.ts +26 -2
  361. package/src/services/webhook-service.ts +1 -1
  362. package/src/services/workflow-service.ts +2 -1
  363. package/src/template-alias-surface.test.ts +240 -0
  364. package/src/testing/index.ts +1 -0
  365. package/src/types/core.types.ts +33 -285
  366. package/src/types/index.ts +25 -0
  367. package/src/types/state.types.ts +6 -5
  368. package/src/types/typed-credentials.test.ts +72 -0
  369. package/src/utils.ts +34 -0
  370. package/src/wirings/{rpc → addon}/addon-scopes.test.ts +1 -1
  371. package/src/wirings/addon/index.ts +12 -0
  372. package/src/wirings/{rpc → addon}/remote-addon-auth.ts +4 -1
  373. package/src/wirings/{rpc → addon}/wire-addon.ts +37 -2
  374. package/src/wirings/agent/agent-memory.test.ts +73 -0
  375. package/src/wirings/agent/agent-memory.ts +16 -2
  376. package/src/wirings/agent/agent-middleware.types.test.ts +41 -0
  377. package/src/wirings/agent/agent-prepare.ts +8 -5
  378. package/src/wirings/agent/agent-runner.ts +2 -3
  379. package/src/wirings/agent/agent-stream-delegate.test.ts +381 -0
  380. package/src/wirings/agent/agent-stream.ts +160 -81
  381. package/src/wirings/agent/agent-utils.ts +19 -0
  382. package/src/wirings/agent/agent.types.ts +12 -11
  383. package/src/wirings/agent/index.ts +1 -0
  384. package/src/wirings/agent/voice-input.ts +1 -1
  385. package/src/wirings/agent/voice-output.test.ts +2 -4
  386. package/src/wirings/agent/voice-output.ts +1 -1
  387. package/src/wirings/agent-scorer/agent-scorer-judge.test.ts +145 -0
  388. package/src/wirings/agent-scorer/agent-scorer-judge.ts +60 -0
  389. package/src/wirings/agent-scorer/agent-scorer.ts +8 -0
  390. package/src/wirings/agent-scorer/agent-scorer.types.ts +21 -0
  391. package/src/wirings/agent-scorer/index.ts +1 -0
  392. package/src/wirings/channel/channel-common.ts +4 -2
  393. package/src/wirings/channel/channel-handler.ts +2 -5
  394. package/src/wirings/channel/channel-middleware-runner.ts +1 -1
  395. package/src/wirings/channel/channel-rpc.types.ts +1 -5
  396. package/src/wirings/channel/channel.types.ts +1 -2
  397. package/src/wirings/channel/local/local-channel-runner.test.ts +1 -2
  398. package/src/wirings/channel/serverless/index.ts +1 -0
  399. package/src/wirings/cli/channel/cli-raw-channel-runner.ts +2 -2
  400. package/src/wirings/cli/channel/index.ts +1 -3
  401. package/src/wirings/cli/cli-runner.test.ts +1 -1
  402. package/src/wirings/cli/cli-runner.ts +11 -7
  403. package/src/wirings/cli/cli.types.ts +24 -3
  404. package/src/wirings/cli/command-parser.test.ts +388 -1
  405. package/src/wirings/cli/command-parser.ts +189 -29
  406. package/src/wirings/gateway/gateway-runner.test.ts +1 -2
  407. package/src/wirings/gateway/gateway-runner.ts +3 -6
  408. package/src/wirings/gateway/gateway.types.ts +5 -4
  409. package/src/wirings/http/http-routes.test.ts +1 -2
  410. package/src/wirings/http/http-runner-addon-ref.test.ts +238 -0
  411. package/src/wirings/http/http-runner.test.ts +3 -8
  412. package/src/wirings/http/http-runner.ts +9 -3
  413. package/src/wirings/http/http.types.ts +3 -12
  414. package/src/wirings/http/index.ts +1 -0
  415. package/src/wirings/http/pikku-fetch-http-request.ts +1 -1
  416. package/src/wirings/http/pikku-fetch-http-response.ts +3 -2
  417. package/src/wirings/mcp/mcp-runner.test.ts +1 -1
  418. package/src/wirings/mcp/mcp.types.ts +1 -2
  419. package/src/wirings/persona/index.ts +1 -4
  420. package/src/wirings/persona/persona.types.ts +14 -0
  421. package/src/wirings/queue/queue-identity.test.ts +1 -1
  422. package/src/wirings/queue/queue.types.ts +2 -6
  423. package/src/wirings/queue/signed-queue-service.ts +1 -1
  424. package/src/wirings/rpc/index.ts +0 -12
  425. package/src/wirings/rpc/rpc-runner.test.ts +4 -4
  426. package/src/wirings/rpc/rpc-runner.ts +3 -3
  427. package/src/wirings/scheduler/scheduler.types.ts +2 -5
  428. package/src/wirings/scope/index.ts +1 -0
  429. package/src/wirings/secret/secret.types.ts +10 -0
  430. package/src/wirings/secret/validate-secret-definitions.test.ts +23 -0
  431. package/src/wirings/secret/validate-secret-definitions.ts +2 -0
  432. package/src/wirings/variable/validate-variable-definitions.test.ts +24 -0
  433. package/src/wirings/variable/validate-variable-definitions.ts +2 -0
  434. package/src/wirings/variable/variable.types.ts +15 -0
  435. package/src/wirings/virtual-user/index.ts +1 -4
  436. package/src/wirings/virtual-user/prepare-virtual-user-run.ts +1 -1
  437. package/src/wirings/virtual-user/virtual-user-derive.test.ts +1 -1
  438. package/src/wirings/virtual-user/virtual-user-derive.ts +1 -1
  439. package/src/wirings/virtual-user/virtual-user.types.ts +1 -5
  440. package/src/wirings/workflow/graph/graph-foreach-queued.test.ts +569 -0
  441. package/src/wirings/workflow/graph/graph-foreach.test.ts +626 -0
  442. package/src/wirings/workflow/graph/graph-node.ts +16 -1
  443. package/src/wirings/workflow/graph/graph-runner.ts +387 -61
  444. package/src/wirings/workflow/graph/workflow-graph.types.ts +28 -3
  445. package/src/wirings/workflow/index.ts +8 -0
  446. package/src/wirings/workflow/pikku-scenario-service.ts +83 -20
  447. package/src/wirings/workflow/pikku-workflow-service.ts +37 -35
  448. package/src/wirings/workflow/run-timeline.ts +1 -1
  449. package/src/wirings/workflow/scenario-expectations.test.ts +28 -13
  450. package/src/wirings/workflow/scenario-run.types.ts +186 -0
  451. package/src/wirings/workflow/scenario-step-guards.ts +1 -14
  452. package/src/wirings/workflow/scenario-step.test.ts +32 -7
  453. package/src/wirings/workflow/scenario-step.types.ts +27 -12
  454. package/src/wirings/workflow/workflow-approval-policy.test.ts +117 -47
  455. package/src/wirings/workflow/workflow-approval.ts +2 -1
  456. package/src/wirings/workflow/workflow-run-engine.types.ts +2 -1
  457. package/src/wirings/workflow/workflow-step-claim.ts +46 -0
  458. package/src/wirings/workflow/workflow.types.ts +4 -12
  459. package/src/wirings-stay-decoupled.test.ts +6 -3
  460. package/tsconfig.tsbuildinfo +1 -1
  461. package/tsconfig.type-tests.json +4 -2
  462. package/dist/ecosystem/ai-agent.d.ts +0 -18
  463. package/dist/ecosystem/ai-agent.js +0 -6
  464. package/dist/ecosystem/ai-scorer.d.ts +0 -12
  465. package/dist/ecosystem/ai-scorer.js +0 -5
  466. package/dist/services/ai-agent-runner-service.d.ts +0 -220
  467. package/dist/services/ai-run-state-service.d.ts +0 -29
  468. package/dist/services/ai-storage-service.d.ts +0 -18
  469. package/dist/services/in-memory-ai-run-state-service.d.ts +0 -19
  470. package/dist/services/in-memory-ai-run-state-service.js +0 -54
  471. package/dist/testing/service-tests/ai-storage-service-tests.d.ts +0 -3
  472. package/dist/testing/service-tests/ai-storage-service-tests.js +0 -302
  473. package/dist/wirings/ai-agent/agent-rpc.d.ts +0 -15
  474. package/dist/wirings/ai-agent/agent-rpc.js +0 -53
  475. package/dist/wirings/ai-agent/ai-agent-agui.d.ts +0 -79
  476. package/dist/wirings/ai-agent/ai-agent-agui.js +0 -327
  477. package/dist/wirings/ai-agent/ai-agent-finalize.d.ts +0 -58
  478. package/dist/wirings/ai-agent/ai-agent-finalize.js +0 -138
  479. package/dist/wirings/ai-agent/ai-agent-helpers.d.ts +0 -35
  480. package/dist/wirings/ai-agent/ai-agent-helpers.js +0 -47
  481. package/dist/wirings/ai-agent/ai-agent-interrupt.d.ts +0 -153
  482. package/dist/wirings/ai-agent/ai-agent-interrupt.js +0 -257
  483. package/dist/wirings/ai-agent/ai-agent-memory.d.ts +0 -42
  484. package/dist/wirings/ai-agent/ai-agent-memory.js +0 -333
  485. package/dist/wirings/ai-agent/ai-agent-model-config.d.ts +0 -16
  486. package/dist/wirings/ai-agent/ai-agent-model-config.js +0 -51
  487. package/dist/wirings/ai-agent/ai-agent-prepare.d.ts +0 -117
  488. package/dist/wirings/ai-agent/ai-agent-prepare.js +0 -728
  489. package/dist/wirings/ai-agent/ai-agent-registry.d.ts +0 -16
  490. package/dist/wirings/ai-agent/ai-agent-registry.js +0 -51
  491. package/dist/wirings/ai-agent/ai-agent-runner.d.ts +0 -7
  492. package/dist/wirings/ai-agent/ai-agent-runner.js +0 -580
  493. package/dist/wirings/ai-agent/ai-agent-stream.d.ts +0 -41
  494. package/dist/wirings/ai-agent/ai-agent-stream.js +0 -1029
  495. package/dist/wirings/ai-agent/ai-agent-turn.d.ts +0 -57
  496. package/dist/wirings/ai-agent/ai-agent-turn.js +0 -82
  497. package/dist/wirings/ai-agent/ai-agent-utils.d.ts +0 -1
  498. package/dist/wirings/ai-agent/ai-agent-utils.js +0 -1
  499. package/dist/wirings/ai-agent/ai-agent.types.d.ts +0 -625
  500. package/dist/wirings/ai-agent/index.d.ts +0 -12
  501. package/dist/wirings/ai-agent/index.js +0 -10
  502. package/dist/wirings/ai-agent/voice-input.d.ts +0 -69
  503. package/dist/wirings/ai-agent/voice-input.js +0 -150
  504. package/dist/wirings/ai-agent/voice-output.d.ts +0 -85
  505. package/dist/wirings/ai-agent/voice-output.js +0 -196
  506. package/dist/wirings/ai-scorer/ai-scorer-grade.d.ts +0 -26
  507. package/dist/wirings/ai-scorer/ai-scorer-grade.js +0 -33
  508. package/dist/wirings/ai-scorer/ai-scorer-judge.d.ts +0 -17
  509. package/dist/wirings/ai-scorer/ai-scorer-judge.js +0 -92
  510. package/dist/wirings/ai-scorer/ai-scorer-live.d.ts +0 -15
  511. package/dist/wirings/ai-scorer/ai-scorer-live.js +0 -38
  512. package/dist/wirings/ai-scorer/ai-scorer-registry.d.ts +0 -18
  513. package/dist/wirings/ai-scorer/ai-scorer-registry.js +0 -46
  514. package/dist/wirings/ai-scorer/ai-scorer-sampling.d.ts +0 -8
  515. package/dist/wirings/ai-scorer/ai-scorer-sampling.js +0 -31
  516. package/dist/wirings/ai-scorer/ai-scorer-snapshots.d.ts +0 -10
  517. package/dist/wirings/ai-scorer/ai-scorer-snapshots.js +0 -40
  518. package/dist/wirings/ai-scorer/ai-scorer-worker.d.ts +0 -15
  519. package/dist/wirings/ai-scorer/ai-scorer-worker.js +0 -58
  520. package/dist/wirings/ai-scorer/ai-scorer.d.ts +0 -39
  521. package/dist/wirings/ai-scorer/ai-scorer.js +0 -40
  522. package/dist/wirings/ai-scorer/ai-scorer.types.d.ts +0 -90
  523. package/dist/wirings/ai-scorer/ai-scorer.types.js +0 -4
  524. package/dist/wirings/ai-scorer/index.d.ts +0 -6
  525. package/dist/wirings/ai-scorer/index.js +0 -5
  526. package/src/ecosystem/agent-scorer.ts +0 -24
  527. package/src/ecosystem/agent.ts +0 -42
  528. package/src/ecosystem/channel/local.ts +0 -1
  529. package/src/ecosystem/channel.ts +0 -22
  530. package/src/ecosystem/cli/channel.ts +0 -25
  531. package/src/ecosystem/cli.ts +0 -21
  532. package/src/ecosystem/credential.ts +0 -10
  533. package/src/ecosystem/dev.ts +0 -12
  534. package/src/ecosystem/function.ts +0 -28
  535. package/src/ecosystem/gateway.ts +0 -4
  536. package/src/ecosystem/hmac.ts +0 -1
  537. package/src/ecosystem/http.ts +0 -22
  538. package/src/ecosystem/mcp.ts +0 -17
  539. package/src/ecosystem/middleware.ts +0 -27
  540. package/src/ecosystem/node-host-resolver.ts +0 -1
  541. package/src/ecosystem/node.ts +0 -5
  542. package/src/ecosystem/oauth2.ts +0 -4
  543. package/src/ecosystem/persona.ts +0 -30
  544. package/src/ecosystem/queue.ts +0 -23
  545. package/src/ecosystem/remote.ts +0 -9
  546. package/src/ecosystem/role.ts +0 -13
  547. package/src/ecosystem/rpc.ts +0 -7
  548. package/src/ecosystem/safe-fetch.ts +0 -8
  549. package/src/ecosystem/scenario.ts +0 -32
  550. package/src/ecosystem/scheduler.ts +0 -5
  551. package/src/ecosystem/schema.ts +0 -1
  552. package/src/ecosystem/scope.ts +0 -14
  553. package/src/ecosystem/secret.ts +0 -15
  554. package/src/ecosystem/services/istanbul-coverage.ts +0 -1
  555. package/src/ecosystem/services/local-content-request-handler.ts +0 -16
  556. package/src/ecosystem/services/local-content.ts +0 -1
  557. package/src/ecosystem/services/v8-coverage.ts +0 -1
  558. package/src/ecosystem/services.ts +0 -82
  559. package/src/ecosystem/trigger.ts +0 -7
  560. package/src/ecosystem/types.ts +0 -34
  561. package/src/ecosystem/variable.ts +0 -14
  562. package/src/ecosystem/virtual-user.ts +0 -48
  563. package/src/ecosystem/workflow.ts +0 -68
  564. package/src/ecosystem.ts +0 -40
  565. package/src/index.ts +0 -244
  566. /package/dist/{services/ai-agent-runner-service.js → bootstrap-compat/root.js} +0 -0
  567. /package/dist/{services/ai-run-state-service.js → classification/data-classification.js} +0 -0
  568. /package/dist/{services/ai-storage-service.js → errors/serialized-error.js} +0 -0
  569. /package/dist/{wirings/ai-agent/ai-agent.types.js → function/function-meta.types.js} +0 -0
  570. /package/src/{column-form.ts → classification/column-form.ts} +0 -0
  571. /package/src/{pikku-request.test.ts → function/pikku-request.test.ts} +0 -0
  572. /package/src/{pikku-request.ts → function/pikku-request.ts} +0 -0
  573. /package/src/wirings/{rpc → addon}/addon-auth-tags.test.ts +0 -0
  574. /package/src/wirings/{rpc → addon}/addon-runner.ts +0 -0
  575. /package/src/wirings/{rpc → addon}/addon-secrets.test.ts +0 -0
  576. /package/src/wirings/{rpc → addon}/wire-addon.test.ts +0 -0
  577. /package/src/wirings/{rpc → addon}/wire-remote-addon.ts +0 -0
@@ -36,10 +36,57 @@ import type {
36
36
  WorkflowExpectErrorOptions,
37
37
  WorkflowExpectScoreOptions,
38
38
  WorkflowExpectServiceOptions,
39
+ WorkflowStepOptions,
39
40
  } from './dsl/workflow-dsl.types.js'
40
41
 
42
+ /**
43
+ * The retry default for the `expect*` family: none, the same as `given`,
44
+ * `when` and `then` take, and for the same reason — a failed assertion is a
45
+ * result, not a transient fault.
46
+ *
47
+ * Left to the workflow-wide `DEFAULT_STEP_RETRIES` they get six attempts each,
48
+ * which re-runs the RPC behind a failing `expectError`, restarts a whole
49
+ * `expectEventually` poll after it has already spent its own deadline, and
50
+ * lets a live judge re-roll a grade until one lands inside the band. The last
51
+ * of those is how it was found: a judge scored a deliberately useless answer
52
+ * 1, and the scenario went green anyway on the next attempt.
53
+ *
54
+ * A caller that genuinely wants attempts still asks for them.
55
+ */
56
+ const assertionStep = <T extends WorkflowStepOptions>(
57
+ options?: T
58
+ ): T & { retries: number } =>
59
+ ({ ...options, retries: options?.retries ?? 0 }) as T & { retries: number }
60
+
41
61
  export { addFeature, resolveFeatureScenarios } from './feature.js'
42
- export type * from './scenario.types.js'
62
+ export type {
63
+ CoreFeature,
64
+ CoreFeatureScenario,
65
+ FeatureMeta,
66
+ FeaturesMeta,
67
+ PikkuBrowserWire,
68
+ PikkuScenarioWire,
69
+ ScenarioBrowserFailure,
70
+ ScenarioBrowserProvider,
71
+ ScenarioEnvironment,
72
+ ScenarioStepKind,
73
+ ScenarioStepMeta,
74
+ ScenarioStepOptions,
75
+ ScenarioStepPhase,
76
+ ScenarioSurface,
77
+ TestIdSelector,
78
+ } from './scenario.types.js'
79
+ export type {
80
+ ScenarioArtifact,
81
+ ScenarioFailureDetail,
82
+ ScenarioResult,
83
+ ScenarioRunRecord,
84
+ ScenarioRunReport,
85
+ ScenarioRunStatus,
86
+ ScenarioRunStore,
87
+ ScenarioRunSummary,
88
+ ScenarioStepRow,
89
+ } from './scenario-run.types.js'
43
90
  export { SCENARIO_SURFACES } from './scenario-step.types.js'
44
91
 
45
92
  // Which of a step's bindings run: one for an action, every witness for a `then`
@@ -50,7 +97,7 @@ export { createCookieJar } from './scenario-cookie-jar.js'
50
97
  export type { ScenarioCookieJar } from './scenario-cookie-jar.js'
51
98
  export { composeStepProse, renderStepTemplate } from './scenario-prose.js'
52
99
 
53
- export { requireActor, requireScenarioEnv } from './scenario-step-guards.js'
100
+ export { requireScenarioEnv } from './scenario-step-guards.js'
54
101
 
55
102
  export type {
56
103
  ScenarioHttpResponse,
@@ -171,20 +218,24 @@ addError(ScenarioBrowserUnavailable, {
171
218
  })
172
219
 
173
220
  /**
174
- * A browser binding was reached without an actor, so there is no persona whose
175
- * window it would run in.
221
+ * A step that is driven by a persona was dispatched without one.
222
+ *
223
+ * Raised before the body runs rather than partway through it: the step declared
224
+ * that it needs an actor — by having a `browser` binding, which cannot be
225
+ * provisioned without a window to open, or by `actor: true` — so a call site
226
+ * that omits one is a mistake at the call site.
176
227
  */
177
- export class ScenarioBrowserActorRequired extends PikkuError {
228
+ export class ScenarioActorRequired extends PikkuError {
178
229
  constructor(public readonly stepFunc: string) {
179
230
  super(
180
- `[scenario] step '${stepFunc}' resolved to its browser binding but was called without an actor. ` +
181
- `Pass { actor: actors.<name> } so the browser signs in as that persona.`
231
+ `[scenario] step '${stepFunc}' is driven by a persona but was called without an actor. ` +
232
+ `Pass { actor: actors.<name> } so it runs as that persona.`
182
233
  )
183
234
  }
184
235
  }
185
- addError(ScenarioBrowserActorRequired, {
236
+ addError(ScenarioActorRequired, {
186
237
  status: 500,
187
- message: 'A browser binding needs an actor.',
238
+ message: 'This step needs an actor.',
188
239
  })
189
240
 
190
241
  /**
@@ -480,8 +531,7 @@ export class PikkuScenarioService implements WorkflowRunExtension {
480
531
  let wireServices: Record<string, unknown> | undefined
481
532
  try {
482
533
  wireServices = (await createWireServices?.(singletonServices, wire)) as
483
- | Record<string, unknown>
484
- | undefined
534
+ Record<string, unknown> | undefined
485
535
  const services =
486
536
  wireServices && Object.keys(wireServices).length > 0
487
537
  ? { ...singletonServices, ...wireServices }
@@ -602,7 +652,7 @@ export class PikkuScenarioService implements WorkflowRunExtension {
602
652
  await new Promise((resolve) => setTimeout(resolve, interval))
603
653
  }
604
654
  },
605
- options
655
+ assertionStep(options)
606
656
  )
607
657
  },
608
658
 
@@ -645,7 +695,7 @@ export class PikkuScenarioService implements WorkflowRunExtension {
645
695
  `[workflow] expectError '${stepName}' ('${resolvedRpcName}') expected an error but the call succeeded: ${JSON.stringify(result)?.slice(0, 300)}`
646
696
  )
647
697
  },
648
- options
698
+ assertionStep(options)
649
699
  )
650
700
  },
651
701
 
@@ -700,7 +750,7 @@ export class PikkuScenarioService implements WorkflowRunExtension {
700
750
  )
701
751
  }
702
752
  },
703
- options
753
+ assertionStep(options)
704
754
  )
705
755
  },
706
756
 
@@ -745,7 +795,7 @@ export class PikkuScenarioService implements WorkflowRunExtension {
745
795
  }
746
796
  return grade
747
797
  },
748
- options
798
+ assertionStep(options)
749
799
  )
750
800
  },
751
801
 
@@ -814,6 +864,10 @@ export class PikkuScenarioService implements WorkflowRunExtension {
814
864
  this.scenarioStepDescription(packageName, resolvedStepFunc) ??
815
865
  stepName
816
866
 
867
+ if (!actor && this.requiresActor(packageName, resolvedStepFunc)) {
868
+ throw new ScenarioActorRequired(resolvedStepFunc)
869
+ }
870
+
817
871
  const declared = this.declaredSurfaces(packageName, resolvedStepFunc)
818
872
  const resolution = resolveScenarioSurfaces(phase, declared, this.runSurface)
819
873
 
@@ -828,13 +882,13 @@ export class PikkuScenarioService implements WorkflowRunExtension {
828
882
  scenario: workflowWire,
829
883
  pikkuUserId: workflowWire.pikkuUserId,
830
884
  actors: this.runActors.get(runId),
885
+ ...(actor ? { actor } : {}),
831
886
  scenarioStep: {
832
887
  name: resolvedStepFunc,
833
888
  stepName,
834
889
  runId,
835
890
  phase,
836
891
  surface,
837
- actor,
838
892
  env: this.scenarioEnvironment,
839
893
  },
840
894
  }
@@ -842,11 +896,10 @@ export class PikkuScenarioService implements WorkflowRunExtension {
842
896
  if (!this.scenarioBrowserProvider) {
843
897
  throw new ScenarioBrowserUnavailable(resolvedStepFunc)
844
898
  }
845
- if (!actor) {
846
- throw new ScenarioBrowserActorRequired(resolvedStepFunc)
847
- }
899
+ // The dispatch guard above already refused an actor-less call, and a
900
+ // browser binding always requires one.
848
901
  wire.browser = await this.scenarioBrowserProvider.sessionFor(
849
- actor.name
902
+ actor!.name
850
903
  )
851
904
  }
852
905
  return await runPikkuFunc(
@@ -947,4 +1000,14 @@ export class PikkuScenarioService implements WorkflowRunExtension {
947
1000
  const surfaces = this.scenarioStepConfig(packageName, stepFunc)?.surfaces
948
1001
  return surfaces?.length ? surfaces : ['default']
949
1002
  }
1003
+
1004
+ /**
1005
+ * Whether a step is driven by a persona, and so must be given one. Stamped by
1006
+ * the definer from a `browser` binding or an explicit `actor: true`.
1007
+ */
1008
+ private requiresActor(packageName: string | null, stepFunc: string): boolean {
1009
+ return (
1010
+ this.scenarioStepConfig(packageName, stepFunc)?.requiresActor === true
1011
+ )
1012
+ }
950
1013
  }
@@ -5,11 +5,8 @@ import {
5
5
  pikkuState,
6
6
  } from '../../pikku-state.js'
7
7
  import { getDurationInMilliseconds } from '../../time-utils.js'
8
- import type {
9
- CoreUserSession,
10
- PikkuRawWire,
11
- SerializedError,
12
- } from '../../types/core.types.js'
8
+ import type { CoreUserSession, PikkuRawWire } from '../../types/core.types.js'
9
+ import type { SerializedError } from '../../errors/serialized-error.js'
13
10
  import type {
14
11
  GroupConcurrencyConfig,
15
12
  JobGroup,
@@ -39,6 +36,7 @@ import {
39
36
  executeGraphStep,
40
37
  runWorkflowGraph,
41
38
  runFromMeta,
39
+ stripInstanceOrdinal,
42
40
  } from './graph/graph-runner.js'
43
41
  import type { WorkflowService } from '../../services/workflow-service.js'
44
42
  import type { ScenarioPersonas } from '../../services/personas-service.js'
@@ -70,7 +68,6 @@ import {
70
68
  WorkflowRunCancelledError,
71
69
  WorkflowRunFailedError,
72
70
  WorkflowRunNotFoundError,
73
- WorkflowStepFunctionMismatchError,
74
71
  WorkflowStepNameNotString,
75
72
  WorkflowSuspendedException,
76
73
  } from './workflow-errors.js'
@@ -97,6 +94,7 @@ import {
97
94
  } from './workflow-approval.js'
98
95
  import { auditApprovalDecision } from './workflow-approval-audit.js'
99
96
  import { recordSuspension, suspendStepNameFor } from './workflow-suspend.js'
97
+ import { claimStepByReadThenWrite } from './workflow-step-claim.js'
100
98
  import {
101
99
  RedispatchBackoff,
102
100
  sweepStalledRuns,
@@ -641,12 +639,12 @@ export abstract class PikkuWorkflowService implements WorkflowService {
641
639
  *
642
640
  * Returns nothing by default so a store that cannot express the query keeps
643
641
  * working unchanged — and, because it does not opt in, gains no re-dispatches
644
- * either. A store must have an atomic `withStepLock` before overriding this,
645
- * or no concurrency for one to exclude: the relay makes duplicate dispatch
646
- * routine, and the claim in `executeWorkflowStepInner` is what keeps a
647
- * duplicate from becoming a second execution. `kysely-postgres` and
648
- * `kysely-mysql` qualify on the lock, `in-memory` on being inline and
649
- * single-process; `mongodb` and `kysely-sqlite` qualify on neither.
642
+ * either. A store must have an atomic `claimStepForExecution` before
643
+ * overriding this, or no concurrency for one to exclude: the relay makes
644
+ * duplicate dispatch routine, and the claim is what keeps a duplicate from
645
+ * becoming a second execution. Every `@pikku/kysely` dialect qualifies on its
646
+ * status-guarded claim, `in-memory` on being inline and single-process;
647
+ * `mongodb` still qualifies on neither.
650
648
  */
651
649
  protected async findUndispatchedSteps(
652
650
  _before: Date,
@@ -1302,6 +1300,29 @@ export abstract class PikkuWorkflowService implements WorkflowService {
1302
1300
  }
1303
1301
  }
1304
1302
 
1303
+ /**
1304
+ * Take sole ownership of a step before it runs, returning the state to run
1305
+ * under — or `null` when another dispatch already owns it.
1306
+ *
1307
+ * Dispatch is at-least-once by design: the relay re-dispatches steps it
1308
+ * believes were dropped, and a queue can redeliver a job it already handed
1309
+ * out. This is the one place that keeps a duplicate dispatch from becoming a
1310
+ * second execution of a side-effecting step, so it is only as strong as the
1311
+ * exclusion it is built on — and `withStepLock` excludes nothing unless the
1312
+ * store backs it with a real primitive. A store able to express the decision
1313
+ * as one conditional write should override this rather than reach for a lock,
1314
+ * which is what `@pikku/kysely` does with a status-guarded `UPDATE`.
1315
+ */
1316
+ protected async claimStepForExecution(
1317
+ runId: string,
1318
+ stepName: string,
1319
+ rpcName: string
1320
+ ): Promise<StepState | null> {
1321
+ return this.withStepLock(runId, stepName, () =>
1322
+ claimStepByReadThenWrite(this, runId, stepName, rpcName)
1323
+ )
1324
+ }
1325
+
1305
1326
  private async executeWorkflowStepInner(
1306
1327
  runId: string,
1307
1328
  stepName: string,
@@ -1309,26 +1330,7 @@ export abstract class PikkuWorkflowService implements WorkflowService {
1309
1330
  data: any,
1310
1331
  rpcService: PikkuRPC
1311
1332
  ): Promise<void> {
1312
- const claimed = await this.withStepLock(runId, stepName, async () => {
1313
- const stepState = await this.getStepState(runId, stepName)
1314
- // knowledge: decisions/security/a-step-runs-the-function-the-workflow-dispatched-it-with.md
1315
- if (
1316
- stepState.rpcName !== undefined &&
1317
- stepState.rpcName !== (rpcName ?? null)
1318
- ) {
1319
- throw new WorkflowStepFunctionMismatchError(runId, stepName)
1320
- }
1321
- if (stepState.status === 'succeeded' || stepState.status === 'running') {
1322
- return null
1323
- }
1324
- if (stepState.status === 'failed') {
1325
- return this.createRetryAttempt(stepState.stepId, 'running')
1326
- }
1327
- if (stepState.status === 'pending' || stepState.status === 'scheduled') {
1328
- await this.setStepRunning(stepState.stepId)
1329
- }
1330
- return stepState
1331
- })
1333
+ const claimed = await this.claimStepForExecution(runId, stepName, rpcName)
1332
1334
 
1333
1335
  if (!claimed) {
1334
1336
  return
@@ -1352,9 +1354,9 @@ export abstract class PikkuWorkflowService implements WorkflowService {
1352
1354
  if (stepName in workflowMeta.nodes) {
1353
1355
  graphNodeId = stepName
1354
1356
  } else {
1355
- const hash = stepName.lastIndexOf('#')
1356
- const base = hash > 0 ? stepName.slice(0, hash) : undefined
1357
- if (base && base in workflowMeta.nodes) graphNodeId = base
1357
+ const base = stripInstanceOrdinal(stepName)
1358
+ if (base !== stepName && base in workflowMeta.nodes)
1359
+ graphNodeId = base
1358
1360
  }
1359
1361
  }
1360
1362
  if (graphNodeId) {
@@ -1,4 +1,4 @@
1
- import type { SerializedError } from '../../types/core.types.js'
1
+ import type { SerializedError } from '../../errors/serialized-error.js'
2
2
  import type { StepState, StepStatus } from './workflow.types.js'
3
3
 
4
4
  export interface RunTimelineEvent {
@@ -65,7 +65,7 @@ describe('workflow.expectError', () => {
65
65
  })
66
66
 
67
67
  await assert.rejects(
68
- wire.expectError('should have failed', 'deleteTodo', {}, { retries: 0 }),
68
+ wire.expectError('should have failed', 'deleteTodo', {}),
69
69
  /expected an error but the call succeeded/
70
70
  )
71
71
  })
@@ -82,7 +82,7 @@ describe('workflow.expectError', () => {
82
82
  'wrong reason',
83
83
  'deleteTodo',
84
84
  {},
85
- { matches: /Forbidden/, retries: 0 }
85
+ { matches: /Forbidden/ }
86
86
  ),
87
87
  /did not match .*database offline/
88
88
  )
@@ -114,10 +114,7 @@ describe('workflow.expectService', () => {
114
114
  const { wire } = await scenarioWire(oneEmail())
115
115
 
116
116
  await assert.rejects(
117
- wire.expectService('two emails', 'email.send', {
118
- times: 2,
119
- retries: 0,
120
- }),
117
+ wire.expectService('two emails', 'email.send', { times: 2 }),
121
118
  /expected 2 call\(s\).*found 1.*email\.send/s
122
119
  )
123
120
  })
@@ -131,7 +128,6 @@ describe('workflow.expectService', () => {
131
128
  await assert.rejects(
132
129
  wire.expectService('emailed someone else', 'email.send', {
133
130
  calledWith: { to: 'x@y.z' },
134
- retries: 0,
135
131
  }),
136
132
  /found 0/
137
133
  )
@@ -172,7 +168,7 @@ describe('workflow.expectScore', () => {
172
168
  const { wire } = await scenarioWire(grades({ score: 0 }))
173
169
 
174
170
  await assert.rejects(
175
- wire.expectScore('was brief', 'run-1', 'brevity', { retries: 0 }),
171
+ wire.expectScore('was brief', 'run-1', 'brevity'),
176
172
  /expected 'brevity' to grade run run-1 at least 0\.5, got 0/
177
173
  )
178
174
  })
@@ -183,10 +179,7 @@ describe('workflow.expectScore', () => {
183
179
  )
184
180
 
185
181
  await assert.rejects(
186
- wire.expectScore('was brief', 'run-1', 'brevity', {
187
- atLeast: 0.8,
188
- retries: 0,
189
- }),
182
+ wire.expectScore('was brief', 'run-1', 'brevity', { atLeast: 0.8 }),
190
183
  /It rambled for four paragraphs/
191
184
  )
192
185
  })
@@ -198,12 +191,34 @@ describe('workflow.expectScore', () => {
198
191
  wire.expectScore('was not sycophantic', 'run-1', 'sycophancy', {
199
192
  atLeast: 0,
200
193
  atMost: 0.3,
201
- retries: 0,
202
194
  }),
203
195
  /between 0 and 0\.3, got 0\.95/
204
196
  )
205
197
  })
206
198
 
199
+ test('a grade that misses the bound is not re-rolled until it lands', async () => {
200
+ let graded = 0
201
+ const { wire } = await scenarioWire({
202
+ rpcWithWire: async () => {
203
+ graded += 1
204
+ return { score: 1 }
205
+ },
206
+ })
207
+
208
+ await assert.rejects(
209
+ wire.expectScore('was not sycophantic', 'run-1', 'sycophancy', {
210
+ atLeast: 0,
211
+ atMost: 0.3,
212
+ }),
213
+ /got 1/
214
+ )
215
+
216
+ // The whole point of a judge is that it is not deterministic. Six attempts
217
+ // at it is six chances for one to land inside the band, which grades the
218
+ // sampling rather than the run.
219
+ assert.equal(graded, 1)
220
+ })
221
+
207
222
  test('a reference answer reaches the grader', async () => {
208
223
  let seen: any
209
224
  const { wire } = await scenarioWire({
@@ -0,0 +1,186 @@
1
+ /**
2
+ * What one `pikku scenario run` leaves behind.
3
+ *
4
+ * A run is history: the scenarios it selected, the sentences they were made of,
5
+ * and the images and footage they produced. The sentences are snapshotted into
6
+ * the record rather than looked up later, because a scenario is code and the
7
+ * code moves — a run from last week describes a suite that no longer exists,
8
+ * and re-deriving its ladder from today's source would quietly rewrite it.
9
+ *
10
+ * Everything here is plain serialisable data. That is what lets one record be
11
+ * a JSON file on disk in OSS and a row in Postgres on a hosted project, read
12
+ * back by the same console screen either way.
13
+ */
14
+ import type { ScenarioBrowserFailure } from './scenario-step.types.js'
15
+
16
+ /** What a run filed, in the order it filed it. */
17
+ export type ScenarioArtifactKind = 'screenshot' | 'failure' | 'video'
18
+
19
+ /**
20
+ * One image or recording a scenario produced.
21
+ *
22
+ * `path` is relative to the run's artifact root and always uses forward
23
+ * slashes: it is a content key, not a filesystem path. Locally it resolves
24
+ * under the run's directory; on a hosted project it is the object key in a
25
+ * bucket, and the same record works unchanged.
26
+ */
27
+ export interface ScenarioArtifact {
28
+ /** The scenario label this belongs to, as the report names it. */
29
+ scenario: string
30
+ kind: ScenarioArtifactKind
31
+ path: string
32
+ /** Whose window it was — a two-actor scenario produces two of everything. */
33
+ actor?: string
34
+ /** The caption the scenario author took a screenshot under. */
35
+ name?: string
36
+ }
37
+
38
+ /** One step of a run, already joined to the prose that declared it. */
39
+ export interface ScenarioStepRow {
40
+ sentence: string
41
+ status: string
42
+ durationMs?: number
43
+ error?: string
44
+ }
45
+
46
+ /** Everything known about why one scenario failed. */
47
+ export interface ScenarioFailureDetail {
48
+ /** The rendered sentence of the failing step; absent if no step failed. */
49
+ sentence?: string
50
+ message: string
51
+ stack?: string
52
+ /**
53
+ * True when the failure was a deliberate one (a PikkuError). Its message is
54
+ * the whole story, so the stack is noise.
55
+ */
56
+ expected?: boolean
57
+ browser?: ScenarioBrowserFailure[]
58
+ }
59
+
60
+ export interface ScenarioResult {
61
+ name: string
62
+ status: 'passed' | 'failed'
63
+ durationMs: number
64
+ output?: unknown
65
+ error?: string
66
+ steps?: ScenarioStepRow[]
67
+ failure?: ScenarioFailureDetail
68
+ /** The scenario registration this ran, which the label alone does not give. */
69
+ scenarioName?: string
70
+ /** The feature that grouped it, when one did. */
71
+ feature?: string
72
+ tags?: string[]
73
+ /** Images and footage this scenario produced, filed under the run. */
74
+ artifacts?: ScenarioArtifact[]
75
+ }
76
+
77
+ /** One scenario that was not run, and why. */
78
+ export interface ScenarioSkip {
79
+ name: string
80
+ reason: string
81
+ }
82
+
83
+ export interface ScenarioRunReport {
84
+ environment: string
85
+ results: ScenarioResult[]
86
+ /**
87
+ * Scenarios not run at all, each carrying why. A skip is only useful if the
88
+ * reader can tell a browser scenario held back by `--no-browser` from one the
89
+ * project itself quarantined, so the reason travels with the name rather than
90
+ * being assumed by the formatter.
91
+ */
92
+ skipped: ScenarioSkip[]
93
+ /** Feature-level hook failures, which belong to no single scenario. */
94
+ hookFailures: string[]
95
+ }
96
+
97
+ export type ScenarioRunStatus = 'running' | 'passed' | 'failed'
98
+
99
+ /**
100
+ * A whole run, as it is stored and read back.
101
+ *
102
+ * `status` is `running` from the moment the run is created until it finishes,
103
+ * so a console watching a run in progress can tell "still going" from "nothing
104
+ * came back" — and a run killed halfway stays `running` forever, which is the
105
+ * honest record of what happened to it.
106
+ */
107
+ export interface ScenarioRunRecord extends ScenarioRunReport {
108
+ runId: string
109
+ status: ScenarioRunStatus
110
+ /** The surface the run targeted: `default`, `browser`, … */
111
+ surface: string
112
+ startedAt: string
113
+ finishedAt?: string
114
+ }
115
+
116
+ /** Enough of a run to list it without loading its steps and stacks. */
117
+ export interface ScenarioRunSummary {
118
+ runId: string
119
+ environment: string
120
+ surface: string
121
+ status: ScenarioRunStatus
122
+ startedAt: string
123
+ finishedAt?: string
124
+ durationMs?: number
125
+ passed: number
126
+ failed: number
127
+ skipped: number
128
+ /** Whether anything is there to watch, so a list can say so without a second read. */
129
+ artifacts: number
130
+ }
131
+
132
+ /**
133
+ * Where runs are kept.
134
+ *
135
+ * Deliberately not the workflow service: a scenario run is a workflow run under
136
+ * the hood, but its history has a different lifetime, a different audience, and
137
+ * a different home — a JSON file beside the project locally, a project-scoped
138
+ * table and an object store when hosted.
139
+ *
140
+ * Written progressively rather than at the end, so a suite that dies on its
141
+ * fortieth scenario still leaves the thirty-nine behind.
142
+ */
143
+ export interface ScenarioRunStore {
144
+ /** Open a run. Called before the first scenario, with `status: 'running'`. */
145
+ start(record: ScenarioRunRecord): Promise<void>
146
+ /** Append one finished scenario to an open run. */
147
+ recordScenario(runId: string, result: ScenarioResult): Promise<void>
148
+ /**
149
+ * File the run's artifacts against the scenarios that produced them.
150
+ *
151
+ * Separate from `recordScenario` because a scenario's video does not exist
152
+ * when the scenario ends — it is finalised by the next scenario's reset, and
153
+ * renamed again when the run closes. Artifacts are matched to a result by the
154
+ * label the report knows it under.
155
+ */
156
+ attachArtifacts(runId: string, artifacts: ScenarioArtifact[]): Promise<void>
157
+ /** Close a run, settling everything only known once every scenario has run. */
158
+ finish(
159
+ runId: string,
160
+ outcome: Pick<
161
+ ScenarioRunRecord,
162
+ 'status' | 'finishedAt' | 'skipped' | 'hookFailures'
163
+ >
164
+ ): Promise<void>
165
+ /**
166
+ * The bytes of one artifact, by the `path` its record carries.
167
+ *
168
+ * Reading goes through the store because only the store knows where a run's
169
+ * files actually are — a directory beside the project, or an object store
170
+ * behind a bucket. Absent when the run or the path is unknown, which is also
171
+ * the answer for a path that tries to leave the run.
172
+ *
173
+ * The bytes are backed by their own `ArrayBuffer` so they can be handed
174
+ * straight to a `Response` — a view onto a pooled or shared buffer is not a
175
+ * valid body.
176
+ */
177
+ readArtifact(
178
+ runId: string,
179
+ path: string
180
+ ): Promise<{ body: Uint8Array<ArrayBuffer>; contentType: string } | undefined>
181
+ /** Most recent first. */
182
+ list(options?: { limit?: number }): Promise<ScenarioRunSummary[]>
183
+ get(runId: string): Promise<ScenarioRunRecord | undefined>
184
+ /** Forget a run and everything it filed. */
185
+ remove(runId: string): Promise<void>
186
+ }
@@ -3,21 +3,8 @@ import type {
3
3
  ScenarioEnvironment,
4
4
  } from './scenario-step.types.js'
5
5
 
6
- export const requireActor = <TActor>(
7
- scenarioStep: PikkuScenarioStepWire<TActor> | undefined
8
- ): TActor => {
9
- const actor = scenarioStep?.actor
10
- if (!actor) {
11
- throw new Error(
12
- `[scenario] step '${scenarioStep?.name ?? 'unknown'}' was called without an actor. ` +
13
- `Pass { actor: actors.<name> } so it runs as that persona.`
14
- )
15
- }
16
- return actor
17
- }
18
-
19
6
  export const requireScenarioEnv = (
20
- scenarioStep: PikkuScenarioStepWire<unknown> | undefined
7
+ scenarioStep: PikkuScenarioStepWire | undefined
21
8
  ): ScenarioEnvironment => {
22
9
  const env = scenarioStep?.env
23
10
  if (!env) {