@pikku/core 0.12.139 → 0.12.142

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 (253) hide show
  1. package/CHANGELOG.md +47 -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/lifecycle.d.ts +6 -0
  7. package/dist/lifecycle.js +58 -0
  8. package/dist/services/audit-service.d.ts +4 -4
  9. package/dist/services/feature-flag-service.d.ts +3 -3
  10. package/dist/services/{http-personas.d.ts → http-virtual-users.d.ts} +25 -47
  11. package/dist/services/{http-personas.js → http-virtual-users.js} +29 -53
  12. package/dist/services/index.d.ts +2 -2
  13. package/dist/services/index.js +1 -1
  14. package/dist/services/meta-service.d.ts +6 -6
  15. package/dist/services/meta-service.js +8 -8
  16. package/dist/{wirings/workflow → services}/scenario-cookie-jar.d.ts +2 -0
  17. package/dist/{wirings/workflow → services}/scenario-cookie-jar.js +18 -14
  18. package/dist/services/scoped-secret-service.d.ts +1 -0
  19. package/dist/services/scoped-secret-service.js +3 -0
  20. package/dist/services/secret-service.d.ts +6 -0
  21. package/dist/services/system-role-guard.d.ts +1 -1
  22. package/dist/services/typed-secret-service.d.ts +1 -0
  23. package/dist/services/typed-secret-service.js +7 -0
  24. package/dist/services/virtual-user-sign-in.d.ts +52 -0
  25. package/dist/services/virtual-user-sign-in.js +80 -0
  26. package/dist/services/virtual-user-statement.d.ts +31 -0
  27. package/dist/services/virtual-user-statement.js +24 -0
  28. package/dist/services/{personas-service.d.ts → virtual-users-service.d.ts} +25 -23
  29. package/dist/services/{personas-service.js → virtual-users-service.js} +3 -3
  30. package/dist/testing/service-tests/secret-service-tests.js +9 -0
  31. package/dist/types/core.types.d.ts +11 -11
  32. package/dist/utils.d.ts +2 -5
  33. package/dist/utils.js +1 -38
  34. package/dist/wirings/cli/cli-runner.js +24 -9
  35. package/dist/wirings/http/http-runner.js +4 -0
  36. package/dist/wirings/http/http.types.d.ts +1 -0
  37. package/dist/wirings/http/pikku-fetch-http-response.d.ts +1 -0
  38. package/dist/wirings/http/pikku-fetch-http-response.js +12 -0
  39. package/dist/wirings/http/web-request.js +7 -0
  40. package/dist/wirings/role/define-system-role.d.ts +1 -1
  41. package/dist/wirings/role/define-system-role.js +1 -1
  42. package/dist/wirings/{actor-flow/actor-flow.types.d.ts → virtual-user/conversation/conversation.types.d.ts} +5 -5
  43. package/dist/wirings/virtual-user/conversation/index.d.ts +2 -0
  44. package/dist/wirings/virtual-user/conversation/run-conversation.d.ts +36 -0
  45. package/dist/wirings/{actor-flow → virtual-user/conversation}/run-conversation.js +30 -30
  46. package/dist/wirings/virtual-user/define-virtual-users.d.ts +32 -0
  47. package/dist/wirings/{persona/define-personas.js → virtual-user/define-virtual-users.js} +9 -9
  48. package/dist/wirings/virtual-user/index.d.ts +30 -7
  49. package/dist/wirings/virtual-user/index.js +23 -4
  50. package/dist/wirings/virtual-user/prepare-virtual-user-run.d.ts +8 -8
  51. package/dist/wirings/virtual-user/prepare-virtual-user-run.js +9 -9
  52. package/dist/wirings/virtual-user/run-virtual-user.d.ts +6 -6
  53. package/dist/wirings/virtual-user/run-virtual-user.js +10 -10
  54. package/dist/wirings/{persona/validate-personas.d.ts → virtual-user/validate-virtual-users.d.ts} +11 -11
  55. package/dist/wirings/{persona/validate-personas.js → virtual-user/validate-virtual-users.js} +24 -24
  56. package/dist/wirings/virtual-user/virtual-user-agents.d.ts +7 -7
  57. package/dist/wirings/virtual-user/virtual-user-agents.js +2 -2
  58. package/dist/wirings/{persona/persona-app-scopes.d.ts → virtual-user/virtual-user-app-scopes.d.ts} +10 -10
  59. package/dist/wirings/{persona/persona-app-scopes.js → virtual-user/virtual-user-app-scopes.js} +12 -12
  60. package/dist/wirings/virtual-user/virtual-user-catalogue.d.ts +3 -3
  61. package/dist/wirings/virtual-user/virtual-user-catalogue.js +2 -2
  62. package/dist/wirings/{persona/persona.types.d.ts → virtual-user/virtual-user-declaration.types.d.ts} +24 -24
  63. package/dist/wirings/virtual-user/virtual-user-derive.js +13 -7
  64. package/dist/wirings/virtual-user/virtual-user-dispositions.d.ts +1 -1
  65. package/dist/wirings/{persona/persona-email.d.ts → virtual-user/virtual-user-email.d.ts} +11 -11
  66. package/dist/wirings/{persona/persona-email.js → virtual-user/virtual-user-email.js} +19 -19
  67. package/dist/wirings/{persona/persona-environments.d.ts → virtual-user/virtual-user-environments.d.ts} +10 -10
  68. package/dist/wirings/{persona/persona-environments.js → virtual-user/virtual-user-environments.js} +21 -21
  69. package/dist/wirings/virtual-user/virtual-user-intents.d.ts +3 -3
  70. package/dist/wirings/virtual-user/virtual-user-intents.js +3 -3
  71. package/dist/wirings/{persona/persona-mailbox.d.ts → virtual-user/virtual-user-mailbox.d.ts} +4 -4
  72. package/dist/wirings/virtual-user/virtual-user-run-store.d.ts +4 -4
  73. package/dist/wirings/virtual-user/virtual-user-scaffold.d.ts +42 -61
  74. package/dist/wirings/virtual-user/virtual-user-scaffold.js +69 -117
  75. package/dist/wirings/virtual-user/virtual-user-schedule-store.d.ts +15 -15
  76. package/dist/wirings/virtual-user/virtual-user-schedule.d.ts +6 -6
  77. package/dist/wirings/virtual-user/virtual-user-schedule.js +11 -11
  78. package/dist/wirings/virtual-user/virtual-user-target.d.ts +6 -6
  79. package/dist/wirings/virtual-user/virtual-user-target.js +5 -5
  80. package/dist/wirings/virtual-user/virtual-user.types.d.ts +11 -11
  81. package/dist/wirings/virtual-user/virtual-user.types.js +1 -1
  82. package/dist/wirings/workflow/dsl/workflow-dsl.types.d.ts +9 -9
  83. package/dist/wirings/workflow/pikku-scenario-service.d.ts +28 -28
  84. package/dist/wirings/workflow/pikku-scenario-service.js +77 -77
  85. package/dist/wirings/workflow/pikku-workflow-service.d.ts +2 -2
  86. package/dist/wirings/workflow/pikku-workflow-service.js +4 -4
  87. package/dist/wirings/workflow/scenario-prose.d.ts +8 -8
  88. package/dist/wirings/workflow/scenario-prose.js +12 -8
  89. package/dist/wirings/workflow/scenario-run.types.d.ts +13 -13
  90. package/dist/wirings/workflow/scenario-step.types.d.ts +23 -23
  91. package/dist/wirings/workflow/workflow.types.d.ts +2 -2
  92. package/knowledge/decisions/index.md +0 -2
  93. package/knowledge/decisions/internals/a-credential-name-declared-twice-must-agree-on-schema-and-type.md +29 -0
  94. package/knowledge/decisions/internals/a-grant-can-name-a-scope-declared-without-its-addon.md +29 -0
  95. package/knowledge/decisions/internals/a-scheduled-task-writes-its-caller-session-before-it-runs.md +22 -0
  96. package/knowledge/decisions/internals/a-secret-set-in-memory-shadows-the-variable-of-the-same-name.md +28 -0
  97. package/knowledge/decisions/internals/a-virtual-user-acts-through-invokeraw-so-a-refusal-is-data.md +32 -0
  98. package/knowledge/decisions/internals/a-virtual-user-cadence-is-a-row-not-a-timer.md +12 -12
  99. package/knowledge/decisions/internals/a-virtual-user-converse-signs-in-before-its-first-turn.md +24 -0
  100. package/knowledge/decisions/internals/a-virtual-user-email-is-computed-with-the-run-id-as-a-sub-address.md +34 -0
  101. package/knowledge/decisions/internals/a-virtual-user-run-is-not-a-workflow-but-it-needs-a-trigger.md +2 -2
  102. package/knowledge/decisions/internals/an-agent-is-offered-under-its-registry-key-and-only-within-the-virtual-users-scopes.md +27 -0
  103. package/knowledge/decisions/internals/an-oauth2-credential-implies-an-optional-app-secret.md +26 -0
  104. package/knowledge/decisions/internals/analytics-identity-resolvers-run-in-declared-order.md +22 -0
  105. package/knowledge/decisions/internals/feature-flag-declarations-refuse-an-empty-anyof-and-conflicting-scopes.md +22 -0
  106. package/knowledge/decisions/internals/feature-flags-fail-open-and-never-enforce-access.md +28 -0
  107. package/knowledge/decisions/internals/flag-sync-rewrites-only-the-declared-text.md +24 -0
  108. package/knowledge/decisions/internals/in-a-scenario-a-4xx-is-data-not-an-exception.md +3 -3
  109. package/knowledge/decisions/internals/index.md +46 -31
  110. package/knowledge/decisions/internals/one-project-shape-check-two-validators.md +10 -10
  111. package/knowledge/decisions/internals/role-names-cannot-contain-the-scope-separator.md +30 -0
  112. package/knowledge/decisions/internals/scenario-agent-calls-sign-in-on-401-only.md +23 -19
  113. package/knowledge/decisions/internals/scenario-steps-return-drained-response-records.md +2 -2
  114. package/knowledge/decisions/internals/scenarios-live-in-files-named-for-them.md +12 -12
  115. package/knowledge/decisions/internals/the-core-scope-validator-refuses-separator-wildcard-and-empty-segments.md +22 -0
  116. package/knowledge/decisions/internals/the-pikku-user-id-resolves-in-a-fixed-order.md +24 -0
  117. 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
  118. 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
  119. package/knowledge/decisions/internals/validate-checks-virtual-users-through-a-shared-module.md +43 -0
  120. 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
  121. package/knowledge/decisions/internals/virtual-user-conversation-verdicts-are-llm-self-evaluations.md +25 -0
  122. package/knowledge/decisions/internals/virtual-user-conversations-seed-a-hidden-kickoff-message.md +23 -0
  123. package/knowledge/decisions/internals/virtual-users-are-declared-in-one-definevirtualusers-call-per-codebase.md +30 -0
  124. package/knowledge/decisions/internals/workflow-core-never-imports-a-browser-driver.md +5 -5
  125. package/knowledge/decisions/internals/workflow-run-capabilities-are-extensions-not-subclasses.md +2 -2
  126. package/knowledge/decisions/internals/workflow-scenario-assertions-never-retry-and-record-one-step.md +4 -4
  127. package/knowledge/decisions/internals/workflow-scenario-hooks-are-a-scenario-only-affordance.md +1 -1
  128. package/knowledge/decisions/internals/workflow-scenario-prose-is-rendered-from-typed-calls-not-parsed-from-english.md +2 -2
  129. package/knowledge/decisions/internals/workflow-scenario-step-targets-are-string-literals-for-the-inspector.md +1 -1
  130. package/knowledge/decisions/internals/workflow-suspended-runs-keep-their-in-process-context.md +1 -1
  131. package/knowledge/decisions/security/a-bearer-token-checked-against-a-secret-reads-it-per-request.md +26 -0
  132. package/knowledge/decisions/security/a-call-without-a-session-raises-missing-session-not-forbidden.md +20 -0
  133. package/knowledge/decisions/security/a-caller-cookie-wins-over-the-jar-for-the-same-name.md +23 -0
  134. package/knowledge/decisions/security/a-channel-lifecycle-acts-as-the-socket-upgrade-session.md +16 -0
  135. package/knowledge/decisions/security/a-channel-requires-a-session-unless-it-says-auth-false.md +26 -0
  136. package/knowledge/decisions/security/a-credential-read-path-follows-its-declared-type-not-absence.md +32 -0
  137. package/knowledge/decisions/security/a-credential-type-is-a-literal-the-inspector-checks-at-declaration.md +27 -0
  138. package/knowledge/decisions/security/a-first-party-cookie-is-written-only-after-its-purposes-consent.md +33 -0
  139. package/knowledge/decisions/security/a-readonly-session-reaches-only-readonly-functions.md +25 -0
  140. package/knowledge/decisions/security/a-scaffold-flag-says-a-surface-exists-not-who-may-call-it.md +10 -5
  141. package/knowledge/decisions/security/a-scheduled-virtual-user-run-needs-no-admin-at-run-time.md +27 -0
  142. package/knowledge/decisions/security/a-scoped-credential-view-is-limited-to-declared-names.md +26 -0
  143. package/knowledge/decisions/security/a-scoped-secret-view-reads-only-declared-keys-and-never-writes.md +25 -0
  144. package/knowledge/decisions/security/a-secret-is-sent-only-to-the-hosts-it-declares.md +29 -0
  145. package/knowledge/decisions/security/a-step-can-name-an-identity-no-virtual-user-holds-through-per-call-headers.md +25 -0
  146. package/knowledge/decisions/security/a-stopped-run-by-id-skips-the-ownership-check.md +24 -0
  147. package/knowledge/decisions/security/a-vault-secret-redacts-on-serialization-and-throws-on-coercion.md +29 -0
  148. package/knowledge/decisions/security/a-virtual-user-catalogue-entry-is-filtered-by-scope-and-approval-is-denied-by-default.md +30 -0
  149. package/knowledge/decisions/security/a-virtual-user-environment-fails-closed.md +38 -0
  150. package/knowledge/decisions/security/a-virtual-user-never-runs-against-production.md +23 -0
  151. package/knowledge/decisions/security/a-virtual-user-reads-its-roles-from-the-apps-own-rpc-before-better-auth.md +27 -0
  152. package/knowledge/decisions/security/a-virtual-user-run-treats-an-unresolved-environment-as-production.md +41 -0
  153. package/knowledge/decisions/security/a-virtual-user-scopes-are-expanded-from-its-roles-once-at-run-preparation.md +25 -0
  154. package/knowledge/decisions/security/a-virtual-user-whose-roles-drift-from-the-declaration-refuses-to-run.md +32 -0
  155. package/knowledge/decisions/security/addon-screen-scopes-hide-and-do-not-enforce.md +20 -0
  156. package/knowledge/decisions/security/an-addon-header-can-only-narrow-the-session.md +26 -0
  157. package/knowledge/decisions/security/an-addon-instance-reads-only-the-secrets-and-credentials-it-is-named.md +33 -0
  158. package/knowledge/decisions/security/an-addon-may-reach-only-itself-and-the-addons-it-uses.md +16 -0
  159. package/knowledge/decisions/security/an-api-key-is-jwt-decoded-into-the-session.md +26 -0
  160. package/knowledge/decisions/security/an-external-virtual-user-caller-presents-one-signed-statement.md +38 -0
  161. package/knowledge/decisions/security/an-inbound-webhook-source-dispatches-only-verified-requests.md +31 -0
  162. package/knowledge/decisions/security/an-mcp-call-with-no-http-request-stays-anonymous.md +18 -0
  163. package/knowledge/decisions/security/an-omitted-user-id-addresses-the-platform-credential.md +32 -0
  164. package/knowledge/decisions/security/analytics-identity-is-stamped-from-the-session-not-the-event.md +24 -0
  165. package/knowledge/decisions/security/app-access-is-a-scope-tree-synthesised-from-virtual-users.md +26 -0
  166. package/knowledge/decisions/security/approval-policy-decides-who-answers-a-tool-call.md +27 -0
  167. package/knowledge/decisions/security/auth-false-on-a-session-function-warns-and-still-requires-one.md +25 -0
  168. package/knowledge/decisions/security/bearer-token-hashes-are-unpeppered-sha256.md +18 -0
  169. package/knowledge/decisions/security/consent-is-read-from-a-cookie-and-a-recognised-denial-counts-as-refusal.md +29 -0
  170. package/knowledge/decisions/security/cors-defaults-to-any-origin-and-refuses-the-wildcard-with-credentials.md +22 -0
  171. package/knowledge/decisions/security/credential-backends-share-one-conformance-suite-for-per-user-isolation.md +29 -0
  172. package/knowledge/decisions/security/each-stage-has-one-virtual-user-key-pair.md +27 -0
  173. package/knowledge/decisions/security/hmac-verification-fails-closed-on-empty-inputs.md +25 -0
  174. package/knowledge/decisions/security/index.md +88 -24
  175. package/knowledge/decisions/security/local-content-refuses-private-buckets-under-the-public-segment.md +21 -0
  176. package/knowledge/decisions/security/mail-is-allowlisted-before-a-virtual-user-run-sees-it.md +38 -0
  177. package/knowledge/decisions/security/mcp-resources-tools-and-prompts-take-sessionless-functions.md +18 -0
  178. package/knowledge/decisions/security/origin-locked-routes-match-the-origin-exactly.md +22 -0
  179. package/knowledge/decisions/security/oss-auth-works-without-pikku-studio-cloud.md +26 -0
  180. package/knowledge/decisions/security/outbound-webhooks-use-an-allowlist-when-configured.md +24 -0
  181. package/knowledge/decisions/security/pikku-carries-virtual-user-scopes-as-data-and-the-app-grants-them.md +35 -0
  182. package/knowledge/decisions/security/scoped-content-access-is-a-root-folder-with-longest-prefix-grants.md +31 -0
  183. package/knowledge/decisions/security/services-without-secrets-get-a-throwing-secrets-accessor.md +22 -0
  184. package/knowledge/decisions/security/shared-guards-refuse-to-mutate-or-shadow-a-system-role.md +34 -0
  185. package/knowledge/decisions/security/signed-uploads-are-verified-by-the-same-check-as-signed-reads.md +26 -0
  186. package/knowledge/decisions/security/system-role-scopes-must-be-declared-with-define-scope.md +24 -0
  187. package/knowledge/decisions/security/system-roles-are-additive-and-immutable.md +16 -0
  188. package/knowledge/decisions/security/the-channel-rpc-does-not-send-the-callers-session.md +21 -0
  189. package/knowledge/decisions/security/the-remote-jobs-inbox-fails-closed-without-a-secret.md +24 -0
  190. package/knowledge/decisions/security/the-remote-rpc-gate-fails-closed-and-binds-the-token-to-its-function.md +34 -0
  191. package/knowledge/decisions/security/the-workflow-status-stream-rechecks-ownership-on-every-poll.md +16 -0
  192. package/knowledge/decisions/security/two-credentials-on-one-key-must-share-a-read-mode.md +29 -0
  193. package/knowledge/decisions/security/verified-bearer-claims-are-pass-through-data-that-core-never-fills.md +23 -0
  194. package/knowledge/decisions/security/virtual-user-catalogue-excludes-the-machinery-that-runs-virtual-users.md +25 -0
  195. 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
  196. package/knowledge/decisions/security/virtual-user-run-records-are-gated-behind-a-read-scope.md +18 -0
  197. package/knowledge/decisions/security/virtual-user-sign-in-does-not-exist-in-production.md +25 -0
  198. package/knowledge/decisions/security/virtual-user-sign-in-has-one-door.md +42 -0
  199. 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
  200. package/knowledge/decisions/security/virtual-user-sign-in-only-works-for-virtual-user-flagged-users.md +29 -0
  201. package/knowledge/decisions/security/workflow-helper-functions-are-sessionless-by-declaration.md +18 -0
  202. 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
  203. package/knowledge/decisions/security/workflow-scenario-steps-are-never-network-invocable.md +5 -5
  204. 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
  205. package/knowledge/decisions/security/writing-a-virtual-user-schedule-is-gated-by-its-own-scope.md +18 -0
  206. package/knowledge/index.md +0 -2
  207. package/knowledge/questions/a-bearer-jwt-with-no-identity-becomes-a-session.md +24 -0
  208. package/knowledge/questions/dev-quick-login-as-impersonation.md +25 -0
  209. package/knowledge/questions/does-a-delayed-rpc-run-as-the-session-it-was-scheduled-with.md +26 -0
  210. package/knowledge/questions/does-a-user-id-with-a-colon-read-another-users-credentials.md +27 -0
  211. package/knowledge/questions/does-a-wirecli-command-need-a-session-by-default.md +26 -0
  212. package/knowledge/questions/does-an-adversarial-in-scope-success-count-as-a-finding.md +26 -0
  213. package/knowledge/questions/does-approve-step-need-the-run-owners-session-or-the-gates-approvers.md +34 -0
  214. package/knowledge/questions/does-cors-origin-true-reflect-any-origin-with-credentials.md +20 -0
  215. package/knowledge/questions/does-skip-user-session-do-anything-on-a-channel.md +24 -0
  216. package/knowledge/questions/does-the-store-session-match-the-other-two.md +19 -0
  217. package/knowledge/questions/index.md +25 -2
  218. package/knowledge/questions/is-the-agent-score-worker-reachable-without-a-session.md +21 -0
  219. package/knowledge/questions/live-scheduled-tests-may-write.md +19 -0
  220. package/knowledge/questions/provision-virtual-users-has-no-caller-in-this-repo.md +23 -0
  221. package/knowledge/questions/remote-secret-refuses-requests-without-a-remote-token-on-every-path.md +32 -0
  222. package/knowledge/questions/should-a-middleware-set-session-persist-to-the-store.md +25 -0
  223. package/knowledge/questions/should-a-scoped-secret-view-throw-from-has-secret-for-a-disallowed-key.md +23 -0
  224. package/knowledge/questions/should-a-wrong-bearer-token-be-anonymous-or-refused.md +35 -0
  225. package/knowledge/questions/should-an-empty-webhook-signing-secret-send-unsigned.md +23 -0
  226. package/knowledge/questions/should-http-virtual-user-sessions-reset-between-scenarios-in-one-run.md +27 -0
  227. package/knowledge/questions/should-the-cookie-jar-scope-cookies-to-the-api-origin.md +26 -0
  228. package/knowledge/questions/should-voice-input-fetch-audio-urls-without-a-host-allowlist.md +20 -0
  229. package/knowledge/questions/staging-impersonation-rules.md +27 -0
  230. package/knowledge/questions/virtual-user-keys-on-cloud-stages-are-pikku-studios-to-build.md +34 -0
  231. package/knowledge/questions/virtual-user-statement-replay-is-per-process.md +20 -0
  232. package/knowledge/questions/what-an-impersonated-session-carries.md +22 -0
  233. package/package.json +3 -3
  234. package/src/public-surface.json +43 -47
  235. package/dist/services/persona-actor-secret.d.ts +0 -38
  236. package/dist/services/persona-actor-secret.js +0 -39
  237. package/dist/services/persona-sign-in.d.ts +0 -117
  238. package/dist/services/persona-sign-in.js +0 -154
  239. package/dist/wirings/actor-flow/index.d.ts +0 -2
  240. package/dist/wirings/actor-flow/run-conversation.d.ts +0 -36
  241. package/dist/wirings/persona/define-personas.d.ts +0 -32
  242. package/dist/wirings/persona/index.d.ts +0 -23
  243. package/dist/wirings/persona/index.js +0 -19
  244. package/knowledge/decisions/internals/actor-flow-conversations-seed-a-hidden-kickoff-message.md +0 -23
  245. package/knowledge/decisions/internals/actor-flow-verdicts-are-llm-self-evaluations.md +0 -25
  246. package/knowledge/decisions/internals/validate-checks-personas-through-a-shared-module.md +0 -43
  247. package/knowledge/decisions/security/actor-sign-in-only-works-for-actor-flagged-users.md +0 -31
  248. package/knowledge/decisions/security/an-actor-credential-is-derived-per-persona.md +0 -42
  249. package/knowledge/decisions/security/pikku-carries-actor-scopes-as-data-and-the-app-grants-them.md +0 -26
  250. /package/dist/wirings/{actor-flow/actor-flow.types.js → virtual-user/conversation/conversation.types.js} +0 -0
  251. /package/dist/wirings/{actor-flow → virtual-user/conversation}/index.js +0 -0
  252. /package/dist/wirings/{persona/persona.types.js → virtual-user/virtual-user-declaration.types.js} +0 -0
  253. /package/dist/wirings/{persona/persona-mailbox.js → virtual-user/virtual-user-mailbox.js} +0 -0
