@pikku/core 0.12.141 → 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 (237) hide show
  1. package/CHANGELOG.md +33 -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/index.d.ts +2 -2
  11. package/dist/services/index.js +1 -1
  12. package/dist/services/meta-service.d.ts +6 -6
  13. package/dist/services/meta-service.js +8 -8
  14. package/dist/{wirings/workflow → services}/scenario-cookie-jar.d.ts +2 -0
  15. package/dist/{wirings/workflow → services}/scenario-cookie-jar.js +18 -14
  16. package/dist/services/system-role-guard.d.ts +1 -1
  17. package/dist/services/virtual-user-sign-in.d.ts +52 -0
  18. package/dist/services/virtual-user-sign-in.js +80 -0
  19. package/dist/services/virtual-user-statement.d.ts +31 -0
  20. package/dist/services/virtual-user-statement.js +24 -0
  21. package/dist/services/{personas-service.d.ts → virtual-users-service.d.ts} +25 -23
  22. package/dist/services/{personas-service.js → virtual-users-service.js} +3 -3
  23. package/dist/types/core.types.d.ts +11 -11
  24. package/dist/wirings/role/define-system-role.d.ts +1 -1
  25. package/dist/wirings/role/define-system-role.js +1 -1
  26. package/dist/wirings/{actor-flow/actor-flow.types.d.ts → virtual-user/conversation/conversation.types.d.ts} +5 -5
  27. package/dist/wirings/virtual-user/conversation/index.d.ts +2 -0
  28. package/dist/wirings/virtual-user/conversation/run-conversation.d.ts +36 -0
  29. package/dist/wirings/{actor-flow → virtual-user/conversation}/run-conversation.js +30 -30
  30. package/dist/wirings/virtual-user/define-virtual-users.d.ts +32 -0
  31. package/dist/wirings/{persona/define-personas.js → virtual-user/define-virtual-users.js} +9 -9
  32. package/dist/wirings/virtual-user/index.d.ts +30 -7
  33. package/dist/wirings/virtual-user/index.js +23 -4
  34. package/dist/wirings/virtual-user/prepare-virtual-user-run.d.ts +8 -8
  35. package/dist/wirings/virtual-user/prepare-virtual-user-run.js +9 -9
  36. package/dist/wirings/virtual-user/run-virtual-user.d.ts +6 -6
  37. package/dist/wirings/virtual-user/run-virtual-user.js +10 -10
  38. package/dist/wirings/{persona/validate-personas.d.ts → virtual-user/validate-virtual-users.d.ts} +11 -11
  39. package/dist/wirings/{persona/validate-personas.js → virtual-user/validate-virtual-users.js} +24 -24
  40. package/dist/wirings/virtual-user/virtual-user-agents.d.ts +7 -7
  41. package/dist/wirings/virtual-user/virtual-user-agents.js +2 -2
  42. package/dist/wirings/{persona/persona-app-scopes.d.ts → virtual-user/virtual-user-app-scopes.d.ts} +10 -10
  43. package/dist/wirings/{persona/persona-app-scopes.js → virtual-user/virtual-user-app-scopes.js} +12 -12
  44. package/dist/wirings/virtual-user/virtual-user-catalogue.d.ts +3 -3
  45. package/dist/wirings/virtual-user/virtual-user-catalogue.js +2 -2
  46. package/dist/wirings/{persona/persona.types.d.ts → virtual-user/virtual-user-declaration.types.d.ts} +24 -24
  47. package/dist/wirings/virtual-user/virtual-user-derive.js +13 -7
  48. package/dist/wirings/virtual-user/virtual-user-dispositions.d.ts +1 -1
  49. package/dist/wirings/{persona/persona-email.d.ts → virtual-user/virtual-user-email.d.ts} +11 -11
  50. package/dist/wirings/{persona/persona-email.js → virtual-user/virtual-user-email.js} +19 -19
  51. package/dist/wirings/{persona/persona-environments.d.ts → virtual-user/virtual-user-environments.d.ts} +10 -10
  52. package/dist/wirings/{persona/persona-environments.js → virtual-user/virtual-user-environments.js} +21 -21
  53. package/dist/wirings/virtual-user/virtual-user-intents.d.ts +3 -3
  54. package/dist/wirings/virtual-user/virtual-user-intents.js +3 -3
  55. package/dist/wirings/{persona/persona-mailbox.d.ts → virtual-user/virtual-user-mailbox.d.ts} +4 -4
  56. package/dist/wirings/virtual-user/virtual-user-run-store.d.ts +4 -4
  57. package/dist/wirings/virtual-user/virtual-user-scaffold.d.ts +42 -61
  58. package/dist/wirings/virtual-user/virtual-user-scaffold.js +69 -117
  59. package/dist/wirings/virtual-user/virtual-user-schedule-store.d.ts +15 -15
  60. package/dist/wirings/virtual-user/virtual-user-schedule.d.ts +6 -6
  61. package/dist/wirings/virtual-user/virtual-user-schedule.js +11 -11
  62. package/dist/wirings/virtual-user/virtual-user-target.d.ts +6 -6
  63. package/dist/wirings/virtual-user/virtual-user-target.js +5 -5
  64. package/dist/wirings/virtual-user/virtual-user.types.d.ts +11 -11
  65. package/dist/wirings/virtual-user/virtual-user.types.js +1 -1
  66. package/dist/wirings/workflow/dsl/workflow-dsl.types.d.ts +9 -9
  67. package/dist/wirings/workflow/pikku-scenario-service.d.ts +28 -28
  68. package/dist/wirings/workflow/pikku-scenario-service.js +77 -77
  69. package/dist/wirings/workflow/pikku-workflow-service.d.ts +2 -2
  70. package/dist/wirings/workflow/pikku-workflow-service.js +4 -4
  71. package/dist/wirings/workflow/scenario-prose.d.ts +8 -8
  72. package/dist/wirings/workflow/scenario-prose.js +12 -8
  73. package/dist/wirings/workflow/scenario-run.types.d.ts +13 -13
  74. package/dist/wirings/workflow/scenario-step.types.d.ts +23 -23
  75. package/dist/wirings/workflow/workflow.types.d.ts +2 -2
  76. package/knowledge/decisions/index.md +0 -2
  77. package/knowledge/decisions/internals/a-credential-name-declared-twice-must-agree-on-schema-and-type.md +29 -0
  78. package/knowledge/decisions/internals/a-grant-can-name-a-scope-declared-without-its-addon.md +29 -0
  79. package/knowledge/decisions/internals/a-scheduled-task-writes-its-caller-session-before-it-runs.md +22 -0
  80. package/knowledge/decisions/internals/a-secret-set-in-memory-shadows-the-variable-of-the-same-name.md +28 -0
  81. package/knowledge/decisions/internals/a-virtual-user-acts-through-invokeraw-so-a-refusal-is-data.md +32 -0
  82. package/knowledge/decisions/internals/a-virtual-user-cadence-is-a-row-not-a-timer.md +12 -12
  83. package/knowledge/decisions/internals/a-virtual-user-converse-signs-in-before-its-first-turn.md +24 -0
  84. package/knowledge/decisions/internals/a-virtual-user-email-is-computed-with-the-run-id-as-a-sub-address.md +34 -0
  85. package/knowledge/decisions/internals/a-virtual-user-run-is-not-a-workflow-but-it-needs-a-trigger.md +2 -2
  86. package/knowledge/decisions/internals/an-agent-is-offered-under-its-registry-key-and-only-within-the-virtual-users-scopes.md +27 -0
  87. package/knowledge/decisions/internals/an-oauth2-credential-implies-an-optional-app-secret.md +26 -0
  88. package/knowledge/decisions/internals/analytics-identity-resolvers-run-in-declared-order.md +22 -0
  89. package/knowledge/decisions/internals/feature-flag-declarations-refuse-an-empty-anyof-and-conflicting-scopes.md +22 -0
  90. package/knowledge/decisions/internals/feature-flags-fail-open-and-never-enforce-access.md +28 -0
  91. package/knowledge/decisions/internals/flag-sync-rewrites-only-the-declared-text.md +24 -0
  92. package/knowledge/decisions/internals/in-a-scenario-a-4xx-is-data-not-an-exception.md +3 -3
  93. package/knowledge/decisions/internals/index.md +46 -31
  94. package/knowledge/decisions/internals/one-project-shape-check-two-validators.md +10 -10
  95. package/knowledge/decisions/internals/role-names-cannot-contain-the-scope-separator.md +30 -0
  96. package/knowledge/decisions/internals/scenario-agent-calls-sign-in-on-401-only.md +23 -19
  97. package/knowledge/decisions/internals/scenario-steps-return-drained-response-records.md +2 -2
  98. package/knowledge/decisions/internals/scenarios-live-in-files-named-for-them.md +12 -12
  99. package/knowledge/decisions/internals/the-core-scope-validator-refuses-separator-wildcard-and-empty-segments.md +22 -0
  100. package/knowledge/decisions/internals/the-pikku-user-id-resolves-in-a-fixed-order.md +24 -0
  101. 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
  102. 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
  103. package/knowledge/decisions/internals/validate-checks-virtual-users-through-a-shared-module.md +43 -0
  104. 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
  105. package/knowledge/decisions/internals/virtual-user-conversation-verdicts-are-llm-self-evaluations.md +25 -0
  106. package/knowledge/decisions/internals/virtual-user-conversations-seed-a-hidden-kickoff-message.md +23 -0
  107. package/knowledge/decisions/internals/virtual-users-are-declared-in-one-definevirtualusers-call-per-codebase.md +30 -0
  108. package/knowledge/decisions/internals/workflow-core-never-imports-a-browser-driver.md +5 -5
  109. package/knowledge/decisions/internals/workflow-run-capabilities-are-extensions-not-subclasses.md +2 -2
  110. package/knowledge/decisions/internals/workflow-scenario-assertions-never-retry-and-record-one-step.md +4 -4
  111. package/knowledge/decisions/internals/workflow-scenario-hooks-are-a-scenario-only-affordance.md +1 -1
  112. package/knowledge/decisions/internals/workflow-scenario-prose-is-rendered-from-typed-calls-not-parsed-from-english.md +2 -2
  113. package/knowledge/decisions/internals/workflow-scenario-step-targets-are-string-literals-for-the-inspector.md +1 -1
  114. package/knowledge/decisions/internals/workflow-suspended-runs-keep-their-in-process-context.md +1 -1
  115. package/knowledge/decisions/security/a-bearer-token-checked-against-a-secret-reads-it-per-request.md +26 -0
  116. package/knowledge/decisions/security/a-call-without-a-session-raises-missing-session-not-forbidden.md +20 -0
  117. package/knowledge/decisions/security/a-caller-cookie-wins-over-the-jar-for-the-same-name.md +23 -0
  118. package/knowledge/decisions/security/a-channel-lifecycle-acts-as-the-socket-upgrade-session.md +16 -0
  119. package/knowledge/decisions/security/a-channel-requires-a-session-unless-it-says-auth-false.md +26 -0
  120. package/knowledge/decisions/security/a-credential-read-path-follows-its-declared-type-not-absence.md +32 -0
  121. package/knowledge/decisions/security/a-credential-type-is-a-literal-the-inspector-checks-at-declaration.md +27 -0
  122. package/knowledge/decisions/security/a-first-party-cookie-is-written-only-after-its-purposes-consent.md +33 -0
  123. package/knowledge/decisions/security/a-readonly-session-reaches-only-readonly-functions.md +25 -0
  124. package/knowledge/decisions/security/a-scaffold-flag-says-a-surface-exists-not-who-may-call-it.md +10 -5
  125. package/knowledge/decisions/security/a-scheduled-virtual-user-run-needs-no-admin-at-run-time.md +27 -0
  126. package/knowledge/decisions/security/a-scoped-credential-view-is-limited-to-declared-names.md +26 -0
  127. package/knowledge/decisions/security/a-scoped-secret-view-reads-only-declared-keys-and-never-writes.md +25 -0
  128. package/knowledge/decisions/security/a-secret-is-sent-only-to-the-hosts-it-declares.md +29 -0
  129. package/knowledge/decisions/security/a-step-can-name-an-identity-no-virtual-user-holds-through-per-call-headers.md +25 -0
  130. package/knowledge/decisions/security/a-stopped-run-by-id-skips-the-ownership-check.md +24 -0
  131. package/knowledge/decisions/security/a-vault-secret-redacts-on-serialization-and-throws-on-coercion.md +29 -0
  132. package/knowledge/decisions/security/a-virtual-user-catalogue-entry-is-filtered-by-scope-and-approval-is-denied-by-default.md +30 -0
  133. package/knowledge/decisions/security/a-virtual-user-environment-fails-closed.md +38 -0
  134. package/knowledge/decisions/security/a-virtual-user-never-runs-against-production.md +23 -0
  135. package/knowledge/decisions/security/a-virtual-user-reads-its-roles-from-the-apps-own-rpc-before-better-auth.md +27 -0
  136. package/knowledge/decisions/security/a-virtual-user-run-treats-an-unresolved-environment-as-production.md +41 -0
  137. package/knowledge/decisions/security/a-virtual-user-scopes-are-expanded-from-its-roles-once-at-run-preparation.md +25 -0
  138. package/knowledge/decisions/security/a-virtual-user-whose-roles-drift-from-the-declaration-refuses-to-run.md +32 -0
  139. package/knowledge/decisions/security/addon-screen-scopes-hide-and-do-not-enforce.md +20 -0
  140. package/knowledge/decisions/security/an-addon-header-can-only-narrow-the-session.md +26 -0
  141. package/knowledge/decisions/security/an-addon-instance-reads-only-the-secrets-and-credentials-it-is-named.md +33 -0
  142. package/knowledge/decisions/security/an-addon-may-reach-only-itself-and-the-addons-it-uses.md +16 -0
  143. package/knowledge/decisions/security/an-api-key-is-jwt-decoded-into-the-session.md +26 -0
  144. package/knowledge/decisions/security/an-external-virtual-user-caller-presents-one-signed-statement.md +38 -0
  145. package/knowledge/decisions/security/an-inbound-webhook-source-dispatches-only-verified-requests.md +31 -0
  146. package/knowledge/decisions/security/an-mcp-call-with-no-http-request-stays-anonymous.md +18 -0
  147. package/knowledge/decisions/security/an-omitted-user-id-addresses-the-platform-credential.md +32 -0
  148. package/knowledge/decisions/security/analytics-identity-is-stamped-from-the-session-not-the-event.md +24 -0
  149. package/knowledge/decisions/security/app-access-is-a-scope-tree-synthesised-from-virtual-users.md +26 -0
  150. package/knowledge/decisions/security/approval-policy-decides-who-answers-a-tool-call.md +27 -0
  151. package/knowledge/decisions/security/auth-false-on-a-session-function-warns-and-still-requires-one.md +25 -0
  152. package/knowledge/decisions/security/bearer-token-hashes-are-unpeppered-sha256.md +18 -0
  153. package/knowledge/decisions/security/consent-is-read-from-a-cookie-and-a-recognised-denial-counts-as-refusal.md +29 -0
  154. package/knowledge/decisions/security/cors-defaults-to-any-origin-and-refuses-the-wildcard-with-credentials.md +22 -0
  155. package/knowledge/decisions/security/credential-backends-share-one-conformance-suite-for-per-user-isolation.md +29 -0
  156. package/knowledge/decisions/security/each-stage-has-one-virtual-user-key-pair.md +27 -0
  157. package/knowledge/decisions/security/hmac-verification-fails-closed-on-empty-inputs.md +25 -0
  158. package/knowledge/decisions/security/index.md +88 -24
  159. package/knowledge/decisions/security/local-content-refuses-private-buckets-under-the-public-segment.md +21 -0
  160. package/knowledge/decisions/security/mail-is-allowlisted-before-a-virtual-user-run-sees-it.md +38 -0
  161. package/knowledge/decisions/security/mcp-resources-tools-and-prompts-take-sessionless-functions.md +18 -0
  162. package/knowledge/decisions/security/origin-locked-routes-match-the-origin-exactly.md +22 -0
  163. package/knowledge/decisions/security/oss-auth-works-without-pikku-studio-cloud.md +26 -0
  164. package/knowledge/decisions/security/outbound-webhooks-use-an-allowlist-when-configured.md +24 -0
  165. package/knowledge/decisions/security/pikku-carries-virtual-user-scopes-as-data-and-the-app-grants-them.md +35 -0
  166. package/knowledge/decisions/security/scoped-content-access-is-a-root-folder-with-longest-prefix-grants.md +31 -0
  167. package/knowledge/decisions/security/services-without-secrets-get-a-throwing-secrets-accessor.md +22 -0
  168. package/knowledge/decisions/security/shared-guards-refuse-to-mutate-or-shadow-a-system-role.md +34 -0
  169. package/knowledge/decisions/security/signed-uploads-are-verified-by-the-same-check-as-signed-reads.md +26 -0
  170. package/knowledge/decisions/security/system-role-scopes-must-be-declared-with-define-scope.md +24 -0
  171. package/knowledge/decisions/security/system-roles-are-additive-and-immutable.md +16 -0
  172. package/knowledge/decisions/security/the-channel-rpc-does-not-send-the-callers-session.md +21 -0
  173. package/knowledge/decisions/security/the-remote-jobs-inbox-fails-closed-without-a-secret.md +24 -0
  174. package/knowledge/decisions/security/the-remote-rpc-gate-fails-closed-and-binds-the-token-to-its-function.md +34 -0
  175. package/knowledge/decisions/security/the-workflow-status-stream-rechecks-ownership-on-every-poll.md +16 -0
  176. package/knowledge/decisions/security/two-credentials-on-one-key-must-share-a-read-mode.md +29 -0
  177. package/knowledge/decisions/security/verified-bearer-claims-are-pass-through-data-that-core-never-fills.md +23 -0
  178. package/knowledge/decisions/security/virtual-user-catalogue-excludes-the-machinery-that-runs-virtual-users.md +25 -0
  179. 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
  180. package/knowledge/decisions/security/virtual-user-run-records-are-gated-behind-a-read-scope.md +18 -0
  181. package/knowledge/decisions/security/virtual-user-sign-in-does-not-exist-in-production.md +25 -0
  182. package/knowledge/decisions/security/virtual-user-sign-in-has-one-door.md +42 -0
  183. 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
  184. package/knowledge/decisions/security/virtual-user-sign-in-only-works-for-virtual-user-flagged-users.md +29 -0
  185. package/knowledge/decisions/security/workflow-helper-functions-are-sessionless-by-declaration.md +18 -0
  186. 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
  187. package/knowledge/decisions/security/workflow-scenario-steps-are-never-network-invocable.md +5 -5
  188. 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
  189. package/knowledge/decisions/security/writing-a-virtual-user-schedule-is-gated-by-its-own-scope.md +18 -0
  190. package/knowledge/index.md +0 -2
  191. package/knowledge/questions/a-bearer-jwt-with-no-identity-becomes-a-session.md +24 -0
  192. package/knowledge/questions/dev-quick-login-as-impersonation.md +25 -0
  193. package/knowledge/questions/does-a-delayed-rpc-run-as-the-session-it-was-scheduled-with.md +26 -0
  194. package/knowledge/questions/does-a-user-id-with-a-colon-read-another-users-credentials.md +27 -0
  195. package/knowledge/questions/does-a-wirecli-command-need-a-session-by-default.md +26 -0
  196. package/knowledge/questions/does-an-adversarial-in-scope-success-count-as-a-finding.md +26 -0
  197. package/knowledge/questions/does-approve-step-need-the-run-owners-session-or-the-gates-approvers.md +34 -0
  198. package/knowledge/questions/does-cors-origin-true-reflect-any-origin-with-credentials.md +20 -0
  199. package/knowledge/questions/does-skip-user-session-do-anything-on-a-channel.md +24 -0
  200. package/knowledge/questions/does-the-store-session-match-the-other-two.md +19 -0
  201. package/knowledge/questions/index.md +25 -2
  202. package/knowledge/questions/is-the-agent-score-worker-reachable-without-a-session.md +21 -0
  203. package/knowledge/questions/live-scheduled-tests-may-write.md +19 -0
  204. package/knowledge/questions/provision-virtual-users-has-no-caller-in-this-repo.md +23 -0
  205. package/knowledge/questions/remote-secret-refuses-requests-without-a-remote-token-on-every-path.md +32 -0
  206. package/knowledge/questions/should-a-middleware-set-session-persist-to-the-store.md +25 -0
  207. package/knowledge/questions/should-a-scoped-secret-view-throw-from-has-secret-for-a-disallowed-key.md +23 -0
  208. package/knowledge/questions/should-a-wrong-bearer-token-be-anonymous-or-refused.md +35 -0
  209. package/knowledge/questions/should-an-empty-webhook-signing-secret-send-unsigned.md +23 -0
  210. package/knowledge/questions/should-http-virtual-user-sessions-reset-between-scenarios-in-one-run.md +27 -0
  211. package/knowledge/questions/should-the-cookie-jar-scope-cookies-to-the-api-origin.md +26 -0
  212. package/knowledge/questions/should-voice-input-fetch-audio-urls-without-a-host-allowlist.md +20 -0
  213. package/knowledge/questions/staging-impersonation-rules.md +27 -0
  214. package/knowledge/questions/virtual-user-keys-on-cloud-stages-are-pikku-studios-to-build.md +34 -0
  215. package/knowledge/questions/virtual-user-statement-replay-is-per-process.md +20 -0
  216. package/knowledge/questions/what-an-impersonated-session-carries.md +22 -0
  217. package/package.json +2 -3
  218. package/src/public-surface.json +38 -47
  219. package/dist/services/persona-actor-secret.d.ts +0 -38
  220. package/dist/services/persona-actor-secret.js +0 -39
  221. package/dist/services/persona-sign-in.d.ts +0 -117
  222. package/dist/services/persona-sign-in.js +0 -154
  223. package/dist/wirings/actor-flow/index.d.ts +0 -2
  224. package/dist/wirings/actor-flow/run-conversation.d.ts +0 -36
  225. package/dist/wirings/persona/define-personas.d.ts +0 -32
  226. package/dist/wirings/persona/index.d.ts +0 -23
  227. package/dist/wirings/persona/index.js +0 -19
  228. package/knowledge/decisions/internals/actor-flow-conversations-seed-a-hidden-kickoff-message.md +0 -23
  229. package/knowledge/decisions/internals/actor-flow-verdicts-are-llm-self-evaluations.md +0 -25
  230. package/knowledge/decisions/internals/validate-checks-personas-through-a-shared-module.md +0 -43
  231. package/knowledge/decisions/security/actor-sign-in-only-works-for-actor-flagged-users.md +0 -31
  232. package/knowledge/decisions/security/an-actor-credential-is-derived-per-persona.md +0 -42
  233. package/knowledge/decisions/security/pikku-carries-actor-scopes-as-data-and-the-app-grants-them.md +0 -26
  234. /package/dist/wirings/{actor-flow/actor-flow.types.js → virtual-user/conversation/conversation.types.js} +0 -0
  235. /package/dist/wirings/{actor-flow → virtual-user/conversation}/index.js +0 -0
  236. /package/dist/wirings/{persona/persona.types.js → virtual-user/virtual-user-declaration.types.js} +0 -0
  237. /package/dist/wirings/{persona/persona-mailbox.js → virtual-user/virtual-user-mailbox.js} +0 -0
