@pikku/core 0.12.141 → 0.12.143

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 (251) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/dist/errors/errors.d.ts +1 -1
  3. package/dist/errors/errors.js +1 -1
  4. package/dist/function/function-meta.types.d.ts +6 -6
  5. package/dist/function/functions.types.d.ts +2 -2
  6. package/dist/services/audit-service.d.ts +4 -4
  7. package/dist/services/feature-flag-service.d.ts +3 -3
  8. package/dist/services/{http-personas.d.ts → http-virtual-users.d.ts} +25 -47
  9. package/dist/services/{http-personas.js → http-virtual-users.js} +29 -53
  10. package/dist/services/in-memory-queue-service.d.ts +8 -1
  11. package/dist/services/in-memory-queue-service.js +67 -0
  12. package/dist/services/incoming-webhook-service.d.ts +2 -0
  13. package/dist/services/incoming-webhook-service.js +4 -0
  14. package/dist/services/index.d.ts +2 -2
  15. package/dist/services/index.js +1 -1
  16. package/dist/services/meta-service.d.ts +6 -6
  17. package/dist/services/meta-service.js +8 -8
  18. package/dist/{wirings/workflow → services}/scenario-cookie-jar.d.ts +2 -0
  19. package/dist/{wirings/workflow → services}/scenario-cookie-jar.js +18 -14
  20. package/dist/services/scheduler-service.d.ts +3 -0
  21. package/dist/services/scheduler-service.js +4 -0
  22. package/dist/services/system-role-guard.d.ts +1 -1
  23. package/dist/services/virtual-user-sign-in.d.ts +52 -0
  24. package/dist/services/virtual-user-sign-in.js +80 -0
  25. package/dist/services/virtual-user-statement.d.ts +31 -0
  26. package/dist/services/virtual-user-statement.js +24 -0
  27. package/dist/services/{personas-service.d.ts → virtual-users-service.d.ts} +25 -23
  28. package/dist/services/{personas-service.js → virtual-users-service.js} +3 -3
  29. package/dist/types/core.types.d.ts +11 -11
  30. package/dist/wirings/queue/index.d.ts +1 -1
  31. package/dist/wirings/queue/queue.types.d.ts +21 -0
  32. package/dist/wirings/queue/signed-queue-service.d.ts +4 -1
  33. package/dist/wirings/queue/signed-queue-service.js +3 -0
  34. package/dist/wirings/role/define-system-role.d.ts +1 -1
  35. package/dist/wirings/role/define-system-role.js +1 -1
  36. package/dist/wirings/scheduler/index.d.ts +1 -1
  37. package/dist/wirings/scheduler/scheduler.types.d.ts +12 -0
  38. package/dist/wirings/{actor-flow/actor-flow.types.d.ts → virtual-user/conversation/conversation.types.d.ts} +5 -5
  39. package/dist/wirings/virtual-user/conversation/index.d.ts +2 -0
  40. package/dist/wirings/virtual-user/conversation/run-conversation.d.ts +36 -0
  41. package/dist/wirings/{actor-flow → virtual-user/conversation}/run-conversation.js +30 -30
  42. package/dist/wirings/virtual-user/define-virtual-users.d.ts +32 -0
  43. package/dist/wirings/{persona/define-personas.js → virtual-user/define-virtual-users.js} +9 -9
  44. package/dist/wirings/virtual-user/index.d.ts +30 -7
  45. package/dist/wirings/virtual-user/index.js +23 -4
  46. package/dist/wirings/virtual-user/prepare-virtual-user-run.d.ts +8 -8
  47. package/dist/wirings/virtual-user/prepare-virtual-user-run.js +9 -9
  48. package/dist/wirings/virtual-user/run-virtual-user.d.ts +6 -6
  49. package/dist/wirings/virtual-user/run-virtual-user.js +10 -10
  50. package/dist/wirings/{persona/validate-personas.d.ts → virtual-user/validate-virtual-users.d.ts} +11 -11
  51. package/dist/wirings/{persona/validate-personas.js → virtual-user/validate-virtual-users.js} +24 -24
  52. package/dist/wirings/virtual-user/virtual-user-agents.d.ts +7 -7
  53. package/dist/wirings/virtual-user/virtual-user-agents.js +2 -2
  54. package/dist/wirings/{persona/persona-app-scopes.d.ts → virtual-user/virtual-user-app-scopes.d.ts} +10 -10
  55. package/dist/wirings/{persona/persona-app-scopes.js → virtual-user/virtual-user-app-scopes.js} +12 -12
  56. package/dist/wirings/virtual-user/virtual-user-catalogue.d.ts +3 -3
  57. package/dist/wirings/virtual-user/virtual-user-catalogue.js +2 -2
  58. package/dist/wirings/{persona/persona.types.d.ts → virtual-user/virtual-user-declaration.types.d.ts} +24 -24
  59. package/dist/wirings/virtual-user/virtual-user-derive.js +13 -7
  60. package/dist/wirings/virtual-user/virtual-user-dispositions.d.ts +1 -1
  61. package/dist/wirings/{persona/persona-email.d.ts → virtual-user/virtual-user-email.d.ts} +11 -11
  62. package/dist/wirings/{persona/persona-email.js → virtual-user/virtual-user-email.js} +19 -19
  63. package/dist/wirings/{persona/persona-environments.d.ts → virtual-user/virtual-user-environments.d.ts} +10 -10
  64. package/dist/wirings/{persona/persona-environments.js → virtual-user/virtual-user-environments.js} +21 -21
  65. package/dist/wirings/virtual-user/virtual-user-intents.d.ts +3 -3
  66. package/dist/wirings/virtual-user/virtual-user-intents.js +3 -3
  67. package/dist/wirings/{persona/persona-mailbox.d.ts → virtual-user/virtual-user-mailbox.d.ts} +4 -4
  68. package/dist/wirings/virtual-user/virtual-user-run-store.d.ts +4 -4
  69. package/dist/wirings/virtual-user/virtual-user-scaffold.d.ts +42 -61
  70. package/dist/wirings/virtual-user/virtual-user-scaffold.js +69 -117
  71. package/dist/wirings/virtual-user/virtual-user-schedule-store.d.ts +15 -15
  72. package/dist/wirings/virtual-user/virtual-user-schedule.d.ts +6 -6
  73. package/dist/wirings/virtual-user/virtual-user-schedule.js +11 -11
  74. package/dist/wirings/virtual-user/virtual-user-target.d.ts +6 -6
  75. package/dist/wirings/virtual-user/virtual-user-target.js +5 -5
  76. package/dist/wirings/virtual-user/virtual-user.types.d.ts +11 -11
  77. package/dist/wirings/virtual-user/virtual-user.types.js +1 -1
  78. package/dist/wirings/workflow/dsl/workflow-dsl.types.d.ts +9 -9
  79. package/dist/wirings/workflow/pikku-scenario-service.d.ts +28 -28
  80. package/dist/wirings/workflow/pikku-scenario-service.js +77 -77
  81. package/dist/wirings/workflow/pikku-workflow-service.d.ts +2 -2
  82. package/dist/wirings/workflow/pikku-workflow-service.js +4 -4
  83. package/dist/wirings/workflow/scenario-prose.d.ts +8 -8
  84. package/dist/wirings/workflow/scenario-prose.js +12 -8
  85. package/dist/wirings/workflow/scenario-run.types.d.ts +13 -13
  86. package/dist/wirings/workflow/scenario-step.types.d.ts +23 -23
  87. package/dist/wirings/workflow/workflow.types.d.ts +2 -2
  88. package/knowledge/decisions/index.md +0 -2
  89. package/knowledge/decisions/internals/a-credential-name-declared-twice-must-agree-on-schema-and-type.md +29 -0
  90. package/knowledge/decisions/internals/a-grant-can-name-a-scope-declared-without-its-addon.md +29 -0
  91. package/knowledge/decisions/internals/a-latency-budget-is-asserted-by-its-cause.md +32 -0
  92. package/knowledge/decisions/internals/a-scheduled-task-writes-its-caller-session-before-it-runs.md +22 -0
  93. package/knowledge/decisions/internals/a-secret-set-in-memory-shadows-the-variable-of-the-same-name.md +28 -0
  94. package/knowledge/decisions/internals/a-virtual-user-acts-through-invokeraw-so-a-refusal-is-data.md +32 -0
  95. package/knowledge/decisions/internals/a-virtual-user-cadence-is-a-row-not-a-timer.md +12 -12
  96. package/knowledge/decisions/internals/a-virtual-user-converse-signs-in-before-its-first-turn.md +24 -0
  97. package/knowledge/decisions/internals/a-virtual-user-email-is-computed-with-the-run-id-as-a-sub-address.md +34 -0
  98. package/knowledge/decisions/internals/a-virtual-user-run-is-not-a-workflow-but-it-needs-a-trigger.md +2 -2
  99. package/knowledge/decisions/internals/a-wall-clock-threshold-is-a-load-test-in-disguise.md +3 -0
  100. package/knowledge/decisions/internals/an-agent-is-offered-under-its-registry-key-and-only-within-the-virtual-users-scopes.md +27 -0
  101. package/knowledge/decisions/internals/an-oauth2-credential-implies-an-optional-app-secret.md +26 -0
  102. package/knowledge/decisions/internals/analytics-identity-resolvers-run-in-declared-order.md +22 -0
  103. package/knowledge/decisions/internals/feature-flag-declarations-refuse-an-empty-anyof-and-conflicting-scopes.md +22 -0
  104. package/knowledge/decisions/internals/feature-flags-fail-open-and-never-enforce-access.md +28 -0
  105. package/knowledge/decisions/internals/flag-sync-rewrites-only-the-declared-text.md +24 -0
  106. package/knowledge/decisions/internals/in-a-scenario-a-4xx-is-data-not-an-exception.md +3 -3
  107. package/knowledge/decisions/internals/index.md +47 -31
  108. package/knowledge/decisions/internals/one-project-shape-check-two-validators.md +10 -10
  109. package/knowledge/decisions/internals/role-names-cannot-contain-the-scope-separator.md +30 -0
  110. package/knowledge/decisions/internals/scenario-agent-calls-sign-in-on-401-only.md +23 -19
  111. package/knowledge/decisions/internals/scenario-steps-return-drained-response-records.md +2 -2
  112. package/knowledge/decisions/internals/scenarios-live-in-files-named-for-them.md +12 -12
  113. package/knowledge/decisions/internals/the-core-scope-validator-refuses-separator-wildcard-and-empty-segments.md +22 -0
  114. package/knowledge/decisions/internals/the-pikku-user-id-resolves-in-a-fixed-order.md +24 -0
  115. package/knowledge/decisions/internals/{the-actor-prompt-says-json-because-of-json-object-mode.md → the-virtual-user-prompt-says-json-because-of-json-object-mode.md} +4 -4
  116. package/knowledge/decisions/internals/{the-persona-runtime-is-exported-from-the-persona-entry-point.md → the-virtual-user-runtime-is-exported-from-the-virtual-user-entry-point.md} +8 -8
  117. package/knowledge/decisions/internals/validate-checks-virtual-users-through-a-shared-module.md +43 -0
  118. package/knowledge/decisions/internals/{actor-flow-drives-the-target-through-a-transport-seam.md → virtual-user-conversation-drives-the-target-through-a-transport-seam.md} +6 -6
  119. package/knowledge/decisions/internals/virtual-user-conversation-verdicts-are-llm-self-evaluations.md +25 -0
  120. package/knowledge/decisions/internals/virtual-user-conversations-seed-a-hidden-kickoff-message.md +23 -0
  121. package/knowledge/decisions/internals/virtual-users-are-declared-in-one-definevirtualusers-call-per-codebase.md +30 -0
  122. package/knowledge/decisions/internals/workflow-core-never-imports-a-browser-driver.md +5 -5
  123. package/knowledge/decisions/internals/workflow-run-capabilities-are-extensions-not-subclasses.md +2 -2
  124. package/knowledge/decisions/internals/workflow-scenario-assertions-never-retry-and-record-one-step.md +4 -4
  125. package/knowledge/decisions/internals/workflow-scenario-hooks-are-a-scenario-only-affordance.md +1 -1
  126. package/knowledge/decisions/internals/workflow-scenario-prose-is-rendered-from-typed-calls-not-parsed-from-english.md +2 -2
  127. package/knowledge/decisions/internals/workflow-scenario-step-targets-are-string-literals-for-the-inspector.md +1 -1
  128. package/knowledge/decisions/internals/workflow-suspended-runs-keep-their-in-process-context.md +1 -1
  129. package/knowledge/decisions/security/a-bearer-token-checked-against-a-secret-reads-it-per-request.md +26 -0
  130. package/knowledge/decisions/security/a-call-without-a-session-raises-missing-session-not-forbidden.md +20 -0
  131. package/knowledge/decisions/security/a-caller-cookie-wins-over-the-jar-for-the-same-name.md +23 -0
  132. package/knowledge/decisions/security/a-channel-lifecycle-acts-as-the-socket-upgrade-session.md +16 -0
  133. package/knowledge/decisions/security/a-channel-requires-a-session-unless-it-says-auth-false.md +26 -0
  134. package/knowledge/decisions/security/a-credential-read-path-follows-its-declared-type-not-absence.md +32 -0
  135. package/knowledge/decisions/security/a-credential-type-is-a-literal-the-inspector-checks-at-declaration.md +27 -0
  136. package/knowledge/decisions/security/a-first-party-cookie-is-written-only-after-its-purposes-consent.md +33 -0
  137. package/knowledge/decisions/security/a-readonly-session-reaches-only-readonly-functions.md +25 -0
  138. package/knowledge/decisions/security/a-scaffold-flag-says-a-surface-exists-not-who-may-call-it.md +10 -5
  139. package/knowledge/decisions/security/a-scheduled-virtual-user-run-needs-no-admin-at-run-time.md +27 -0
  140. package/knowledge/decisions/security/a-scoped-credential-view-is-limited-to-declared-names.md +26 -0
  141. package/knowledge/decisions/security/a-scoped-secret-view-reads-only-declared-keys-and-never-writes.md +25 -0
  142. package/knowledge/decisions/security/a-secret-is-sent-only-to-the-hosts-it-declares.md +29 -0
  143. package/knowledge/decisions/security/a-step-can-name-an-identity-no-virtual-user-holds-through-per-call-headers.md +25 -0
  144. package/knowledge/decisions/security/a-stopped-run-by-id-skips-the-ownership-check.md +24 -0
  145. package/knowledge/decisions/security/a-vault-secret-redacts-on-serialization-and-throws-on-coercion.md +29 -0
  146. package/knowledge/decisions/security/a-virtual-user-catalogue-entry-is-filtered-by-scope-and-approval-is-denied-by-default.md +30 -0
  147. package/knowledge/decisions/security/a-virtual-user-environment-fails-closed.md +38 -0
  148. package/knowledge/decisions/security/a-virtual-user-never-runs-against-production.md +23 -0
  149. package/knowledge/decisions/security/a-virtual-user-reads-its-roles-from-the-apps-own-rpc-before-better-auth.md +27 -0
  150. package/knowledge/decisions/security/a-virtual-user-run-treats-an-unresolved-environment-as-production.md +41 -0
  151. package/knowledge/decisions/security/a-virtual-user-scopes-are-expanded-from-its-roles-once-at-run-preparation.md +25 -0
  152. package/knowledge/decisions/security/a-virtual-user-whose-roles-drift-from-the-declaration-refuses-to-run.md +32 -0
  153. package/knowledge/decisions/security/addon-screen-scopes-hide-and-do-not-enforce.md +20 -0
  154. package/knowledge/decisions/security/an-addon-header-can-only-narrow-the-session.md +26 -0
  155. package/knowledge/decisions/security/an-addon-instance-reads-only-the-secrets-and-credentials-it-is-named.md +33 -0
  156. package/knowledge/decisions/security/an-addon-may-reach-only-itself-and-the-addons-it-uses.md +16 -0
  157. package/knowledge/decisions/security/an-api-key-is-jwt-decoded-into-the-session.md +26 -0
  158. package/knowledge/decisions/security/an-external-virtual-user-caller-presents-one-signed-statement.md +38 -0
  159. package/knowledge/decisions/security/an-inbound-webhook-source-dispatches-only-verified-requests.md +31 -0
  160. package/knowledge/decisions/security/an-mcp-call-with-no-http-request-stays-anonymous.md +18 -0
  161. package/knowledge/decisions/security/an-omitted-user-id-addresses-the-platform-credential.md +32 -0
  162. package/knowledge/decisions/security/analytics-identity-is-stamped-from-the-session-not-the-event.md +24 -0
  163. package/knowledge/decisions/security/app-access-is-a-scope-tree-synthesised-from-virtual-users.md +26 -0
  164. package/knowledge/decisions/security/approval-policy-decides-who-answers-a-tool-call.md +27 -0
  165. package/knowledge/decisions/security/auth-false-on-a-session-function-warns-and-still-requires-one.md +25 -0
  166. package/knowledge/decisions/security/bearer-token-hashes-are-unpeppered-sha256.md +18 -0
  167. package/knowledge/decisions/security/consent-is-read-from-a-cookie-and-a-recognised-denial-counts-as-refusal.md +29 -0
  168. package/knowledge/decisions/security/cors-defaults-to-any-origin-and-refuses-the-wildcard-with-credentials.md +22 -0
  169. package/knowledge/decisions/security/credential-backends-share-one-conformance-suite-for-per-user-isolation.md +29 -0
  170. package/knowledge/decisions/security/each-stage-has-one-virtual-user-key-pair.md +27 -0
  171. package/knowledge/decisions/security/hmac-verification-fails-closed-on-empty-inputs.md +25 -0
  172. package/knowledge/decisions/security/index.md +88 -24
  173. package/knowledge/decisions/security/local-content-refuses-private-buckets-under-the-public-segment.md +21 -0
  174. package/knowledge/decisions/security/mail-is-allowlisted-before-a-virtual-user-run-sees-it.md +38 -0
  175. package/knowledge/decisions/security/mcp-resources-tools-and-prompts-take-sessionless-functions.md +18 -0
  176. package/knowledge/decisions/security/origin-locked-routes-match-the-origin-exactly.md +22 -0
  177. package/knowledge/decisions/security/oss-auth-works-without-pikku-studio-cloud.md +26 -0
  178. package/knowledge/decisions/security/outbound-webhooks-use-an-allowlist-when-configured.md +24 -0
  179. package/knowledge/decisions/security/pikku-carries-virtual-user-scopes-as-data-and-the-app-grants-them.md +35 -0
  180. package/knowledge/decisions/security/scoped-content-access-is-a-root-folder-with-longest-prefix-grants.md +31 -0
  181. package/knowledge/decisions/security/services-without-secrets-get-a-throwing-secrets-accessor.md +22 -0
  182. package/knowledge/decisions/security/shared-guards-refuse-to-mutate-or-shadow-a-system-role.md +34 -0
  183. package/knowledge/decisions/security/signed-uploads-are-verified-by-the-same-check-as-signed-reads.md +26 -0
  184. package/knowledge/decisions/security/system-role-scopes-must-be-declared-with-define-scope.md +24 -0
  185. package/knowledge/decisions/security/system-roles-are-additive-and-immutable.md +16 -0
  186. package/knowledge/decisions/security/the-channel-rpc-does-not-send-the-callers-session.md +21 -0
  187. package/knowledge/decisions/security/the-remote-jobs-inbox-fails-closed-without-a-secret.md +24 -0
  188. package/knowledge/decisions/security/the-remote-rpc-gate-fails-closed-and-binds-the-token-to-its-function.md +34 -0
  189. package/knowledge/decisions/security/the-workflow-status-stream-rechecks-ownership-on-every-poll.md +16 -0
  190. package/knowledge/decisions/security/two-credentials-on-one-key-must-share-a-read-mode.md +29 -0
  191. package/knowledge/decisions/security/verified-bearer-claims-are-pass-through-data-that-core-never-fills.md +23 -0
  192. package/knowledge/decisions/security/virtual-user-catalogue-excludes-the-machinery-that-runs-virtual-users.md +25 -0
  193. package/knowledge/decisions/security/{actor-flow-missing-approval-decisions-default-to-denied.md → virtual-user-conversation-missing-approval-decisions-default-to-denied.md} +7 -7
  194. package/knowledge/decisions/security/virtual-user-run-records-are-gated-behind-a-read-scope.md +18 -0
  195. package/knowledge/decisions/security/virtual-user-sign-in-does-not-exist-in-production.md +25 -0
  196. package/knowledge/decisions/security/virtual-user-sign-in-has-one-door.md +42 -0
  197. package/knowledge/decisions/security/{actor-sign-in-is-proven-by-set-cookie-not-a-non-empty-jar.md → virtual-user-sign-in-is-proven-by-set-cookie-not-a-non-empty-jar.md} +10 -8
  198. package/knowledge/decisions/security/virtual-user-sign-in-only-works-for-virtual-user-flagged-users.md +29 -0
  199. package/knowledge/decisions/security/workflow-helper-functions-are-sessionless-by-declaration.md +18 -0
  200. package/knowledge/decisions/security/{workflow-scenario-sessions-are-isolated-per-actor-and-per-scenario.md → workflow-scenario-sessions-are-isolated-per-virtual-user-and-per-scenario.md} +6 -6
  201. package/knowledge/decisions/security/workflow-scenario-steps-are-never-network-invocable.md +5 -5
  202. package/knowledge/decisions/security/{workflow-actor-steps-always-use-the-real-transport.md → workflow-virtual-user-steps-always-use-the-real-transport.md} +11 -11
  203. package/knowledge/decisions/security/writing-a-virtual-user-schedule-is-gated-by-its-own-scope.md +18 -0
  204. package/knowledge/index.md +0 -2
  205. package/knowledge/questions/a-bearer-jwt-with-no-identity-becomes-a-session.md +24 -0
  206. package/knowledge/questions/dev-quick-login-as-impersonation.md +25 -0
  207. package/knowledge/questions/does-a-delayed-rpc-run-as-the-session-it-was-scheduled-with.md +26 -0
  208. package/knowledge/questions/does-a-user-id-with-a-colon-read-another-users-credentials.md +27 -0
  209. package/knowledge/questions/does-a-wirecli-command-need-a-session-by-default.md +26 -0
  210. package/knowledge/questions/does-an-adversarial-in-scope-success-count-as-a-finding.md +26 -0
  211. package/knowledge/questions/does-approve-step-need-the-run-owners-session-or-the-gates-approvers.md +34 -0
  212. package/knowledge/questions/does-cors-origin-true-reflect-any-origin-with-credentials.md +20 -0
  213. package/knowledge/questions/does-skip-user-session-do-anything-on-a-channel.md +24 -0
  214. package/knowledge/questions/does-the-store-session-match-the-other-two.md +19 -0
  215. package/knowledge/questions/index.md +25 -2
  216. package/knowledge/questions/is-the-agent-score-worker-reachable-without-a-session.md +21 -0
  217. package/knowledge/questions/live-scheduled-tests-may-write.md +19 -0
  218. package/knowledge/questions/provision-virtual-users-has-no-caller-in-this-repo.md +23 -0
  219. package/knowledge/questions/remote-secret-refuses-requests-without-a-remote-token-on-every-path.md +32 -0
  220. package/knowledge/questions/should-a-middleware-set-session-persist-to-the-store.md +25 -0
  221. package/knowledge/questions/should-a-scoped-secret-view-throw-from-has-secret-for-a-disallowed-key.md +23 -0
  222. package/knowledge/questions/should-a-wrong-bearer-token-be-anonymous-or-refused.md +35 -0
  223. package/knowledge/questions/should-an-empty-webhook-signing-secret-send-unsigned.md +23 -0
  224. package/knowledge/questions/should-http-virtual-user-sessions-reset-between-scenarios-in-one-run.md +27 -0
  225. package/knowledge/questions/should-the-cookie-jar-scope-cookies-to-the-api-origin.md +26 -0
  226. package/knowledge/questions/should-voice-input-fetch-audio-urls-without-a-host-allowlist.md +20 -0
  227. package/knowledge/questions/staging-impersonation-rules.md +27 -0
  228. package/knowledge/questions/virtual-user-keys-on-cloud-stages-are-pikku-studios-to-build.md +33 -0
  229. package/knowledge/questions/virtual-user-statement-replay-is-per-process.md +20 -0
  230. package/knowledge/questions/what-an-impersonated-session-carries.md +22 -0
  231. package/package.json +2 -3
  232. package/src/public-surface.json +38 -47
  233. package/dist/services/persona-actor-secret.d.ts +0 -38
  234. package/dist/services/persona-actor-secret.js +0 -39
  235. package/dist/services/persona-sign-in.d.ts +0 -117
  236. package/dist/services/persona-sign-in.js +0 -154
  237. package/dist/wirings/actor-flow/index.d.ts +0 -2
  238. package/dist/wirings/actor-flow/run-conversation.d.ts +0 -36
  239. package/dist/wirings/persona/define-personas.d.ts +0 -32
  240. package/dist/wirings/persona/index.d.ts +0 -23
  241. package/dist/wirings/persona/index.js +0 -19
  242. package/knowledge/decisions/internals/actor-flow-conversations-seed-a-hidden-kickoff-message.md +0 -23
  243. package/knowledge/decisions/internals/actor-flow-verdicts-are-llm-self-evaluations.md +0 -25
  244. package/knowledge/decisions/internals/validate-checks-personas-through-a-shared-module.md +0 -43
  245. package/knowledge/decisions/security/actor-sign-in-only-works-for-actor-flagged-users.md +0 -31
  246. package/knowledge/decisions/security/an-actor-credential-is-derived-per-persona.md +0 -42
  247. package/knowledge/decisions/security/pikku-carries-actor-scopes-as-data-and-the-app-grants-them.md +0 -26
  248. /package/dist/wirings/{actor-flow/actor-flow.types.js → virtual-user/conversation/conversation.types.js} +0 -0
  249. /package/dist/wirings/{actor-flow → virtual-user/conversation}/index.js +0 -0
  250. /package/dist/wirings/{persona/persona.types.js → virtual-user/virtual-user-declaration.types.js} +0 -0
  251. /package/dist/wirings/{persona/persona-mailbox.js → virtual-user/virtual-user-mailbox.js} +0 -0