@@ -1,7 +1,7 @@
1
1
  import { getDurationInMilliseconds } from '../../time-utils.js';
2
2
  import { catalogueIndex, catalogueLookup, describeEntry, isReadOnly, reachableCatalogue, renderCatalogue, } from './virtual-user-catalogue.js';
3
3
  import { dispositionProfile, } from './virtual-user-dispositions.js';
4
- import { IntentStack, intentsForPersona } from './virtual-user-intents.js';
4
+ import { IntentStack, intentsForVirtualUser } from './virtual-user-intents.js';
5
5
  import { createRng } from './virtual-user-rng.js';
6
6
  /** One turn: what the user decided to do next, and why. */
7
7
  const ACTION_SCHEMA = {
@@ -108,11 +108,11 @@ export const rememberIds = (body, into, depth = 0) => {
108
108
  }
109
109
  return into;
110
110
  };
111
- const personaInstructions = (persona, profile, catalogue, agents, fixtures) => [
111
+ const virtualUserInstructions = (virtualUser, profile, catalogue, agents, fixtures) => [
112
112
  `You are a real person using a product through its API. Stay in character; you are the user, not the system.`,
113
- `Your name is ${persona.name}.`,
114
- persona.jobTitle ? `Your role: ${persona.jobTitle}.` : '',
115
- persona.personality ? `Your manner: ${persona.personality}.` : '',
113
+ `Your name is ${virtualUser.name}.`,
114
+ virtualUser.jobTitle ? `Your role: ${virtualUser.jobTitle}.` : '',
115
+ virtualUser.personality ? `Your manner: ${virtualUser.personality}.` : '',
116
116
  '',
117
117
  profile.instructions,
118
118
  '',
@@ -146,7 +146,7 @@ const personaInstructions = (persona, profile, catalogue, agents, fixtures) => [
146
146
  * work inside it stays genuinely open-ended.
147
147
  */
148
148
  export const runVirtualUser = async (params) => {
149
- const { persona, personaId, disposition = 'realistic', tuning, target, llm, model, budget, stop, validateOutput, classify, fixtures = [], agents = [], scopes, allowApprovalRequired = false, maxStepsPerIntent = DEFAULT_MAX_STEPS_PER_INTENT, } = params;
149
+ const { virtualUser, virtualUserId, disposition = 'realistic', tuning, target, llm, model, budget, stop, validateOutput, classify, fixtures = [], agents = [], scopes, allowApprovalRequired = false, maxStepsPerIntent = DEFAULT_MAX_STEPS_PER_INTENT, } = params;
150
150
  const profile = dispositionProfile(disposition, tuning);
151
151
  const seed = params.seed ?? Math.floor(Math.random() * 2 ** 31);
152
152
  const rng = createRng(seed);
@@ -161,7 +161,7 @@ export const runVirtualUser = async (params) => {
161
161
  });
162
162
  const index = catalogueIndex(reachable);
163
163
  const sources = [
164
- ...intentsForPersona(params.intents, personaId),
164
+ ...intentsForVirtualUser(params.intents, virtualUserId),
165
165
  ...(params.goals ?? []).map((goal, i) => ({
166
166
  id: `goal_${i + 1}`,
167
167
  title: goal,
@@ -191,7 +191,7 @@ export const runVirtualUser = async (params) => {
191
191
  : typeof budget.duration === 'number'
192
192
  ? budget.duration
193
193
  : getDurationInMilliseconds(budget.duration);
194
- const instructions = personaInstructions(persona, profile, renderCatalogue(reachable), agents, fixtures);
194
+ const instructions = virtualUserInstructions(virtualUser, profile, renderCatalogue(reachable), agents, fixtures);
195
195
  const addFinding = (finding) => {
196
196
  findings.push(finding);
197
197
  tally.findings++;
@@ -423,7 +423,7 @@ export const runVirtualUser = async (params) => {
423
423
  });
424
424
  }
425
425
  }
426
- // This persona does not hold the scopes this endpoint declares, and the
426
+ // This virtual user does not hold the scopes this endpoint declares, and the
427
427
  // server served it anyway — the declaration and the enforcement disagree.
428
428
  if (response.ok &&
429
429
  scopes &&
@@ -431,7 +431,7 @@ export const runVirtualUser = async (params) => {
431
431
  kinds.push('unexpected-success');
432
432
  addFinding({
433
433
  kind: 'unexpected-success',
434
- detail: `${entry.name} succeeded for '${personaId}', who does not hold ${entry.scopes
434
+ detail: `${entry.name} succeeded for '${virtualUserId}', who does not hold ${entry.scopes
435
435
  .filter((scope) => !scopes.includes(scope))
436
436
  .join(', ')}`,
437
437
  rpcName: entry.name,
@@ -1,9 +1,9 @@
1
- import type { PersonaDefinitions, PersonaMeta, PersonasMeta } from './persona.types.js';
1
+ import type { VirtualUserDefinitions, VirtualUserMeta, VirtualUsersMeta } from './virtual-user-declaration.types.js';
2
2
  /**
3
- * Whether a persona can actually be driven by a virtual user.
3
+ * Whether a virtual user can actually be run.
4
4
  *
5
5
  * A provider login cannot: driving Google's or GitHub's consent screen needs a
6
- * human, so the persona is seedable and assertable but not runnable. Derived
6
+ * human, so the virtual user is seedable and assertable but not runnable. Derived
7
7
  * rather than asked for, because it is a consequence of the account rather than
8
8
  * a separate decision — and a flag someone must remember to set is a flag that
9
9
  * ships wrong.
@@ -11,22 +11,22 @@ import type { PersonaDefinitions, PersonaMeta, PersonasMeta } from './persona.ty
11
11
  * An explicit `runnable: false` still wins: it marks the person who is only
12
12
  * ever acted *upon*, which no account shape reveals.
13
13
  */
14
- export declare const isRunnablePersona: (persona: {
14
+ export declare const isRunnableVirtualUser: (virtualUser: {
15
15
  runnable?: boolean;
16
16
  account?: {
17
17
  provider?: string;
18
18
  };
19
19
  }) => boolean;
20
20
  /**
21
- * Validates declared personas and keys them by id.
21
+ * Validates declared virtual users and keys them by id.
22
22
  *
23
23
  * Definitions sharing an id must be identical; a conflicting redeclaration is a
24
24
  * hard error naming both source files, on the same terms as scopes and roles.
25
25
  */
26
- export declare function validateAndBuildPersonasMeta(definitions: PersonaDefinitions): PersonasMeta;
26
+ export declare function validateAndBuildVirtualUsersMeta(definitions: VirtualUserDefinitions): VirtualUsersMeta;
27
27
  /** What a run compares against the store before its first step. */
28
28
  export interface RoleVerification {
29
- persona: string;
29
+ virtualUserId: string;
30
30
  expected: string[];
31
31
  actual: string[];
32
32
  missing: string[];
@@ -34,20 +34,20 @@ export interface RoleVerification {
34
34
  ok: boolean;
35
35
  }
36
36
  /**
37
- * Compares a persona's declared roles against what the stage actually granted.
37
+ * Compares a virtual user's declared roles against what the stage actually granted.
38
38
  *
39
39
  * Run at sign-in, before the first step. The declaration is authoritative —
40
40
  * the generated seed applies it — but pikku cannot guarantee the seed ran on a
41
41
  * stage it did not deploy, and virtual users are meant to run against staging
42
42
  * and production, so that case is the normal one.
43
43
  *
44
- * Both directions matter, for opposite reasons. An under-granted persona
44
+ * Both directions matter, for opposite reasons. An under-granted virtual user
45
45
  * reports authorization bugs that are really seed drift. An over-granted one
46
46
  * silently stops testing the boundary it was written to test — nothing fails,
47
47
  * the findings just quietly stop being about anything.
48
48
  */
49
- export declare const verifyPersonaRoles: (persona: string, expected: readonly string[], actual: readonly string[]) => RoleVerification;
49
+ export declare const verifyVirtualUserRoles: (virtualUserId: string, expected: readonly string[], actual: readonly string[]) => RoleVerification;
50
50
  /** The message a run stops with, or `null` when the roles line up. */
51
51
  export declare const roleMismatchMessage: (verification: RoleVerification) => string | null;
52
52
  /** Convenience: the meta a run needs, with `runnable` already resolved. */
53
- export declare const runnablePersonas: (meta: PersonasMeta) => PersonaMeta[];
53
+ export declare const runnableVirtualUsers: (meta: VirtualUsersMeta) => VirtualUserMeta[];
@@ -1,8 +1,8 @@
1
1
  /**
2
- * Whether a persona can actually be driven by a virtual user.
2
+ * Whether a virtual user can actually be run.
3
3
  *
4
4
  * A provider login cannot: driving Google's or GitHub's consent screen needs a
5
- * human, so the persona is seedable and assertable but not runnable. Derived
5
+ * human, so the virtual user is seedable and assertable but not runnable. Derived
6
6
  * rather than asked for, because it is a consequence of the account rather than
7
7
  * a separate decision — and a flag someone must remember to set is a flag that
8
8
  * ships wrong.
@@ -10,62 +10,62 @@
10
10
  * An explicit `runnable: false` still wins: it marks the person who is only
11
11
  * ever acted *upon*, which no account shape reveals.
12
12
  */
13
- export const isRunnablePersona = (persona) => {
14
- if (persona.runnable === false) {
13
+ export const isRunnableVirtualUser = (virtualUser) => {
14
+ if (virtualUser.runnable === false) {
15
15
  return false;
16
16
  }
17
- return persona.account?.provider === undefined;
17
+ return virtualUser.account?.provider === undefined;
18
18
  };
19
19
  /**
20
- * Validates declared personas and keys them by id.
20
+ * Validates declared virtual users and keys them by id.
21
21
  *
22
22
  * Definitions sharing an id must be identical; a conflicting redeclaration is a
23
23
  * hard error naming both source files, on the same terms as scopes and roles.
24
24
  */
25
- export function validateAndBuildPersonasMeta(definitions) {
25
+ export function validateAndBuildVirtualUsersMeta(definitions) {
26
26
  const meta = {};
27
- for (const persona of definitions) {
28
- if (!persona.id) {
29
- throw new Error('A persona is declared with an empty id.');
27
+ for (const virtualUser of definitions) {
28
+ if (!virtualUser.id) {
29
+ throw new Error('A virtual user is declared with an empty id.');
30
30
  }
31
- if (!persona.name) {
32
- throw new Error(`Persona '${persona.id}' has no name. A persona is a person; give them one.`);
31
+ if (!virtualUser.name) {
32
+ throw new Error(`VirtualUser '${virtualUser.id}' has no name. A virtual user is a person; give them one.`);
33
33
  }
34
- const existing = meta[persona.id];
34
+ const existing = meta[virtualUser.id];
35
35
  if (existing) {
36
36
  const same = JSON.stringify({ ...existing, sourceFile: undefined }) ===
37
- JSON.stringify({ ...persona, sourceFile: undefined });
37
+ JSON.stringify({ ...virtualUser, sourceFile: undefined });
38
38
  if (!same) {
39
- throw new Error(`Persona '${persona.id}' is declared twice with different content.\n` +
39
+ throw new Error(`VirtualUser '${virtualUser.id}' is declared twice with different content.\n` +
40
40
  ` First declaration: ${existing.sourceFile ?? 'unknown'}\n` +
41
- ` Second declaration: ${persona.sourceFile ?? 'unknown'}`);
41
+ ` Second declaration: ${virtualUser.sourceFile ?? 'unknown'}`);
42
42
  }
43
43
  continue;
44
44
  }
45
- meta[persona.id] = persona;
45
+ meta[virtualUser.id] = virtualUser;
46
46
  }
47
47
  return meta;
48
48
  }
49
49
  /**
50
- * Compares a persona's declared roles against what the stage actually granted.
50
+ * Compares a virtual user's declared roles against what the stage actually granted.
51
51
  *
52
52
  * Run at sign-in, before the first step. The declaration is authoritative —
53
53
  * the generated seed applies it — but pikku cannot guarantee the seed ran on a
54
54
  * stage it did not deploy, and virtual users are meant to run against staging
55
55
  * and production, so that case is the normal one.
56
56
  *
57
- * Both directions matter, for opposite reasons. An under-granted persona
57
+ * Both directions matter, for opposite reasons. An under-granted virtual user
58
58
  * reports authorization bugs that are really seed drift. An over-granted one
59
59
  * silently stops testing the boundary it was written to test — nothing fails,
60
60
  * the findings just quietly stop being about anything.
61
61
  */
62
- export const verifyPersonaRoles = (persona, expected, actual) => {
62
+ export const verifyVirtualUserRoles = (virtualUserId, expected, actual) => {
63
63
  const expectedSet = new Set(expected);
64
64
  const actualSet = new Set(actual);
65
65
  const missing = [...expectedSet].filter((r) => !actualSet.has(r)).sort();
66
66
  const extra = [...actualSet].filter((r) => !expectedSet.has(r)).sort();
67
67
  return {
68
- persona,
68
+ virtualUserId,
69
69
  expected: [...expectedSet].sort(),
70
70
  actual: [...actualSet].sort(),
71
71
  missing,
@@ -85,10 +85,10 @@ export const roleMismatchMessage = (verification) => {
85
85
  if (verification.extra.length) {
86
86
  parts.push(`unexpectedly holds ${verification.extra.join(', ')}`);
87
87
  }
88
- return (`Persona '${verification.persona}' ${parts.join(' and ')}. ` +
88
+ return (`VirtualUser '${verification.virtualUserId}' ${parts.join(' and ')}. ` +
89
89
  `Declared: ${verification.expected.join(', ') || 'none'}. ` +
90
90
  `On the stage: ${verification.actual.join(', ') || 'none'}. ` +
91
- `Refusing to run — findings from a persona whose roles have drifted are about the seed, not the product.`);
91
+ `Refusing to run — findings from a virtual user whose roles have drifted are about the seed, not the product.`);
92
92
  };
93
93
  /** Convenience: the meta a run needs, with `runnable` already resolved. */
94
- export const runnablePersonas = (meta) => Object.values(meta).filter((persona) => persona.runnable);
94
+ export const runnableVirtualUsers = (meta) => Object.values(meta).filter((virtualUser) => virtualUser.runnable);
@@ -1,14 +1,14 @@
1
1
  /**
2
- * Which of the product's agents a persona can actually talk to.
2
+ * Which of the product's agents a virtual user can actually talk to.
3
3
  *
4
- * An agent is not something a persona declares. It is something a persona
4
+ * An agent is not something a virtual user declares. It is something a virtual user
5
5
  * *reaches*, under exactly the rule an RPC is reached by: `CoreAgent.scopes`
6
- * is checked against the session, so a persona holding the scopes an agent
6
+ * is checked against the session, so a virtual user holding the scopes an agent
7
7
  * requires finds it in front of them, and one that does not, does not.
8
8
  *
9
- * That is the whole reason a persona has no `agent:` field. Naming one would
9
+ * That is the whole reason a virtual user has no `agent:` field. Naming one would
10
10
  * add no capability the roles do not already confer, and would narrow the
11
- * persona to a single brain when the interesting behaviour is the choice
11
+ * virtual user to a single brain when the interesting behaviour is the choice
12
12
  * between calling the API directly and handing the work to a specialist.
13
13
  */
14
14
  /** The part of an agent's meta this needs. */
@@ -28,12 +28,12 @@ export interface ReachableAgent {
28
28
  * That key is the export's own name, which is what `addAgent` stores and what
29
29
  * `resolveAgent` looks up. The `name` an agent declares in its config is a
30
30
  * display label and is frequently something else entirely — offering that one
31
- * hands the persona a name the server cannot resolve, and the run dies on a
31
+ * hands the virtual user a name the server cannot resolve, and the run dies on a
32
32
  * 500 the moment it takes the offer.
33
33
  *
34
34
  * Like {@link reachableCatalogue}, this narrows *what is offered* and never
35
35
  * what is enforced: the server decides who may talk to what, and an agent
36
- * answering a persona this filter would have withheld is a finding rather than
36
+ * answering a virtual user this filter would have withheld is a finding rather than
37
37
  * a bug here.
38
38
  *
39
39
  * An agent declaring no scopes is offered to everybody, on the same terms as a
@@ -5,12 +5,12 @@ import { hasScopes } from '../../scopes.js';
5
5
  * That key is the export's own name, which is what `addAgent` stores and what
6
6
  * `resolveAgent` looks up. The `name` an agent declares in its config is a
7
7
  * display label and is frequently something else entirely — offering that one
8
- * hands the persona a name the server cannot resolve, and the run dies on a
8
+ * hands the virtual user a name the server cannot resolve, and the run dies on a
9
9
  * 500 the moment it takes the offer.
10
10
  *
11
11
  * Like {@link reachableCatalogue}, this narrows *what is offered* and never
12
12
  * what is enforced: the server decides who may talk to what, and an agent
13
- * answering a persona this filter would have withheld is a finding rather than
13
+ * answering a virtual user this filter would have withheld is a finding rather than
14
14
  * a bug here.
15
15
  *
16
16
  * An agent declaring no scopes is offered to everybody, on the same terms as a
@@ -1,5 +1,5 @@
1
1
  import type { ScopeDefinitionMeta } from '../scope/scope.types.js';
2
- import type { PersonaMeta } from './persona.types.js';
2
+ import type { VirtualUserMeta } from './virtual-user-declaration.types.js';
3
3
  /**
4
4
  * The root every app grant hangs off. One segment, so a grant of `app` alone
5
5
  * means "may use all of them" under the same parent-grant rule the admin tree
@@ -10,16 +10,16 @@ export declare const APP_SCOPE_ROOT = "app";
10
10
  /** `app:staff` for `staff`. */
11
11
  export declare const appScopeId: (app: string) => string;
12
12
  /**
13
- * The apps named across the declared personas, sorted and deduplicated.
13
+ * The apps named across the declared virtual users, sorted and deduplicated.
14
14
  *
15
- * The personas *are* the registry. There is no separate list of apps to keep in
15
+ * The virtual users *are* the registry. There is no separate list of apps to keep in
16
16
  * step, because a frontend nobody signs into is not a thing the auth layer has
17
17
  * an opinion about — and the moment somebody does sign into it, they are a
18
- * persona and they name it.
18
+ * virtual user and they name it.
19
19
  */
20
- export declare const declaredApps: (personas: PersonaMeta[]) => string[];
20
+ export declare const declaredApps: (virtualUsers: VirtualUserMeta[]) => string[];
21
21
  /**
22
- * The `app` scope tree, synthesised from the personas that name an app.
22
+ * The `app` scope tree, synthesised from the virtual users that name an app.
23
23
  *
24
24
  * Which frontend a person may sign into is a different question from what they
25
25
  * may do once inside it, but it is the same *kind* of question — a grant that
@@ -31,11 +31,11 @@ export declare const declaredApps: (personas: PersonaMeta[]) => string[];
31
31
  *
32
32
  * Synthesised rather than written by hand because the declaration already
33
33
  * exists. Asking an app to also spell out `defineScope({ app: { staff: {} } })`
34
- * beside its personas invites the two to drift, and the failure that produces —
35
- * a persona provisioned with a grant the vocabulary no longer declares — is one
34
+ * beside its virtual users invites the two to drift, and the failure that produces —
35
+ * a virtual user provisioned with a grant the vocabulary no longer declares — is one
36
36
  * `pikku scopes prune` away from silently revoking sign-in.
37
37
  *
38
- * Null when no persona names an app, which is the single-frontend case: nothing
38
+ * Null when no virtual user names an app, which is the single-frontend case: nothing
39
39
  * to declare, and no empty root cluttering the console's grant list.
40
40
  */
41
- export declare const buildAppScopeDefinition: (personas: PersonaMeta[]) => ScopeDefinitionMeta | null;
41
+ export declare const buildAppScopeDefinition: (virtualUsers: VirtualUserMeta[]) => ScopeDefinitionMeta | null;
@@ -8,20 +8,20 @@ export const APP_SCOPE_ROOT = 'app';
8
8
  /** `app:staff` for `staff`. */
9
9
  export const appScopeId = (app) => `${APP_SCOPE_ROOT}:${app}`;
10
10
  /**
11
- * The apps named across the declared personas, sorted and deduplicated.
11
+ * The apps named across the declared virtual users, sorted and deduplicated.
12
12
  *
13
- * The personas *are* the registry. There is no separate list of apps to keep in
13
+ * The virtual users *are* the registry. There is no separate list of apps to keep in
14
14
  * step, because a frontend nobody signs into is not a thing the auth layer has
15
15
  * an opinion about — and the moment somebody does sign into it, they are a
16
- * persona and they name it.
16
+ * virtual user and they name it.
17
17
  */
18
- export const declaredApps = (personas) => [
19
- ...new Set(personas
20
- .map((persona) => persona.app)
18
+ export const declaredApps = (virtualUsers) => [
19
+ ...new Set(virtualUsers
20
+ .map((virtualUser) => virtualUser.app)
21
21
  .filter((app) => typeof app === 'string' && app !== '')),
22
22
  ].sort();
23
23
  /**
24
- * The `app` scope tree, synthesised from the personas that name an app.
24
+ * The `app` scope tree, synthesised from the virtual users that name an app.
25
25
  *
26
26
  * Which frontend a person may sign into is a different question from what they
27
27
  * may do once inside it, but it is the same *kind* of question — a grant that
@@ -33,15 +33,15 @@ export const declaredApps = (personas) => [
33
33
  *
34
34
  * Synthesised rather than written by hand because the declaration already
35
35
  * exists. Asking an app to also spell out `defineScope({ app: { staff: {} } })`
36
- * beside its personas invites the two to drift, and the failure that produces —
37
- * a persona provisioned with a grant the vocabulary no longer declares — is one
36
+ * beside its virtual users invites the two to drift, and the failure that produces —
37
+ * a virtual user provisioned with a grant the vocabulary no longer declares — is one
38
38
  * `pikku scopes prune` away from silently revoking sign-in.
39
39
  *
40
- * Null when no persona names an app, which is the single-frontend case: nothing
40
+ * Null when no virtual user names an app, which is the single-frontend case: nothing
41
41
  * to declare, and no empty root cluttering the console's grant list.
42
42
  */
43
- export const buildAppScopeDefinition = (personas) => {
44
- const apps = declaredApps(personas);
43
+ export const buildAppScopeDefinition = (virtualUsers) => {
44
+ const apps = declaredApps(virtualUsers);
45
45
  if (apps.length === 0) {
46
46
  return null;
47
47
  }
@@ -28,7 +28,7 @@ export declare const renderCatalogue: (entries: readonly ApiCatalogueEntry[]) =>
28
28
  /**
29
29
  * The entries a given user may reach: approval-gated calls dropped unless the
30
30
  * run opted in, mutations dropped for a read-only disposition, and anything the
31
- * actor's tier cannot satisfy dropped when the caller supplied one.
31
+ * virtual user's tier cannot satisfy dropped when the caller supplied one.
32
32
  *
33
33
  * This narrows *what is offered*, never what is enforced — the server is the
34
34
  * only thing that decides who may call what, and a call this filter would have
@@ -38,7 +38,7 @@ export declare const reachableCatalogue: (entries: readonly ApiCatalogueEntry[],
38
38
  readOnly?: boolean;
39
39
  allowApprovalRequired?: boolean;
40
40
  /**
41
- * The scopes this persona holds, resolved from its roles. Omit to keep
41
+ * The scopes this virtual user holds, resolved from its roles. Omit to keep
42
42
  * every entry.
43
43
  *
44
44
  * Scopes rather than permission-function names: a function's `scopes` are
@@ -49,7 +49,7 @@ export declare const reachableCatalogue: (entries: readonly ApiCatalogueEntry[],
49
49
  scopes?: readonly string[];
50
50
  }) => ApiCatalogueEntry[];
51
51
  /**
52
- * Endpoints this persona is *not* entitled to reach.
52
+ * Endpoints this virtual user is *not* entitled to reach.
53
53
  *
54
54
  * The adversarial oracle. Withheld from the catalogue, so reaching one takes
55
55
  * initiative — and a 2xx from one is the finding, because the server is the
@@ -67,7 +67,7 @@ export const renderCatalogue = (entries) => entries.map(renderEntry).join('\n');
67
67
  /**
68
68
  * The entries a given user may reach: approval-gated calls dropped unless the
69
69
  * run opted in, mutations dropped for a read-only disposition, and anything the
70
- * actor's tier cannot satisfy dropped when the caller supplied one.
70
+ * virtual user's tier cannot satisfy dropped when the caller supplied one.
71
71
  *
72
72
  * This narrows *what is offered*, never what is enforced — the server is the
73
73
  * only thing that decides who may call what, and a call this filter would have
@@ -84,7 +84,7 @@ export const reachableCatalogue = (entries, { readOnly = false, allowApprovalReq
84
84
  return true;
85
85
  });
86
86
  /**
87
- * Endpoints this persona is *not* entitled to reach.
87
+ * Endpoints this virtual user is *not* entitled to reach.
88
88
  *
89
89
  * The adversarial oracle. Withheld from the catalogue, so reaching one takes
90
90
  * initiative — and a 2xx from one is the finding, because the server is the
@@ -1,23 +1,23 @@
1
1
  import type { VirtualUserDisposition } from '../virtual-user/virtual-user.types.js';
2
2
  import type { VirtualUserTuning } from '../virtual-user/virtual-user-dispositions.js';
3
3
  /**
4
- * One login belonging to a persona.
4
+ * One login belonging to a virtual user.
5
5
  *
6
6
  * Deliberately near-empty in the common case: the address is computed from the
7
- * persona's name and the run id, and the password derives from
8
- * `SCENARIO_ACTOR_SECRET`, so there is nothing left to write down. It fills up
7
+ * virtual user's name and the run id, and sign-in needs no password, so there is
8
+ * nothing left to write down. It fills up
9
9
  * only for a provider login.
10
10
  *
11
11
  * Shaped after better-auth's `account` table, which is what most pikku apps are
12
12
  * actually running: `providerId` plus the provider's own id, and no email
13
13
  * column — the address belongs to the person, not the login.
14
14
  */
15
- export type CorePersonaAccount = {
15
+ export type CoreVirtualUserAccount = {
16
16
  /**
17
17
  * The identity provider, e.g. `'google'`. Omit for email and password.
18
18
  *
19
19
  * A provider account is declarable but not runnable: driving its consent
20
- * screen needs a human, so `pikku persona run` refuses rather than failing
20
+ * screen needs a human, so `pikku virtual-user run` refuses rather than failing
21
21
  * somewhere inside a browser.
22
22
  */
23
23
  provider?: string;
@@ -26,12 +26,12 @@ export type CorePersonaAccount = {
26
26
  * A person who uses your product: a name, a job, a temperament, and what they
27
27
  * are trying to get done.
28
28
  *
29
- * This is Cooper's persona — "a fictional character created to represent a user
29
+ * This is Cooper's virtual user — "a fictional character created to represent a user
30
30
  * type" — with the two things a synthetic user additionally needs: the roles
31
- * that say what they may do, and the account they log in with. A persona while
31
+ * that say what they may do, and the account they log in with. A virtual user while
32
32
  * running is a virtual user; there is no second declaration for that.
33
33
  */
34
- export type CorePersona = {
34
+ export type CoreVirtualUser = {
35
35
  /** The person's name, as a person would give it. */
36
36
  name: string;
37
37
  jobTitle?: string;
@@ -41,14 +41,14 @@ export type CorePersona = {
41
41
  * serves, an asset on a CDN, a Gravatar link.
42
42
  *
43
43
  * Optional because it is decoration. Omitted, the console draws a colour and
44
- * an icon derived from the persona's id, which is stable, needs no network and
44
+ * an icon derived from the virtual user's id, which is stable, needs no network and
45
45
  * is more legible than the identicon a synthetic address would resolve to —
46
46
  * which is also why nothing is derived here.
47
47
  */
48
48
  avatarUrl?: string;
49
49
  /**
50
50
  * The roles this person holds. Only system roles may be named — a custom
51
- * role can be deleted from the console, so a persona pinned to one silently
51
+ * role can be deleted from the console, so a virtual user pinned to one silently
52
52
  * stops testing what it claims to.
53
53
  *
54
54
  * These are granted, not merely recorded: the generated seed applies them,
@@ -74,15 +74,15 @@ export type CorePersona = {
74
74
  /** Fixture paths this person may upload, relative to the project root. */
75
75
  fixtures?: string[];
76
76
  /** Their login. `{}` — email and password — is the normal case. */
77
- account?: CorePersonaAccount;
77
+ account?: CoreVirtualUserAccount;
78
78
  /**
79
79
  * Further logins belonging to the same human, keyed by name.
80
80
  *
81
- * Rare, and shaped that way on purpose. All of them share the persona's one
81
+ * Rare, and shaped that way on purpose. All of them share the virtual user's one
82
82
  * computed address: better-auth's `allowDifferentEmails` defaults to false,
83
83
  * so distinct addresses would be refused linking by the library under test.
84
84
  */
85
- linkedAccounts?: Record<string, CorePersonaAccount>;
85
+ linkedAccounts?: Record<string, CoreVirtualUserAccount>;
86
86
  /**
87
87
  * The environments this person may run against, by config key.
88
88
  *
@@ -100,7 +100,7 @@ export type CorePersona = {
100
100
  *
101
101
  * An assertion, not something inferred from the absence of a disposition:
102
102
  * `target` *must not* run, because a run signing in as her would race the
103
- * scenario banning her. "Nobody has given this persona a disposition yet" is
103
+ * scenario banning her. "Nobody has given this virtual user a disposition yet" is
104
104
  * a different state, and collapsing the two turns a deliberate constraint
105
105
  * into a flaky suite.
106
106
  */
@@ -110,20 +110,20 @@ export type CorePersona = {
110
110
  *
111
111
  * A person uses one of them — staff their app, customers theirs — and that is
112
112
  * what decides where a browser step navigates: `/dashboard` is a different
113
- * page in each, so the actor settles which one without the scenario saying.
113
+ * page in each, so the virtual user settles which one without the scenario saying.
114
114
  * The runner takes the base url per app (`--app-url <app>=<url>`, or
115
- * `appUrls` on the environment) and each actor browses against its own.
115
+ * `appUrls` on the environment) and each virtual user browses against its own.
116
116
  *
117
117
  * Omitted is the normal case: one app, one url, nothing to say.
118
118
  */
119
119
  app?: string;
120
120
  };
121
- /** Personas to declare, keyed by id. */
122
- export type CorePersonas = Record<string, CorePersona>;
123
- export type PersonaAccountMeta = {
121
+ /** Virtual users to declare, keyed by id. */
122
+ export type CoreVirtualUsers = Record<string, CoreVirtualUser>;
123
+ export type VirtualUserAccountMeta = {
124
124
  provider?: string;
125
125
  };
126
- export type PersonaMeta = {
126
+ export type VirtualUserMeta = {
127
127
  /** The key it was declared under. */
128
128
  id: string;
129
129
  name: string;
@@ -137,8 +137,8 @@ export type PersonaMeta = {
137
137
  disposition?: VirtualUserDisposition;
138
138
  tuning?: VirtualUserTuning;
139
139
  fixtures?: string[];
140
- account?: PersonaAccountMeta;
141
- linkedAccounts?: Record<string, PersonaAccountMeta>;
140
+ account?: VirtualUserAccountMeta;
141
+ linkedAccounts?: Record<string, VirtualUserAccountMeta>;
142
142
  /** Absent means "everywhere but production", which is resolved, not stored. */
143
143
  environments?: string[];
144
144
  runnable: boolean;
@@ -146,5 +146,5 @@ export type PersonaMeta = {
146
146
  app?: string;
147
147
  sourceFile?: string;
148
148
  };
149
- export type PersonaDefinitions = PersonaMeta[];
150
- export type PersonasMeta = Record<string, PersonaMeta>;
149
+ export type VirtualUserDefinitions = VirtualUserMeta[];
150
+ export type VirtualUsersMeta = Record<string, VirtualUserMeta>;
@@ -25,9 +25,9 @@ export const deriveCatalogue = (functions, schemas = {}) => {
25
25
  if (meta.expose !== true)
26
26
  continue;
27
27
  // A virtual user is not offered the machinery that runs virtual users. A
28
- // persona whose role carries `virtualUser:*` would otherwise be able to
28
+ // virtual user whose role carries `virtualUser:*` would otherwise be able to
29
29
  // start further runs, read every run's findings — an adversarial run's
30
- // transcript is working exploits against this same app — and put a persona
30
+ // transcript is working exploits against this same app — and put a virtual user
31
31
  // on a schedule that outlives it. Same reasoning as the scenario-step rule
32
32
  // above: the tool is about the run, not about the product being explored.
33
33
  if (meta.scopes?.some((scope) => scope.split(':')[0] === 'virtualUser')) {
@@ -68,15 +68,21 @@ export const deriveCatalogue = (functions, schemas = {}) => {
68
68
  const scenarioSteps = (meta) => {
69
69
  if (meta.steps?.length) {
70
70
  return meta.steps.flatMap((step) => step.type === 'scenarioStep'
71
- ? [{ stepFunc: step.stepFunc, phase: step.phase, actor: step.actor }]
71
+ ? [
72
+ {
73
+ stepFunc: step.stepFunc,
74
+ phase: step.phase,
75
+ virtualUser: step.virtualUser,
76
+ },
77
+ ]
72
78
  : []);
73
79
  }
74
80
  // knowledge: decisions/internals/virtual-user-step-order-comes-from-insertion-order.md
75
81
  return Object.values(meta.nodes ?? {}).flatMap((node) => {
76
- const { rpcName, scenarioStepPhase, actor } = (node ?? {});
82
+ const { rpcName, scenarioStepPhase, virtualUser } = (node ?? {});
77
83
  if (!rpcName || !scenarioStepPhase)
78
84
  return [];
79
- return [{ stepFunc: rpcName, phase: scenarioStepPhase, actor }];
85
+ return [{ stepFunc: rpcName, phase: scenarioStepPhase, virtualUser }];
80
86
  });
81
87
  };
82
88
  /**
@@ -109,7 +115,7 @@ export const deriveIntents = (workflows, functions = {}) => {
109
115
  phase: step.phase,
110
116
  // knowledge: decisions/internals/a-scenario-step-template-is-offered-unfilled.md
111
117
  description: template ?? description ?? '',
112
- actor: step.actor,
118
+ virtualUser: step.virtualUser,
113
119
  }));
114
120
  }
115
121
  intents.push({
@@ -118,7 +124,7 @@ export const deriveIntents = (workflows, functions = {}) => {
118
124
  description: meta.description,
119
125
  steps: steps.length ? steps : undefined,
120
126
  tags: meta.tags?.length ? meta.tags : undefined,
121
- personas: meta.actors?.length ? meta.actors : undefined,
127
+ virtualUsers: meta.virtualUsers?.length ? meta.virtualUsers : undefined,
122
128
  });
123
129
  }
124
130
  return intents;
@@ -5,7 +5,7 @@ import type { VirtualUserDisposition } from './virtual-user.types.js';
5
5
  * behaviour the dials cannot express on their own.
6
6
  */
7
7
  export interface DispositionProfile {
8
- /** Appended to the persona instructions. Behaviour, never identity. */
8
+ /** Appended to the virtual user instructions. Behaviour, never identity. */
9
9
  instructions: string;
10
10
  /**
11
11
  * Relative weights for the scheduler's four moves. `continue` dominating is