@@ -16,7 +16,7 @@ import type { IntentRecord, StepRecord, VirtualUserDisposition, VirtualUserFindi
16
16
  */
17
17
  export interface VirtualUserRunRecord {
18
18
  runId: string;
19
- persona: string;
19
+ virtualUserId: string;
20
20
  disposition: VirtualUserDisposition;
21
21
  /** What makes a run replayable at all — a finding without it is an anecdote. */
22
22
  seed: number;
@@ -59,7 +59,7 @@ export interface VirtualUserRunRecord {
59
59
  }
60
60
  /** What a run is created with — everything else is filled in by the outcome. */
61
61
  export interface VirtualUserRunStart {
62
- persona: string;
62
+ virtualUserId: string;
63
63
  disposition: VirtualUserDisposition;
64
64
  seed: number;
65
65
  goals?: readonly string[];
@@ -101,9 +101,9 @@ export interface VirtualUserRunStore {
101
101
  /** Marks a run `failed`. The run itself broke; it has no findings. */
102
102
  fail(runId: string, error: string): Promise<void>;
103
103
  get(runId: string): Promise<VirtualUserRunRecord | null>;
104
- /** Newest first. `persona` narrows to one persona's history. */
104
+ /** Newest first. `virtualUserId` narrows to one virtual user's history. */
105
105
  list(options?: {
106
- persona?: string;
106
+ virtualUserId?: string;
107
107
  limit?: number;
108
108
  offset?: number;
109
109
  }): Promise<VirtualUserRunRecord[]>;
@@ -1,10 +1,11 @@
1
+ import { type InProcessAuth } from '../../services/virtual-user-sign-in.js';
1
2
  import type { Logger } from '../../services/logger.js';
2
3
  import type { MetaService } from '../../services/meta-service.js';
3
4
  import type { VariablesService } from '../../services/variables-service.js';
4
5
  import type { AgentRunnerService } from '../../services/agent-runner-service.js';
5
- import type { HttpPersonasConfig } from '../../services/http-personas.js';
6
- import type { ResolvedPersona, ScenarioPersonas } from '../../services/personas-service.js';
7
- import type { PersonaEnvironment } from '../persona/persona-environments.js';
6
+ import type { HttpVirtualUsersConfig } from '../../services/http-virtual-users.js';
7
+ import type { ResolvedVirtualUser, ScenarioVirtualUsers } from '../../services/virtual-users-service.js';
8
+ import type { VirtualUserEnvironment } from '../virtual-user/virtual-user-environments.js';
8
9
  import type { StepRecord, VirtualUserDisposition } from './virtual-user.types.js';
9
10
  import type { VirtualUserRunRecord, VirtualUserRunStore } from './virtual-user-run-store.js';
10
11
  import type { VirtualUserScheduleRecord, VirtualUserScheduleStore } from './virtual-user-schedule-store.js';
@@ -20,11 +21,11 @@ import type { VirtualUserTickResult } from './virtual-user-schedule.js';
20
21
  * once rather than in every generated copy of it.
21
22
  *
22
23
  * What an application does supply arrives as a parameter — its declared
23
- * personas, its `createPersonas`, its config — because those are the only
24
+ * virtual users, its `createVirtualUsers`, its config — because those are the only
24
25
  * things codegen knows that this cannot.
25
26
  */
26
- /** Persona id → the declaration, which is what `personaConfigs` is. */
27
- export type ScaffoldPersonas = Record<string, ResolvedPersona>;
27
+ /** Virtual user id → the declaration, which is what `virtualUserConfigs` is. */
28
+ export type ScaffoldVirtualUsers = Record<string, ResolvedVirtualUser>;
28
29
  /**
29
30
  * The variables a scaffolded run reads.
30
31
  *
@@ -39,48 +40,27 @@ export declare const VIRTUAL_USER_VARIABLES: {
39
40
  * somewhere it only assumed was itself.
40
41
  */
41
42
  readonly apiUrl: 'VIRTUAL_USER_API_URL';
42
- readonly secret: 'SCENARIO_ACTOR_SECRET';
43
43
  readonly model: 'VIRTUAL_USER_MODEL';
44
44
  /**
45
- * The same two variables a scenario run reads, because a virtual user signs
46
- * in and calls through exactly the doors a scenario does. An app that mounts
47
- * auth somewhere other than the root — `/api/auth` is the common one — has no
48
- * other way to say so, and without them the run signs in against a 404 and
49
- * spends its whole budget thinking about why nothing works.
45
+ * Where the app serves its exposed RPCs, as a scenario run reads it. An app
46
+ * that mounts them somewhere other than `/rpc` has no other way to say so.
50
47
  */
51
- readonly signInPath: 'SCENARIO_SIGN_IN_PATH';
52
48
  readonly rpcPath: 'SCENARIO_RPC_PATH';
53
- /**
54
- * The deployed way in. A Fabric operator token is asymmetric — a stage can
55
- * verify one and can never mint one — so unlike the actor secret it is safe
56
- * for a run against a real environment. Read from the environment only as the
57
- * fallback for a run nobody handed a token to, which is what a schedule is.
58
- */
59
- readonly operatorToken: 'FABRIC_OPERATOR_TOKEN';
60
49
  };
61
50
  /**
62
- * Which door under the auth mount, given the credential in hand.
63
- *
64
- * Both are better-auth plugins mounted side by side, so `SCENARIO_SIGN_IN_PATH`
65
- * names the mount and the last segment is ours to pick — an app that moved auth
66
- * to `/api/auth` says so once and both paths follow. A path that names neither
67
- * plugin is left alone, since it was configured deliberately.
68
- */
69
- export declare const signInPathFor: (configured: string | undefined, plugin: 'actor' | 'fabric') => string | undefined;
70
- /**
71
- * The declared persona behind an id, refused unless it is one a run may be.
51
+ * The declared virtual user behind an id, refused unless it is one a run may be.
72
52
  *
73
- * An acted-upon persona has no session of its own, and running one would race
53
+ * An acted-upon virtual user has no session of its own, and running one would race
74
54
  * whatever scenario acts on it.
75
55
  */
76
- export declare const runnablePersona: (personas: ScaffoldPersonas, personaId: string) => ResolvedPersona;
56
+ export declare const runnableVirtualUser: (virtualUsers: ScaffoldVirtualUsers, virtualUserId: string) => ResolvedVirtualUser;
77
57
  /** The store, or the error naming the one to wire. */
78
58
  export declare const requireVirtualUserRunStore: (store: VirtualUserRunStore | undefined, reading?: boolean) => VirtualUserRunStore;
79
59
  export declare const requireVirtualUserScheduleStore: (store: VirtualUserScheduleStore | undefined) => VirtualUserScheduleStore;
80
60
  /** What a caller asked for, before the declaration fills in what it left out. */
81
61
  export interface StartVirtualUserRunParams {
82
62
  store: VirtualUserRunStore | undefined;
83
- personas: ScaffoldPersonas;
63
+ virtualUsers: ScaffoldVirtualUsers;
84
64
  /**
85
65
  * The app's config, read only for `nodeEnv` — structural because an
86
66
  * application's Config is its own interface and need not declare it at all.
@@ -89,11 +69,11 @@ export interface StartVirtualUserRunParams {
89
69
  config: {
90
70
  nodeEnv?: string;
91
71
  } | undefined;
92
- /** `environments` from pikku.config.json, as generated beside the personas. */
93
- environments?: Readonly<Record<string, PersonaEnvironment>>;
72
+ /** `environments` from pikku.config.json, as generated beside the virtual users. */
73
+ environments?: Readonly<Record<string, VirtualUserEnvironment>>;
94
74
  /** Which of them this process is. Defaults to `PIKKU_ENV`. */
95
75
  environment?: string;
96
- persona: string;
76
+ virtualUserId: string;
97
77
  disposition?: string;
98
78
  seed?: number;
99
79
  goals?: string[];
@@ -104,7 +84,7 @@ export interface StartVirtualUserRunParams {
104
84
  /** The recorded run, and the values the dispatch has to carry unchanged. */
105
85
  export interface StartedVirtualUserRun {
106
86
  runId: string;
107
- persona: string;
87
+ virtualUserId: string;
108
88
  disposition: VirtualUserDisposition;
109
89
  seed: number;
110
90
  goals: string[];
@@ -117,7 +97,7 @@ export interface StartedVirtualUserRun {
117
97
  * scheduled tick have in common. The dispatch that follows is typed off the
118
98
  * app's RPC map, so it stays in the generated wiring.
119
99
  */
120
- export declare const startVirtualUserRun: ({ store, personas, config, environments, environment, persona: personaId, disposition: requested, seed: requestedSeed, goals, memory, startedBy, }: StartVirtualUserRunParams) => Promise<StartedVirtualUserRun>;
100
+ export declare const startVirtualUserRun: ({ store, virtualUsers, config, environments, environment, virtualUserId, disposition: requested, seed: requestedSeed, goals, memory, startedBy, }: StartVirtualUserRunParams) => Promise<StartedVirtualUserRun>;
121
101
  /**
122
102
  * One run on the wire.
123
103
  *
@@ -127,7 +107,7 @@ export declare const startVirtualUserRun: ({ store, personas, config, environmen
127
107
  */
128
108
  export declare const serializeVirtualUserRun: (run: VirtualUserRunRecord) => {
129
109
  runId: string;
130
- persona: string;
110
+ virtualUserId: string;
131
111
  disposition: VirtualUserDisposition;
132
112
  seed: number;
133
113
  status: "completed" | "failed" | "running";
@@ -175,8 +155,8 @@ export declare const serializeVirtualUserSteps: (steps: readonly StepRecord[]) =
175
155
  * takes; the engine's own duration also accepts `'30m'`, which nothing on this
176
156
  * side ever writes.
177
157
  */
178
- export declare const serializeVirtualUserSchedule: (schedule: VirtualUserScheduleRecord, personas: ScaffoldPersonas) => {
179
- persona: string;
158
+ export declare const serializeVirtualUserSchedule: (schedule: VirtualUserScheduleRecord, virtualUsers: ScaffoldVirtualUsers) => {
159
+ virtualUserId: string;
180
160
  enabled: boolean;
181
161
  disposition: VirtualUserDisposition;
182
162
  goals: string[];
@@ -197,8 +177,8 @@ export declare const serializeVirtualUserSchedule: (schedule: VirtualUserSchedul
197
177
  };
198
178
  export interface WriteVirtualUserScheduleParams {
199
179
  store: VirtualUserScheduleStore | undefined;
200
- personas: ScaffoldPersonas;
201
- persona: string;
180
+ virtualUsers: ScaffoldVirtualUsers;
181
+ virtualUserId: string;
202
182
  enabled?: boolean;
203
183
  disposition?: string;
204
184
  goals?: string[];
@@ -214,25 +194,25 @@ export interface WriteVirtualUserScheduleParams {
214
194
  config?: {
215
195
  nodeEnv?: string;
216
196
  };
217
- environments?: Readonly<Record<string, PersonaEnvironment>>;
197
+ environments?: Readonly<Record<string, VirtualUserEnvironment>>;
218
198
  environment?: string;
219
199
  }
220
200
  /**
221
- * Writes a persona's cadence.
201
+ * Writes a virtual user's cadence.
222
202
  *
223
203
  * Applies the same rules `startVirtualUserRun` enforces, at the point the row is
224
- * written rather than every hour afterwards: an acted-upon persona has no
225
- * session, and an enabled cadence whose disposition production refuses, so
226
- * either is a tick that can only ever fail to start, with nobody watching.
204
+ * written rather than every hour afterwards: an acted-upon virtual user has no
205
+ * session, and production refuses every run, so either is a tick that can only
206
+ * ever fail to start, with nobody watching.
227
207
  *
228
- * The disposition checked is the one the row will hold, since a field left out
229
- * keeps what it had: enabling a row saved earlier as adversarial is refused
230
- * just as saving one would be.
208
+ * Whether the row is enabled is the one thing checked, since a field left out
209
+ * keeps what it had: enabling a row saved earlier is refused just as saving a
210
+ * new one would be.
231
211
  */
232
- export declare const writeVirtualUserSchedule: ({ store, personas, persona, enabled, disposition, goals, budget, minIntervalMs, maxIntervalMs, nextRunAt, config, environments, environment, }: WriteVirtualUserScheduleParams) => Promise<VirtualUserScheduleRecord>;
212
+ export declare const writeVirtualUserSchedule: ({ store, virtualUsers, virtualUserId, enabled, disposition, goals, budget, minIntervalMs, maxIntervalMs, nextRunAt, config, environments, environment, }: WriteVirtualUserScheduleParams) => Promise<VirtualUserScheduleRecord>;
233
213
  /** What a due schedule asks `runVirtualUser` for. */
234
214
  export declare const virtualUserScheduleRunInput: (schedule: VirtualUserScheduleRecord) => {
235
- persona: string;
215
+ virtualUserId: string;
236
216
  disposition: VirtualUserDisposition;
237
217
  goals: string[];
238
218
  budget: {
@@ -245,7 +225,7 @@ export declare const virtualUserScheduleRunInput: (schedule: VirtualUserSchedule
245
225
  * What a tick did.
246
226
  *
247
227
  * Logged rather than returned: the caller is a cron, and a run this started is
248
- * otherwise the only trace that a persona is still out there working.
228
+ * otherwise the only trace that a virtual user is still out there working.
249
229
  */
250
230
  export declare const logVirtualUserTick: (logger: Logger, result: VirtualUserTickResult) => void;
251
231
  export interface ExecuteVirtualUserRunParams {
@@ -254,11 +234,11 @@ export interface ExecuteVirtualUserRunParams {
254
234
  agentRunner: AgentRunnerService | undefined;
255
235
  variables: VariablesService;
256
236
  logger: Logger;
257
- personas: ScaffoldPersonas;
258
- /** The app's generated `createPersonas`, which knows its own persona ids. */
259
- createPersonas: (options: Omit<HttpPersonasConfig, 'personas'>) => ScenarioPersonas;
237
+ virtualUsers: ScaffoldVirtualUsers;
238
+ /** The app's generated `createVirtualUsers`, which knows its own virtual user ids. */
239
+ createVirtualUsers: (options: Omit<HttpVirtualUsersConfig, 'virtualUsers'>) => ScenarioVirtualUsers;
260
240
  runId: string;
261
- persona: string;
241
+ virtualUserId: string;
262
242
  disposition: string;
263
243
  goals: string[];
264
244
  memory: Record<string, string>;
@@ -268,16 +248,17 @@ export interface ExecuteVirtualUserRunParams {
268
248
  mutations?: number;
269
249
  durationMs?: number;
270
250
  };
271
- operatorToken?: string;
251
+ /** The app's auth instance, which a run signs in through without a credential. */
252
+ auth: InProcessAuth | undefined;
272
253
  }
273
254
  /**
274
255
  * The run itself.
275
256
  *
276
257
  * Everything it needs is derived through `metaService` and the generated
277
- * personas — the same public surface any consumer has. Nothing reaches into
258
+ * virtual users — the same public surface any consumer has. Nothing reaches into
278
259
  * pikku's internals, because an app could not, and a feature built on what only
279
260
  * the framework can see would not be this feature.
280
261
  */
281
- export declare const executeVirtualUserRun: ({ runStore, metaService, agentRunner, variables, logger, personas, createPersonas, runId, persona: personaId, disposition, goals, memory, seed, budget, operatorToken, }: ExecuteVirtualUserRunParams) => Promise<{
262
+ export declare const executeVirtualUserRun: ({ runStore, metaService, agentRunner, variables, logger, virtualUsers, createVirtualUsers, runId, virtualUserId, disposition, goals, memory, seed, budget, auth, }: ExecuteVirtualUserRunParams) => Promise<{
282
263
  findings: number;
283
264
  }>;
@@ -1,9 +1,9 @@
1
+ import { inProcessSignIn, } from '../../services/virtual-user-sign-in.js';
1
2
  import { readEnvVariable } from '../../env.js';
2
3
  import { BadRequestError, ForbiddenError } from '../../errors/errors.js';
3
4
  import { prepareVirtualUserRun } from './prepare-virtual-user-run.js';
4
5
  import { runVirtualUser as runVirtualUserEngine } from './run-virtual-user.js';
5
- import { personaVirtualUserTarget } from './virtual-user-target.js';
6
- import { PRODUCTION_DISPOSITION } from './virtual-user.types.js';
6
+ import { virtualUserTarget } from './virtual-user-target.js';
7
7
  /**
8
8
  * The variables a scaffolded run reads.
9
9
  *
@@ -18,55 +18,28 @@ export const VIRTUAL_USER_VARIABLES = {
18
18
  * somewhere it only assumed was itself.
19
19
  */
20
20
  apiUrl: 'VIRTUAL_USER_API_URL',
21
- secret: 'SCENARIO_ACTOR_SECRET',
22
21
  model: 'VIRTUAL_USER_MODEL',
23
22
  /**
24
- * The same two variables a scenario run reads, because a virtual user signs
25
- * in and calls through exactly the doors a scenario does. An app that mounts
26
- * auth somewhere other than the root — `/api/auth` is the common one — has no
27
- * other way to say so, and without them the run signs in against a 404 and
28
- * spends its whole budget thinking about why nothing works.
23
+ * Where the app serves its exposed RPCs, as a scenario run reads it. An app
24
+ * that mounts them somewhere other than `/rpc` has no other way to say so.
29
25
  */
30
- signInPath: 'SCENARIO_SIGN_IN_PATH',
31
26
  rpcPath: 'SCENARIO_RPC_PATH',
32
- /**
33
- * The deployed way in. A Fabric operator token is asymmetric — a stage can
34
- * verify one and can never mint one — so unlike the actor secret it is safe
35
- * for a run against a real environment. Read from the environment only as the
36
- * fallback for a run nobody handed a token to, which is what a schedule is.
37
- */
38
- operatorToken: 'FABRIC_OPERATOR_TOKEN',
39
- };
40
- /**
41
- * Which door under the auth mount, given the credential in hand.
42
- *
43
- * Both are better-auth plugins mounted side by side, so `SCENARIO_SIGN_IN_PATH`
44
- * names the mount and the last segment is ours to pick — an app that moved auth
45
- * to `/api/auth` says so once and both paths follow. A path that names neither
46
- * plugin is left alone, since it was configured deliberately.
47
- */
48
- export const signInPathFor = (configured, plugin) => {
49
- if (!configured) {
50
- return undefined;
51
- }
52
- const mount = configured.replace(/\/sign-in\/(actor|fabric)$/, '');
53
- return mount === configured ? configured : `${mount}/sign-in/${plugin}`;
54
27
  };
55
28
  /**
56
- * The declared persona behind an id, refused unless it is one a run may be.
29
+ * The declared virtual user behind an id, refused unless it is one a run may be.
57
30
  *
58
- * An acted-upon persona has no session of its own, and running one would race
31
+ * An acted-upon virtual user has no session of its own, and running one would race
59
32
  * whatever scenario acts on it.
60
33
  */
61
- export const runnablePersona = (personas, personaId) => {
62
- const persona = personas[personaId];
63
- if (!persona) {
64
- throw new BadRequestError(`Unknown persona "${personaId}" — declare it with definePersonas()`);
34
+ export const runnableVirtualUser = (virtualUsers, virtualUserId) => {
35
+ const virtualUser = virtualUsers[virtualUserId];
36
+ if (!virtualUser) {
37
+ throw new BadRequestError(`Unknown virtual user "${virtualUserId}" — declare it with defineVirtualUsers()`);
65
38
  }
66
- if (!persona.runnable) {
67
- throw new BadRequestError(`Persona "${personaId}" is declared as acted upon, never run`);
39
+ if (!virtualUser.runnable) {
40
+ throw new BadRequestError(`VirtualUser "${virtualUserId}" is declared as acted upon, never run`);
68
41
  }
69
- return persona;
42
+ return virtualUser;
70
43
  };
71
44
  const MISSING_RUN_STORE = 'No virtualUserRunStore is wired — a run has nowhere to be recorded. ' +
72
45
  'Wire KyselyVirtualUserRunStore from @pikku/kysely, or your own implementation of VirtualUserRunStore.';
@@ -94,7 +67,7 @@ export const requireVirtualUserScheduleStore = (store) => {
94
67
  * `NODE_ENV=production` there too — keying on it refuses every disposition on
95
68
  * the one environment they exist to be used on. `PIKKU_ENV` names which of the
96
69
  * configured environments this is, which is the question actually being asked,
97
- * and it is the same signal `personaEnvironmentRefusal` already checks at
70
+ * and it is the same signal `virtualUserEnvironmentRefusal` already checks at
98
71
  * sign-in.
99
72
  *
100
73
  * Unresolved is treated as production: an environment nobody can name is one
@@ -108,12 +81,11 @@ const isProductionRun = (config, environments, environment) => {
108
81
  }
109
82
  return environment ? Boolean(environments[environment]?.production) : true;
110
83
  };
111
- // Every disposition other than this one exists to find out what the product
112
- // does wrong, which is not a thing to do to real customers' data.
113
- const refuseInProduction = (personaId, disposition, config, environments, environment) => {
114
- if (disposition !== PRODUCTION_DISPOSITION &&
115
- isProductionRun(config, environments, environment)) {
116
- throw new ForbiddenError(`Only the '${PRODUCTION_DISPOSITION}' disposition may run against production; "${personaId}" is ${disposition}`);
84
+ // A virtual user is a probe, and probing is not a thing to do to real
85
+ // customers' data, whatever the disposition.
86
+ const refuseInProduction = (virtualUserId, config, environments, environment) => {
87
+ if (isProductionRun(config, environments, environment)) {
88
+ throw new ForbiddenError(`Virtual users do not run against production; "${virtualUserId}" cannot run here`);
117
89
  }
118
90
  };
119
91
  /**
@@ -123,15 +95,13 @@ const refuseInProduction = (personaId, disposition, config, environments, enviro
123
95
  * scheduled tick have in common. The dispatch that follows is typed off the
124
96
  * app's RPC map, so it stays in the generated wiring.
125
97
  */
126
- export const startVirtualUserRun = async ({ store, personas, config, environments, environment = readEnvVariable('PIKKU_ENV'), persona: personaId, disposition: requested, seed: requestedSeed, goals, memory, startedBy, }) => {
98
+ export const startVirtualUserRun = async ({ store, virtualUsers, config, environments, environment = readEnvVariable('PIKKU_ENV'), virtualUserId, disposition: requested, seed: requestedSeed, goals, memory, startedBy, }) => {
127
99
  const runStore = requireVirtualUserRunStore(store);
128
- const persona = runnablePersona(personas, personaId);
100
+ const virtualUser = runnableVirtualUser(virtualUsers, virtualUserId);
129
101
  const disposition = (requested ??
130
- persona.disposition ??
102
+ virtualUser.disposition ??
131
103
  'realistic');
132
- // Checked against the effective disposition, so an override cannot smuggle
133
- // one in.
134
- refuseInProduction(personaId, disposition, config, environments, environment);
104
+ refuseInProduction(virtualUserId, config, environments, environment);
135
105
  // Seeded here rather than inside the engine so the record carries the seed
136
106
  // even if the run dies before returning — an unreproducible crash costs the
137
107
  // most.
@@ -139,7 +109,7 @@ export const startVirtualUserRun = async ({ store, personas, config, environment
139
109
  const resolvedGoals = goals ?? [];
140
110
  const resolvedMemory = memory ?? {};
141
111
  const runId = await runStore.start({
142
- persona: persona.id,
112
+ virtualUserId: virtualUser.id,
143
113
  disposition,
144
114
  seed,
145
115
  goals: resolvedGoals,
@@ -148,7 +118,7 @@ export const startVirtualUserRun = async ({ store, personas, config, environment
148
118
  });
149
119
  return {
150
120
  runId,
151
- persona: persona.id,
121
+ virtualUserId: virtualUser.id,
152
122
  disposition,
153
123
  seed,
154
124
  goals: resolvedGoals,
@@ -164,7 +134,7 @@ export const startVirtualUserRun = async ({ store, personas, config, environment
164
134
  */
165
135
  export const serializeVirtualUserRun = (run) => ({
166
136
  runId: run.runId,
167
- persona: run.persona,
137
+ virtualUserId: run.virtualUserId,
168
138
  disposition: run.disposition,
169
139
  seed: run.seed,
170
140
  status: run.status,
@@ -212,10 +182,10 @@ export const serializeVirtualUserSteps = (steps) => steps.map((step) => ({
212
182
  * takes; the engine's own duration also accepts `'30m'`, which nothing on this
213
183
  * side ever writes.
214
184
  */
215
- export const serializeVirtualUserSchedule = (schedule, personas) => {
216
- const persona = personas[schedule.persona];
185
+ export const serializeVirtualUserSchedule = (schedule, virtualUsers) => {
186
+ const virtualUser = virtualUsers[schedule.virtualUserId];
217
187
  return {
218
- persona: schedule.persona,
188
+ virtualUserId: schedule.virtualUserId,
219
189
  enabled: schedule.enabled,
220
190
  disposition: schedule.disposition,
221
191
  goals: schedule.goals,
@@ -234,36 +204,36 @@ export const serializeVirtualUserSchedule = (schedule, personas) => {
234
204
  lastRunId: schedule.lastRunId,
235
205
  lastRunAt: schedule.lastRunAt ? schedule.lastRunAt.toISOString() : null,
236
206
  declared: {
237
- disposition: (persona?.disposition ??
207
+ disposition: (virtualUser?.disposition ??
238
208
  'realistic'),
239
- goals: persona?.goals ?? [],
209
+ goals: virtualUser?.goals ?? [],
240
210
  },
241
211
  };
242
212
  };
243
213
  /**
244
- * Writes a persona's cadence.
214
+ * Writes a virtual user's cadence.
245
215
  *
246
216
  * Applies the same rules `startVirtualUserRun` enforces, at the point the row is
247
- * written rather than every hour afterwards: an acted-upon persona has no
248
- * session, and an enabled cadence whose disposition production refuses, so
249
- * either is a tick that can only ever fail to start, with nobody watching.
217
+ * written rather than every hour afterwards: an acted-upon virtual user has no
218
+ * session, and production refuses every run, so either is a tick that can only
219
+ * ever fail to start, with nobody watching.
250
220
  *
251
- * The disposition checked is the one the row will hold, since a field left out
252
- * keeps what it had: enabling a row saved earlier as adversarial is refused
253
- * just as saving one would be.
221
+ * Whether the row is enabled is the one thing checked, since a field left out
222
+ * keeps what it had: enabling a row saved earlier is refused just as saving a
223
+ * new one would be.
254
224
  */
255
- export const writeVirtualUserSchedule = async ({ store, personas, persona, enabled, disposition, goals, budget, minIntervalMs, maxIntervalMs, nextRunAt, config, environments, environment = readEnvVariable('PIKKU_ENV'), }) => {
225
+ export const writeVirtualUserSchedule = async ({ store, virtualUsers, virtualUserId, enabled, disposition, goals, budget, minIntervalMs, maxIntervalMs, nextRunAt, config, environments, environment = readEnvVariable('PIKKU_ENV'), }) => {
256
226
  const scheduleStore = requireVirtualUserScheduleStore(store);
257
- const declared = runnablePersona(personas, persona);
227
+ const declared = runnableVirtualUser(virtualUsers, virtualUserId);
258
228
  const current = enabled === undefined || disposition === undefined
259
- ? await scheduleStore.get(persona)
229
+ ? await scheduleStore.get(virtualUserId)
260
230
  : null;
261
231
  const created = disposition === undefined && !current ? declared.disposition : undefined;
262
232
  if (enabled ?? current?.enabled) {
263
- refuseInProduction(persona, disposition ?? current?.disposition ?? created ?? 'realistic', config, environments, environment);
233
+ refuseInProduction(virtualUserId, config, environments, environment);
264
234
  }
265
235
  return scheduleStore.set({
266
- persona,
236
+ virtualUserId,
267
237
  enabled,
268
238
  disposition: (disposition ?? created),
269
239
  goals,
@@ -283,7 +253,7 @@ export const writeVirtualUserSchedule = async ({ store, personas, persona, enabl
283
253
  };
284
254
  /** What a due schedule asks `runVirtualUser` for. */
285
255
  export const virtualUserScheduleRunInput = (schedule) => ({
286
- persona: schedule.persona,
256
+ virtualUserId: schedule.virtualUserId,
287
257
  disposition: schedule.disposition,
288
258
  goals: schedule.goals,
289
259
  budget: schedule.budget
@@ -300,28 +270,28 @@ export const virtualUserScheduleRunInput = (schedule) => ({
300
270
  * What a tick did.
301
271
  *
302
272
  * Logged rather than returned: the caller is a cron, and a run this started is
303
- * otherwise the only trace that a persona is still out there working.
273
+ * otherwise the only trace that a virtual user is still out there working.
304
274
  */
305
275
  export const logVirtualUserTick = (logger, result) => {
306
- for (const { persona, runId } of result.dispatched) {
307
- logger.info(`Virtual user ${persona} started run ${runId} on schedule`);
276
+ for (const { virtualUserId, runId } of result.dispatched) {
277
+ logger.info(`Virtual user ${virtualUserId} started run ${runId} on schedule`);
308
278
  }
309
279
  for (const runId of result.reaped) {
310
- logger.warn(`Virtual user run ${runId} was abandoned — marked failed so its persona can run again`);
280
+ logger.warn(`Virtual user run ${runId} was abandoned — marked failed so its virtual user can run again`);
311
281
  }
312
- for (const { persona, reason } of result.skipped) {
313
- logger.info(`Virtual user ${persona} skipped this tick: ${reason}`);
282
+ for (const { virtualUserId, reason } of result.skipped) {
283
+ logger.info(`Virtual user ${virtualUserId} skipped this tick: ${reason}`);
314
284
  }
315
285
  };
316
286
  /**
317
287
  * The run itself.
318
288
  *
319
289
  * Everything it needs is derived through `metaService` and the generated
320
- * personas — the same public surface any consumer has. Nothing reaches into
290
+ * virtual users — the same public surface any consumer has. Nothing reaches into
321
291
  * pikku's internals, because an app could not, and a feature built on what only
322
292
  * the framework can see would not be this feature.
323
293
  */
324
- export const executeVirtualUserRun = async ({ runStore, metaService, agentRunner, variables, logger, personas, createPersonas, runId, persona: personaId, disposition, goals, memory, seed, budget, operatorToken, }) => {
294
+ export const executeVirtualUserRun = async ({ runStore, metaService, agentRunner, variables, logger, virtualUsers, createVirtualUsers, runId, virtualUserId, disposition, goals, memory, seed, budget, auth, }) => {
325
295
  const store = requireVirtualUserRunStore(runStore);
326
296
  try {
327
297
  if (!metaService) {
@@ -332,20 +302,11 @@ export const executeVirtualUserRun = async ({ runStore, metaService, agentRunner
332
302
  }
333
303
  const apiUrl = await variables.get(VIRTUAL_USER_VARIABLES.apiUrl);
334
304
  if (!apiUrl) {
335
- throw new Error(`${VIRTUAL_USER_VARIABLES.apiUrl} is not set — a virtual user has no address to sign in at.`);
305
+ throw new Error(`${VIRTUAL_USER_VARIABLES.apiUrl} is not set, so the virtual user has no address to drive. ` +
306
+ `Set it to the URL this app is reached at, including any HTTP prefix (for example https://my-app.example.com/api). \`pikku dev\` sets it for you.`);
336
307
  }
337
- // An operator token wins wherever one is available: it is asymmetric, and
338
- // it does not need the target to hold a shared secret at all. The actor
339
- // secret is the local-only fallback, because only `pikku dev` serves the
340
- // endpoint that accepts it.
341
- const token = operatorToken ??
342
- (await variables.get(VIRTUAL_USER_VARIABLES.operatorToken));
343
- const secret = token
344
- ? undefined
345
- : await variables.get(VIRTUAL_USER_VARIABLES.secret);
346
- if (!token && !secret) {
347
- throw new Error(`Neither an operator token nor ${VIRTUAL_USER_VARIABLES.secret} is available — there is nobody for the virtual user to be. ` +
348
- `Hand a Fabric operator token in with the run against a deployed stage, or export ${VIRTUAL_USER_VARIABLES.secret} against a local \`pikku dev\` target.`);
308
+ if (!auth) {
309
+ throw new Error('The app has no auth service, so the virtual user has no way to sign in. Register better-auth (with the pikkuVirtualUser plugin) as the auth singleton service.');
349
310
  }
350
311
  const model = await variables.get(VIRTUAL_USER_VARIABLES.model);
351
312
  if (!model) {
@@ -358,40 +319,31 @@ export const executeVirtualUserRun = async ({ runStore, metaService, agentRunner
358
319
  ...new Set(Object.values(functionsMeta).flatMap((meta) => [meta.inputSchemaName, meta.outputSchemaName].filter((name) => !!name))),
359
320
  ];
360
321
  const schemas = (await metaService.getSchemas(schemaNames));
361
- const persona = personas[personaId];
362
- if (!persona) {
363
- throw new Error(`Persona "${personaId}" is no longer declared`);
322
+ const virtualUser = virtualUsers[virtualUserId];
323
+ if (!virtualUser) {
324
+ throw new Error(`VirtualUser "${virtualUserId}" is no longer declared`);
364
325
  }
365
326
  const { catalogue, intents, agents } = prepareVirtualUserRun({
366
- persona,
327
+ virtualUser,
367
328
  functionsMeta,
368
329
  schemas,
369
330
  workflowsMeta: await metaService.getWorkflowMeta(),
370
331
  systemRoles: await metaService.getSystemRolesMeta(),
371
332
  agentsMeta: await metaService.getAgentsMeta(),
372
333
  });
373
- const configuredSignInPath = (await variables.get(VIRTUAL_USER_VARIABLES.signInPath)) ?? undefined;
374
- const signedIn = createPersonas({
334
+ const signedIn = createVirtualUsers({
375
335
  apiUrl,
376
- ...(token
377
- ? {
378
- operator: {
379
- token,
380
- signInPath: signInPathFor(configuredSignInPath, 'fabric'),
381
- },
382
- }
383
- : { secret }),
336
+ signIn: inProcessSignIn(auth),
384
337
  model,
385
- signInPath: signInPathFor(configuredSignInPath, 'actor'),
386
338
  rpcPath: (await variables.get(VIRTUAL_USER_VARIABLES.rpcPath)) ?? undefined,
387
339
  });
388
- const target = signedIn[personaId];
340
+ const target = signedIn[virtualUserId];
389
341
  if (!target) {
390
- throw new Error(`Persona "${personaId}" cannot sign in`);
342
+ throw new Error(`VirtualUser "${virtualUserId}" cannot sign in`);
391
343
  }
392
344
  const result = await runVirtualUserEngine({
393
- persona,
394
- personaId,
345
+ virtualUser,
346
+ virtualUserId,
395
347
  disposition: disposition,
396
348
  catalogue,
397
349
  intents,
@@ -399,11 +351,11 @@ export const executeVirtualUserRun = async ({ runStore, metaService, agentRunner
399
351
  memory,
400
352
  seed,
401
353
  agents,
402
- target: personaVirtualUserTarget(target, {
354
+ target: virtualUserTarget(target, {
403
355
  model,
404
356
  agents: agents.map((agent) => agent.name),
405
357
  }),
406
- // `AgentRunnerService.run` IS the engine's `ActorLLM` — same params, same
358
+ // `AgentRunnerService.run` IS the engine's `VirtualUserLLM` — same params, same
407
359
  // result — so a virtual user thinks through the same runner every agent in
408
360
  // the app does, provider quirks and all.
409
361
  llm: (params) => agentRunner.run(params),
@@ -429,7 +381,7 @@ export const executeVirtualUserRun = async ({ runStore, metaService, agentRunner
429
381
  // record is the only place that distinction survives — leaving it at
430
382
  // 'running' forever is what `fail` exists to prevent.
431
383
  const message = error instanceof Error ? error.message : String(error);
432
- logger.error(`Virtual user run ${runId} (${personaId}) failed: ${message}`);
384
+ logger.error(`Virtual user run ${runId} (${virtualUserId}) failed: ${message}`);
433
385
  await store.fail(runId, message);
434
386
  throw error;
435
387
  }