@@ -7,7 +7,7 @@ import type { CLIMeta } from '../wirings/cli/cli.types.js';
7
7
  import type { MCPResourceMeta, MCPToolMeta, MCPPromptMeta } from '../wirings/mcp/mcp.types.js';
8
8
  import type { WorkflowsMeta } from '../wirings/workflow/workflow.types.js';
9
9
  import type { FeaturesMeta } from '../wirings/workflow/scenario.types.js';
10
- import type { ResolvedPersona } from './personas-service.js';
10
+ import type { ResolvedVirtualUser } from './virtual-users-service.js';
11
11
  import type { SystemRoleDefinitionsMeta } from '../wirings/role/role.types.js';
12
12
  import type { FeatureFlagDefinitionsMeta } from '../wirings/flag/flag.types.js';
13
13
  import type { ScopeDefinitionsMeta } from '../wirings/scope/scope.types.js';
@@ -147,12 +147,12 @@ export interface MetaService {
147
147
  getGatewayMeta(): Promise<GatewaysMeta>;
148
148
  getRpcMeta(): Promise<RPCMetaRecord>;
149
149
  getWorkflowMeta(): Promise<WorkflowsMeta>;
150
- getPersonasMeta(): Promise<Record<string, ResolvedPersona>>;
150
+ getVirtualUsersMeta(): Promise<Record<string, ResolvedVirtualUser>>;
151
151
  /**
152
152
  * The roles declared with `defineSystemRole`, keyed by name.
153
153
  *
154
- * A persona declares roles; a function checks scopes. Anything showing what a
155
- * persona can reach — the console's persona page, a run's catalogue — needs
154
+ * A virtual user declares roles; a function checks scopes. Anything showing what a
155
+ * virtual user can reach — the console's virtual user page, a run's catalogue — needs
156
156
  * this to get from one to the other, and needs it to be the same expansion the
157
157
  * seed granted from rather than a second one.
158
158
  */
@@ -206,7 +206,7 @@ export declare class LocalMetaService implements MetaService {
206
206
  private gatewayMetaCache;
207
207
  private rpcMetaCache;
208
208
  private workflowMetaCache;
209
- private personasMetaCache;
209
+ private virtualUsersMetaCache;
210
210
  private systemRolesMetaCache;
211
211
  private scopesMetaCache;
212
212
  private featureFlagsMetaCache;
@@ -244,7 +244,7 @@ export declare class LocalMetaService implements MetaService {
244
244
  getRpcMeta(): Promise<RPCMetaRecord>;
245
245
  private readWorkflowMetaDir;
246
246
  getWorkflowMeta(): Promise<WorkflowsMeta>;
247
- getPersonasMeta(): Promise<Record<string, ResolvedPersona>>;
247
+ getVirtualUsersMeta(): Promise<Record<string, ResolvedVirtualUser>>;
248
248
  getSystemRolesMeta(): Promise<SystemRoleDefinitionsMeta>;
249
249
  getScopesMeta(): Promise<ScopeDefinitionsMeta>;
250
250
  getFeatureFlagsMeta(): Promise<FeatureFlagDefinitionsMeta>;
@@ -14,7 +14,7 @@ export class LocalMetaService {
14
14
  gatewayMetaCache = null;
15
15
  rpcMetaCache = null;
16
16
  workflowMetaCache = null;
17
- personasMetaCache = null;
17
+ virtualUsersMetaCache = null;
18
18
  systemRolesMetaCache = null;
19
19
  scopesMetaCache = null;
20
20
  featureFlagsMetaCache = null;
@@ -114,7 +114,7 @@ export class LocalMetaService {
114
114
  this.gatewayMetaCache = null;
115
115
  this.rpcMetaCache = null;
116
116
  this.workflowMetaCache = null;
117
- this.personasMetaCache = null;
117
+ this.virtualUsersMetaCache = null;
118
118
  this.systemRolesMetaCache = null;
119
119
  this.scopesMetaCache = null;
120
120
  this.featureFlagsMetaCache = null;
@@ -265,12 +265,12 @@ export class LocalMetaService {
265
265
  return this.workflowMetaCache;
266
266
  }
267
267
  }
268
- async getPersonasMeta() {
269
- if (this.personasMetaCache)
270
- return this.personasMetaCache;
271
- const content = await this.readFile('workflow/personas.gen.json');
272
- this.personasMetaCache = content ? JSON.parse(content) : {};
273
- return this.personasMetaCache;
268
+ async getVirtualUsersMeta() {
269
+ if (this.virtualUsersMetaCache)
270
+ return this.virtualUsersMetaCache;
271
+ const content = await this.readFile('workflow/virtual-users.gen.json');
272
+ this.virtualUsersMetaCache = content ? JSON.parse(content) : {};
273
+ return this.virtualUsersMetaCache;
274
274
  }
275
275
  async getSystemRolesMeta() {
276
276
  if (this.systemRolesMetaCache)
@@ -1,6 +1,8 @@
1
1
  export interface ScenarioCookieJar {
2
2
  fetch: typeof fetch;
3
3
  clear(): void;
4
+ /** Take in raw `Set-Cookie` values from a response this jar did not fetch. */
5
+ absorb(setCookies: readonly string[]): void;
4
6
  readonly empty: boolean;
5
7
  }
6
8
  /**
@@ -7,7 +7,24 @@
7
7
  export const createCookieJar = (apiUrl) => {
8
8
  const jar = new Map();
9
9
  const origin = new URL(apiUrl).origin;
10
+ const absorb = (setCookies) => {
11
+ for (const raw of setCookies) {
12
+ const [pair] = raw.split(';');
13
+ const separator = pair.indexOf('=');
14
+ if (separator > 0) {
15
+ const name = pair.slice(0, separator);
16
+ const value = pair.slice(separator + 1);
17
+ if (value) {
18
+ jar.set(name, value);
19
+ }
20
+ else {
21
+ jar.delete(name);
22
+ }
23
+ }
24
+ }
25
+ };
10
26
  return {
27
+ absorb,
11
28
  fetch: async (input, init) => {
12
29
  const headers = new Headers(init?.headers);
13
30
  headers.set('origin', origin);
@@ -26,20 +43,7 @@ export const createCookieJar = (apiUrl) => {
26
43
  headers.set('cookie', [caller, ...held].filter(Boolean).join('; '));
27
44
  }
28
45
  const response = await fetch(input, { ...init, headers });
29
- for (const raw of response.headers.getSetCookie()) {
30
- const [pair] = raw.split(';');
31
- const separator = pair.indexOf('=');
32
- if (separator > 0) {
33
- const name = pair.slice(0, separator);
34
- const value = pair.slice(separator + 1);
35
- if (value) {
36
- jar.set(name, value);
37
- }
38
- else {
39
- jar.delete(name);
40
- }
41
- }
42
- }
46
+ absorb(response.headers.getSetCookie());
43
47
  return response;
44
48
  },
45
49
  clear: () => jar.clear(),
@@ -1,4 +1,5 @@
1
1
  import type { CoreUserSession } from '../types/core.types.js';
2
+ import type { ScheduledTaskRuns } from '../wirings/scheduler/scheduler.types.js';
2
3
  export interface ScheduledTaskSummary {
3
4
  taskId: string;
4
5
  rpcName: string;
@@ -17,6 +18,8 @@ export declare abstract class SchedulerService {
17
18
  abstract getTask(taskId: string): Promise<ScheduledTaskInfo | null>;
18
19
  abstract getAllTasks(): Promise<ScheduledTaskSummary[]>;
19
20
  abstract close(): Promise<void>;
21
+ /** Per wired cron task. Empty when this scheduler keeps no run history. */
22
+ getTaskRuns(): Promise<Record<string, ScheduledTaskRuns>>;
20
23
  start(): Promise<void>;
21
24
  stop(): Promise<void>;
22
25
  }
@@ -1,4 +1,8 @@
1
1
  export class SchedulerService {
2
+ /** Per wired cron task. Empty when this scheduler keeps no run history. */
3
+ async getTaskRuns() {
4
+ return {};
5
+ }
2
6
  async start() { }
3
7
  async stop() { }
4
8
  }
@@ -5,7 +5,7 @@ import type { Role } from './scope-service.js';
5
5
  *
6
6
  * Shared rather than left to each implementation because the rule is a
7
7
  * contract, not a policy: a store that lets the console delete `buyer` makes
8
- * every persona and scenario written against `buyer` silently stop testing
8
+ * every virtual user and scenario written against `buyer` silently stop testing
9
9
  * what it claims to, and nothing fails at the point of the mistake. Two
10
10
  * implementations disagreeing about that would be worse than either rule.
11
11
  *
@@ -0,0 +1,52 @@
1
+ import type { ResolvedVirtualUser } from './virtual-users-service.js';
2
+ import type { ScenarioCookieJar } from './scenario-cookie-jar.js';
3
+ /**
4
+ * How a virtual user obtains a session on the target. Both implementations end
5
+ * in the app's one virtual user sign-in handler; they differ in how they get
6
+ * there. See docs/virtual-user-sign-in.md.
7
+ */
8
+ export interface VirtualUserSignIn {
9
+ /**
10
+ * Establish a session in `jar`. Throws on failure with a message naming the
11
+ * virtual user, since a run that continues unauthenticated fails later and
12
+ * somewhere less informative.
13
+ */
14
+ login(jar: ScenarioCookieJar, virtualUser: ResolvedVirtualUser): Promise<void>;
15
+ }
16
+ /**
17
+ * The auth mount a configured sign-in path sits under, or `undefined` when it
18
+ * names nothing recognisable.
19
+ *
20
+ * better-auth serves sign-in and `get-session` from one prefix, so an app that
21
+ * mounts it at `/api/auth` moves both together and says so once through
22
+ * `signInPath`. Reading `get-session` from a hardcoded `/auth` on such an app
23
+ * 404s, and a 404 reads as "this stage does not report roles", which silently
24
+ * turns off the check that tells a permissions finding from seed drift.
25
+ */
26
+ export declare const authMount: (signInPath?: string) => string | undefined;
27
+ /** Signs one statement naming `virtualUser`, for an audience the signer knows. */
28
+ export type VirtualUserStatementMint = (virtualUser: ResolvedVirtualUser) => string | Promise<string>;
29
+ export interface StatementSignInOptions {
30
+ apiUrl: string;
31
+ /** Default `/auth/sign-in/virtual-user`. */
32
+ signInPath?: string;
33
+ mint: VirtualUserStatementMint;
34
+ }
35
+ /**
36
+ * Sign in from outside the app by presenting a signed statement.
37
+ *
38
+ * A fresh statement is minted per login, because each is single-use: the re-login
39
+ * after a 401 must not replay the first.
40
+ */
41
+ export declare const statementSignIn: ({ apiUrl, signInPath, mint, }: StatementSignInOptions) => VirtualUserSignIn;
42
+ /** The part of an auth instance an in-process sign-in needs. */
43
+ export interface InProcessAuth {
44
+ api: Record<string, any>;
45
+ }
46
+ /**
47
+ * Sign in from inside the app, with no credential: the code asking is the app.
48
+ *
49
+ * Calls the server-only endpoint through `auth.api`, which the HTTP router
50
+ * never serves, and takes the session cookie from the response.
51
+ */
52
+ export declare const inProcessSignIn: (auth: InProcessAuth) => VirtualUserSignIn;
@@ -0,0 +1,80 @@
1
+ import { PikkuError } from '../errors/error-handler.js';
2
+ import { VIRTUAL_USER_IN_PROCESS_ENDPOINT } from './virtual-user-statement.js';
3
+ /**
4
+ * A sign-in the target refused. `PikkuError`, not `Error`, so the CLI prints
5
+ * this message alone: a rejected statement or a virtual user the stage has never
6
+ * seen is something to go and fix, and a stack trace through the fetch internals
7
+ * only buries the status and the body that say which one it is.
8
+ */
9
+ const failed = async (what, virtualUserId, res) => {
10
+ const body = (await res.text().catch(() => '')).slice(0, 300);
11
+ return new PikkuError(`[scenario] ${what} failed for '${virtualUserId}' (${res.status}): ${body}`);
12
+ };
13
+ /**
14
+ * The auth mount a configured sign-in path sits under, or `undefined` when it
15
+ * names nothing recognisable.
16
+ *
17
+ * better-auth serves sign-in and `get-session` from one prefix, so an app that
18
+ * mounts it at `/api/auth` moves both together and says so once through
19
+ * `signInPath`. Reading `get-session` from a hardcoded `/auth` on such an app
20
+ * 404s, and a 404 reads as "this stage does not report roles", which silently
21
+ * turns off the check that tells a permissions finding from seed drift.
22
+ */
23
+ export const authMount = (signInPath) => {
24
+ const mount = signInPath ? signInPath.lastIndexOf('/sign-in/') : -1;
25
+ return !signInPath || mount === -1 ? undefined : signInPath.slice(0, mount);
26
+ };
27
+ /**
28
+ * Sign in from outside the app by presenting a signed statement.
29
+ *
30
+ * A fresh statement is minted per login, because each is single-use: the re-login
31
+ * after a 401 must not replay the first.
32
+ */
33
+ export const statementSignIn = ({ apiUrl, signInPath = '/auth/sign-in/virtual-user', mint, }) => ({
34
+ async login(jar, virtualUser) {
35
+ const res = await jar.fetch(`${apiUrl}${signInPath}`, {
36
+ method: 'POST',
37
+ headers: { 'content-type': 'application/json' },
38
+ body: JSON.stringify({ statement: await mint(virtualUser) }),
39
+ });
40
+ if (res.status === 404) {
41
+ throw new PikkuError(`[scenario] ${apiUrl}${signInPath} does not exist, so '${virtualUser.id}' cannot sign in. ` +
42
+ `The target has no VIRTUAL_USER_VERIFY_KEY set, or does not serve virtual user sign-in at all (production never does).`);
43
+ }
44
+ if (!res.ok) {
45
+ throw await failed('virtual user sign-in', virtualUser.id, res);
46
+ }
47
+ // What proves a session was established is this response setting a cookie,
48
+ // not the jar being non-empty: the target may have set one earlier.
49
+ if (res.headers.getSetCookie().length === 0) {
50
+ throw new PikkuError(`[scenario] virtual user sign-in for '${virtualUser.id}' returned no session cookie`);
51
+ }
52
+ },
53
+ });
54
+ /**
55
+ * Sign in from inside the app, with no credential: the code asking is the app.
56
+ *
57
+ * Calls the server-only endpoint through `auth.api`, which the HTTP router
58
+ * never serves, and takes the session cookie from the response.
59
+ */
60
+ export const inProcessSignIn = (auth) => ({
61
+ async login(jar, virtualUser) {
62
+ const endpoint = auth.api[VIRTUAL_USER_IN_PROCESS_ENDPOINT];
63
+ if (typeof endpoint !== 'function') {
64
+ throw new PikkuError(`[scenario] '${virtualUser.id}' cannot sign in: the app's auth has no virtual user sign-in. ` +
65
+ `Add the pikkuVirtualUser plugin from @pikku/better-auth, and note it is only registered under pikku dev or the stage opt-in.`);
66
+ }
67
+ const res = await endpoint({
68
+ body: { email: virtualUser.email, name: virtualUser.name },
69
+ asResponse: true,
70
+ });
71
+ if (!res.ok) {
72
+ throw await failed('virtual user sign-in', virtualUser.id, res);
73
+ }
74
+ const cookies = res.headers.getSetCookie();
75
+ if (cookies.length === 0) {
76
+ throw new PikkuError(`[scenario] virtual user sign-in for '${virtualUser.id}' returned no session cookie`);
77
+ }
78
+ jar.absorb(cookies);
79
+ },
80
+ });
@@ -0,0 +1,31 @@
1
+ /**
2
+ * The signed statement an external caller presents to sign a virtual user in.
3
+ *
4
+ * An EdDSA (Ed25519) JWT: `sub` is the virtual user id, `aud` the app's
5
+ * audience, `exp` a few minutes out and `jti` a one-time id. The app can only
6
+ * verify one, against a public key; whoever deploys holds the private key.
7
+ */
8
+ export declare const VIRTUAL_USER_STATEMENT_ALGORITHM = "EdDSA";
9
+ /** How long a statement lives when its signer does not say. */
10
+ export declare const VIRTUAL_USER_STATEMENT_TTL_SECONDS = 180;
11
+ /** The longest a verifier accepts, whatever `exp` claims. */
12
+ export declare const VIRTUAL_USER_STATEMENT_MAX_TTL_SECONDS = 300;
13
+ /** The variable a stage carries its PUBLIC key in. Not `PIKKU_`/`PIKKUSTUDIO_`: a stage rejects those. */
14
+ export declare const VIRTUAL_USER_VERIFY_KEY_VARIABLE = "VIRTUAL_USER_VERIFY_KEY";
15
+ /** The variable whoever deploys holds the PRIVATE key in. Never set on a stage. */
16
+ export declare const VIRTUAL_USER_SIGNING_KEY_VARIABLE = "VIRTUAL_USER_SIGNING_KEY";
17
+ /** The key a run reaches the in-process sign-in under, on `auth.api`. */
18
+ export declare const VIRTUAL_USER_IN_PROCESS_ENDPOINT = "signInVirtualUserInternal";
19
+ export interface VirtualUserStatementVerifier {
20
+ /**
21
+ * Resolves to the virtual user the statement names. Throws
22
+ * {@link VirtualUserStatementError} for anything that is not a fresh,
23
+ * unreplayed statement for `audience` signed by the private half of the key.
24
+ */
25
+ verify(statement: string, audience: string): Promise<{
26
+ virtualUserId: string;
27
+ }>;
28
+ }
29
+ export declare class VirtualUserStatementError extends Error {
30
+ constructor(message: string);
31
+ }
@@ -0,0 +1,24 @@
1
+ /**
2
+ * The signed statement an external caller presents to sign a virtual user in.
3
+ *
4
+ * An EdDSA (Ed25519) JWT: `sub` is the virtual user id, `aud` the app's
5
+ * audience, `exp` a few minutes out and `jti` a one-time id. The app can only
6
+ * verify one, against a public key; whoever deploys holds the private key.
7
+ */
8
+ export const VIRTUAL_USER_STATEMENT_ALGORITHM = 'EdDSA';
9
+ /** How long a statement lives when its signer does not say. */
10
+ export const VIRTUAL_USER_STATEMENT_TTL_SECONDS = 180;
11
+ /** The longest a verifier accepts, whatever `exp` claims. */
12
+ export const VIRTUAL_USER_STATEMENT_MAX_TTL_SECONDS = 300;
13
+ /** The variable a stage carries its PUBLIC key in. Not `PIKKU_`/`PIKKUSTUDIO_`: a stage rejects those. */
14
+ export const VIRTUAL_USER_VERIFY_KEY_VARIABLE = 'VIRTUAL_USER_VERIFY_KEY';
15
+ /** The variable whoever deploys holds the PRIVATE key in. Never set on a stage. */
16
+ export const VIRTUAL_USER_SIGNING_KEY_VARIABLE = 'VIRTUAL_USER_SIGNING_KEY';
17
+ /** The key a run reaches the in-process sign-in under, on `auth.api`. */
18
+ export const VIRTUAL_USER_IN_PROCESS_ENDPOINT = 'signInVirtualUserInternal';
19
+ export class VirtualUserStatementError extends Error {
20
+ constructor(message) {
21
+ super(message);
22
+ this.name = 'VirtualUserStatementError';
23
+ }
24
+ }
@@ -1,5 +1,5 @@
1
- import type { ConverseOptions, ActorFlowVerdict } from '../wirings/actor-flow/actor-flow.types.js';
2
- import type { PersonaMeta } from '../wirings/persona/persona.types.js';
1
+ import type { ConverseOptions, ConversationVerdict } from '../wirings/virtual-user/conversation/conversation.types.js';
2
+ import type { VirtualUserMeta } from '../wirings/virtual-user/virtual-user-declaration.types.js';
3
3
  /**
4
4
  * What the transport answered, for a step that treats the status as data.
5
5
  *
@@ -30,8 +30,8 @@ export interface ScenarioHttpResponse<T = unknown> {
30
30
  * Drain a response into the shape a step can carry: the parsed body (an empty
31
31
  * one counting as no body at all) alongside the text it was parsed from.
32
32
  *
33
- * `invokeRaw` returns this, and a step that has to reach past a persona — a
34
- * route with no RPC, an identity no persona can hold — reaches for this rather
33
+ * `invokeRaw` returns this, and a step that has to reach past a virtual user — a
34
+ * route with no RPC, an identity no virtual user can hold — reaches for this rather
35
35
  * than writing the same record by hand.
36
36
  *
37
37
  * @example snippet: scenarioPolling
@@ -55,7 +55,7 @@ export interface ScenarioJsonRequest {
55
55
  /**
56
56
  * POST JSON somewhere and report what came back, without throwing on a 4xx/5xx.
57
57
  *
58
- * Every scenario that reaches past a persona was writing this by hand — the same
58
+ * Every scenario that reaches past a virtual user was writing this by hand — the same
59
59
  * `content-type`, the same `JSON.stringify`, the same drain — and the copies had
60
60
  * drifted: some returned `res.json()`, which loses the status and throws
61
61
  * outright when the target answers an empty body or an HTML error page. A
@@ -68,36 +68,38 @@ export declare const postScenarioJson: <T = unknown>(url: string, { body, header
68
68
  /** Per-call transport options. */
69
69
  export interface ScenarioInvokeOptions {
70
70
  /**
71
- * Headers to send alongside the persona's own session. This is how a step
72
- * expresses an identity no declared persona can — an impersonation header, or
71
+ * Headers to send alongside the virtual user's own session. This is how a step
72
+ * expresses an identity no declared virtual user can — an impersonation header, or
73
73
  * one of the header-shim principals a credential scenario invents.
74
74
  */
75
75
  headers?: Record<string, string>;
76
76
  }
77
77
  /**
78
- * The RPC surface a persona can reach, as name → input/output. A project binds
78
+ * The RPC surface a virtual user can reach, as name → input/output. A project binds
79
79
  * its generated exposed RPC map here; the default leaves every name open, which
80
- * is what a persona built by hand (or by a third-party driver) gets.
80
+ * is what a virtual user built by hand (or by a third-party driver) gets.
81
81
  */
82
82
  export type ScenarioRpcMap = Record<string, {
83
83
  input: any;
84
84
  output: any;
85
85
  }>;
86
86
  /**
87
- * The persona a step's `actor` slot carries, for a project whose personas are
88
- * known. No declared personas keeps the open type rather than collapsing to
87
+ * The virtual user a step's `virtualUser` slot carries, for a project whose virtual users are
88
+ * known. No declared virtual users keeps the open type rather than collapsing to
89
89
  * `never` — a project may still build one itself.
90
90
  */
91
- export type ScenarioPersonaOf<TPersonas> = [keyof TPersonas] extends [never] ? ScenarioPersona : TPersonas[keyof TPersonas];
91
+ export type ScenarioVirtualUserOf<TVirtualUsers> = [
92
+ keyof TVirtualUsers
93
+ ] extends [never] ? ScenarioVirtualUser : TVirtualUsers[keyof TVirtualUsers];
92
94
  /**
93
95
  * A declared person, signed in and acting over the real transport.
94
96
  *
95
- * The runtime half of `definePersonas()`: sign-in materialises a user row
96
- * flagged `actor: true`, and everything a step or a virtual user does goes
97
+ * The runtime half of `defineVirtualUsers()`: sign-in materialises a user row
98
+ * flagged `virtualUser: true`, and everything a step or a virtual user does goes
97
99
  * through here.
98
100
  */
99
- export interface ScenarioPersona<TAgentName extends string = string, TRpcMap extends ScenarioRpcMap = ScenarioRpcMap> {
100
- /** The id it was declared under — what a step's `actor` slot names. */
101
+ export interface ScenarioVirtualUser<TAgentName extends string = string, TRpcMap extends ScenarioRpcMap = ScenarioRpcMap> {
102
+ /** The id it was declared under — what a step's `virtualUser` slot names. */
101
103
  readonly name: string;
102
104
  /** Their address, computed from the id. Flows use it for invites/lookups. */
103
105
  readonly email: string;
@@ -110,7 +112,7 @@ export interface ScenarioPersona<TAgentName extends string = string, TRpcMap ext
110
112
  */
111
113
  invokeRaw<TName extends keyof TRpcMap & string>(rpcName: TName, data: TRpcMap[TName]['input'], options?: ScenarioInvokeOptions): Promise<ScenarioHttpResponse>;
112
114
  /** Converse with a Pikku AI agent in character and return its verdict. */
113
- converse(options: ConverseOptions<TAgentName>): Promise<ActorFlowVerdict>;
115
+ converse(options: ConverseOptions<TAgentName>): Promise<ConversationVerdict>;
114
116
  /**
115
117
  * The roles this person actually holds on the stage, read back after signing
116
118
  * in — what a run compares against the declaration before its first step.
@@ -123,16 +125,16 @@ export interface ScenarioPersona<TAgentName extends string = string, TRpcMap ext
123
125
  sessionRoles(): Promise<string[] | null>;
124
126
  }
125
127
  /**
126
- * A declared persona with its address filled in — what codegen writes and what
128
+ * A declared virtual user with its address filled in — what codegen writes and what
127
129
  * a seed, a scenario run and a virtual-user run all read.
128
130
  *
129
- * The address is not declared. It is derived from the persona's id and the
130
- * project's persona domain, so two people can never accidentally share one and
131
+ * The address is not declared. It is derived from the virtual user's id and the
132
+ * project's virtual user domain, so two people can never accidentally share one and
131
133
  * sign in as the same user row — which would silently collapse exactly the
132
134
  * isolation scenarios a second person exists for.
133
135
  */
134
- export type ResolvedPersona = PersonaMeta & {
136
+ export type ResolvedVirtualUser = VirtualUserMeta & {
135
137
  email: string;
136
138
  };
137
- /** The injected `personas` service: persona id → persona. */
138
- export type ScenarioPersonas = Record<string, ScenarioPersona>;
139
+ /** The injected `virtualUsers` service: virtual user id → virtual user. */
140
+ export type ScenarioVirtualUsers = Record<string, ScenarioVirtualUser>;
@@ -2,8 +2,8 @@
2
2
  * Drain a response into the shape a step can carry: the parsed body (an empty
3
3
  * one counting as no body at all) alongside the text it was parsed from.
4
4
  *
5
- * `invokeRaw` returns this, and a step that has to reach past a persona — a
6
- * route with no RPC, an identity no persona can hold — reaches for this rather
5
+ * `invokeRaw` returns this, and a step that has to reach past a virtual user — a
6
+ * route with no RPC, an identity no virtual user can hold — reaches for this rather
7
7
  * than writing the same record by hand.
8
8
  *
9
9
  * @example snippet: scenarioPolling
@@ -28,7 +28,7 @@ const parseJsonBody = (text) => {
28
28
  /**
29
29
  * POST JSON somewhere and report what came back, without throwing on a 4xx/5xx.
30
30
  *
31
- * Every scenario that reaches past a persona was writing this by hand — the same
31
+ * Every scenario that reaches past a virtual user was writing this by hand — the same
32
32
  * `content-type`, the same `JSON.stringify`, the same drain — and the copies had
33
33
  * drifted: some returned `res.json()`, which loses the status and throws
34
34
  * outright when the target answers an empty body or an HTML error page. A
@@ -25,7 +25,7 @@ import type { SchedulerService } from '../services/scheduler-service.js';
25
25
  import type { DeploymentService } from '../services/deployment-service.js';
26
26
  import type { AgentStorageService } from '../services/agent-storage-service.js';
27
27
  import type { ContentService } from '../services/content-service.js';
28
- import type { ScenarioPersonaOf, ScenarioPersonas } from '../services/personas-service.js';
28
+ import type { ScenarioVirtualUserOf, ScenarioVirtualUsers } from '../services/virtual-users-service.js';
29
29
  import type { AgentRunnerService } from '../services/agent-runner-service.js';
30
30
  import type { AIEmbeddingService } from '../services/ai-embedding-service.js';
31
31
  import type { AgentRunStateService } from '../services/agent-run-state-service.js';
@@ -89,8 +89,8 @@ export interface CoreUserSession {
89
89
  userId?: string;
90
90
  /** The tenant the session is acting inside, when the project has more than one. */
91
91
  orgId?: string;
92
- /** True when the session belongs to a synthetic scenario actor — lets audits/analytics address synthetic traffic */
93
- actor?: boolean;
92
+ /** True when the session belongs to a synthetic scenario virtual user — lets audits/analytics address synthetic traffic */
93
+ virtualUser?: boolean;
94
94
  /**
95
95
  * Scopes granted to this session, checked against a function's `scopes`.
96
96
  * Populated by whoever builds the session — core reads them, never fetches.
@@ -150,7 +150,7 @@ export interface CoreSingletonServices<Config extends CoreConfig = CoreConfig> {
150
150
  */
151
151
  virtualUserRunStore?: VirtualUserRunStore;
152
152
  /**
153
- * Each persona's cadence, for apps that want their virtual users to keep
153
+ * Each virtual user's cadence, for apps that want their virtual users to keep
154
154
  * going without being asked. Separate from the run store on purpose: wiring
155
155
  * nothing is how an app says it only wants the runs it starts itself.
156
156
  */
@@ -214,7 +214,7 @@ export type GetCredential<TCredentials = Record<string, unknown>> = string exten
214
214
  <K extends keyof TCredentials & string>(name: K): TCredentials[K] | null | Promise<TCredentials[K] | null>;
215
215
  <T = unknown>(name: string): T | null | Promise<T | null>;
216
216
  };
217
- export type PikkuWire<In = unknown, Out = unknown, HasInitialSession extends boolean = false, UserSession extends CoreUserSession = CoreUserSession, TypedRPC extends PikkuRPC = PikkuRPC, IsChannel extends true | null = null, MCPTools extends string | never = never, TypedWorkflow extends PikkuWorkflowWire | never = PikkuWorkflowWire, TriggerOutput = unknown, TypedScenario extends PikkuScenarioWire<any> | never = PikkuScenarioWire<any>, TypedActors extends ScenarioPersonas = ScenarioPersonas, TypedCredentials = Record<string, unknown>> = {
217
+ export type PikkuWire<In = unknown, Out = unknown, HasInitialSession extends boolean = false, UserSession extends CoreUserSession = CoreUserSession, TypedRPC extends PikkuRPC = PikkuRPC, IsChannel extends true | null = null, MCPTools extends string | never = never, TypedWorkflow extends PikkuWorkflowWire | never = PikkuWorkflowWire, TriggerOutput = unknown, TypedScenario extends PikkuScenarioWire<any> | never = PikkuScenarioWire<any>, TScenarioVirtualUsers extends ScenarioVirtualUsers = ScenarioVirtualUsers, TypedCredentials = Record<string, unknown>> = {
218
218
  /** Always present — lazily initialised on first access for every function invocation */
219
219
  rpc: TypedRPC;
220
220
  /** Get a single credential by name — lazy-loads from CredentialService on first call, sync thereafter */
@@ -241,17 +241,17 @@ export type PikkuWire<In = unknown, Out = unknown, HasInitialSession extends boo
241
241
  cli: PikkuCLI<TypedRPC['remote']>;
242
242
  workflow: TypedWorkflow;
243
243
  scenario: TypedScenario;
244
- actors: TypedActors;
244
+ virtualUsers: TScenarioVirtualUsers;
245
245
  /**
246
- * The persona this step runs as, injected by the runner.
246
+ * The virtual user this step runs as, injected by the runner.
247
247
  *
248
248
  * Required on a `browser` binding, which cannot be provisioned without one,
249
- * and on any binding whose step declares `actor: true`. A step that declares
250
- * neither has no `actor` on its wire at all rather than an optional one —
249
+ * and on any binding whose step declares `virtualUser: true`. A step that declares
250
+ * neither has no `virtualUser` on its wire at all rather than an optional one —
251
251
  * a pure assertion has nobody to be, and `attemptsSignIn` deliberately posts
252
- * credentials instead of reusing an actor's session.
252
+ * credentials instead of reusing a virtual user's session.
253
253
  */
254
- actor: ScenarioPersonaOf<TypedActors>;
254
+ virtualUser: ScenarioVirtualUserOf<TScenarioVirtualUsers>;
255
255
  /** Present on every scenario step invocation */
256
256
  scenarioStep: PikkuScenarioStepWire;
257
257
  /** Present only when the runner provisioned a browser for this step */
@@ -1,4 +1,4 @@
1
- export type { ConfigValidationResult, CoreQueueWorker, GroupConcurrencyConfig, JobGroup, JobOptions, PikkuJobConfig, PikkuWorkerConfig, PikkuQueue, QueueJob, QueueJobStatus, QueueService, QueueWorkers, QueueWorkersMeta, } from './queue.types.js';
1
+ export type { ConfigValidationResult, CoreQueueWorker, GroupConcurrencyConfig, JobGroup, JobOptions, PikkuJobConfig, PikkuWorkerConfig, PikkuQueue, QueueInspection, QueueJob, QueueJobCounts, QueueJobRecord, QueueJobStatus, QueueService, QueueWorkers, QueueWorkersMeta, } from './queue.types.js';
2
2
  export { wireQueueWorker, runQueueJob, QueueJobDiscardedError, QueueJobFailedError, } from './queue-runner.js';
3
3
  export type { QueueIdentityBinding } from './queue-identity.js';
4
4
  export { SignedQueueService } from './signed-queue-service.js';
@@ -94,8 +94,29 @@ export interface JobOptions {
94
94
  /** Counts against the worker's {@link PikkuWorkerConfig.groupConcurrency}. */
95
95
  group?: JobGroup;
96
96
  }
97
+ export type QueueJobCounts = Record<QueueJobStatus, number>;
98
+ export type QueueJobRecord = {
99
+ jobId: string;
100
+ status: QueueJobStatus;
101
+ attemptsMade: number;
102
+ maxAttempts: number;
103
+ createdAt: Date;
104
+ processedAt: Date | null;
105
+ finishedAt: Date | null;
106
+ durationMs: number | null;
107
+ error: string | null;
108
+ };
109
+ export type QueueInspection = {
110
+ counts: QueueJobCounts;
111
+ /** Newest first. */
112
+ recent: QueueJobRecord[];
113
+ };
97
114
  export interface QueueService {
98
115
  readonly supportsResults: boolean;
116
+ /** Live counts and recent jobs for one queue. Absent on a queue service that cannot inspect its backlog. */
117
+ inspect?(queueName: string, opts?: {
118
+ limit?: number;
119
+ }): Promise<QueueInspection>;
99
120
  add<T>(queueName: string, data: Safe<T>, options?: JobOptions): Promise<string>;
100
121
  getJob<T, R>(queueName: string, jobId: string): Promise<QueueJob<T, R> | null>;
101
122
  }
@@ -1,7 +1,7 @@
1
1
  import type { Safe } from '../../classification/secret-value.js';
2
2
  import type { Logger } from '../../services/logger.js';
3
3
  import type { SecretService } from '../../services/secret-service.js';
4
- import type { JobOptions, QueueJob, QueueService } from './queue.types.js';
4
+ import type { JobOptions, QueueInspection, QueueJob, QueueService } from './queue.types.js';
5
5
  /**
6
6
  * Wraps any {@link QueueService} so `JobOptions.pikkuUserId` leaves the process
7
7
  * as a signed claim that `runQueueJob` can verify.
@@ -14,4 +14,7 @@ export declare class SignedQueueService implements QueueService {
14
14
  get supportsResults(): boolean;
15
15
  add<T>(queueName: string, data: Safe<T>, options?: JobOptions): Promise<string>;
16
16
  getJob<T, R>(queueName: string, jobId: string): Promise<QueueJob<T, R> | null>;
17
+ get inspect(): ((queueName: string, opts?: {
18
+ limit?: number;
19
+ }) => Promise<QueueInspection>) | undefined;
17
20
  }
@@ -39,4 +39,7 @@ export class SignedQueueService {
39
39
  async getJob(queueName, jobId) {
40
40
  return this.queueService.getJob(queueName, jobId);
41
41
  }
42
+ get inspect() {
43
+ return this.queueService.inspect?.bind(this.queueService);
44
+ }
42
45
  }
@@ -3,7 +3,7 @@ import type { CoreSystemRoles } from './role.types.js';
3
3
  * No-op function for declaring system roles.
4
4
  * This exists purely for TypeScript type checking and will be tree-shaken.
5
5
  * The CLI extracts metadata via AST parsing and generates a `SystemRoleName`
6
- * union, so a persona naming an undeclared role fails the build.
6
+ * union, so a virtual user naming an undeclared role fails the build.
7
7
  *
8
8
  * A system role is a role that ships with the product: the console may grant
9
9
  * it, but cannot rename, re-scope or delete it. Roles an admin composes in the
@@ -2,7 +2,7 @@
2
2
  * No-op function for declaring system roles.
3
3
  * This exists purely for TypeScript type checking and will be tree-shaken.
4
4
  * The CLI extracts metadata via AST parsing and generates a `SystemRoleName`
5
- * union, so a persona naming an undeclared role fails the build.
5
+ * union, so a virtual user naming an undeclared role fails the build.
6
6
  *
7
7
  * A system role is a role that ships with the product: the console may grant
8
8
  * it, but cannot rename, re-scope or delete it. Roles an admin composes in the