@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.
- package/CHANGELOG.md +33 -0
- package/dist/errors/errors.d.ts +1 -1
- package/dist/errors/errors.js +1 -1
- package/dist/function/function-meta.types.d.ts +6 -6
- package/dist/function/functions.types.d.ts +2 -2
- package/dist/services/audit-service.d.ts +4 -4
- package/dist/services/feature-flag-service.d.ts +3 -3
- package/dist/services/{http-personas.d.ts → http-virtual-users.d.ts} +25 -47
- package/dist/services/{http-personas.js → http-virtual-users.js} +29 -53
- package/dist/services/index.d.ts +2 -2
- package/dist/services/index.js +1 -1
- package/dist/services/meta-service.d.ts +6 -6
- package/dist/services/meta-service.js +8 -8
- package/dist/{wirings/workflow → services}/scenario-cookie-jar.d.ts +2 -0
- package/dist/{wirings/workflow → services}/scenario-cookie-jar.js +18 -14
- package/dist/services/system-role-guard.d.ts +1 -1
- package/dist/services/virtual-user-sign-in.d.ts +52 -0
- package/dist/services/virtual-user-sign-in.js +80 -0
- package/dist/services/virtual-user-statement.d.ts +31 -0
- package/dist/services/virtual-user-statement.js +24 -0
- package/dist/services/{personas-service.d.ts → virtual-users-service.d.ts} +25 -23
- package/dist/services/{personas-service.js → virtual-users-service.js} +3 -3
- package/dist/types/core.types.d.ts +11 -11
- package/dist/wirings/role/define-system-role.d.ts +1 -1
- package/dist/wirings/role/define-system-role.js +1 -1
- package/dist/wirings/{actor-flow/actor-flow.types.d.ts → virtual-user/conversation/conversation.types.d.ts} +5 -5
- package/dist/wirings/virtual-user/conversation/index.d.ts +2 -0
- package/dist/wirings/virtual-user/conversation/run-conversation.d.ts +36 -0
- package/dist/wirings/{actor-flow → virtual-user/conversation}/run-conversation.js +30 -30
- package/dist/wirings/virtual-user/define-virtual-users.d.ts +32 -0
- package/dist/wirings/{persona/define-personas.js → virtual-user/define-virtual-users.js} +9 -9
- package/dist/wirings/virtual-user/index.d.ts +30 -7
- package/dist/wirings/virtual-user/index.js +23 -4
- package/dist/wirings/virtual-user/prepare-virtual-user-run.d.ts +8 -8
- package/dist/wirings/virtual-user/prepare-virtual-user-run.js +9 -9
- package/dist/wirings/virtual-user/run-virtual-user.d.ts +6 -6
- package/dist/wirings/virtual-user/run-virtual-user.js +10 -10
- package/dist/wirings/{persona/validate-personas.d.ts → virtual-user/validate-virtual-users.d.ts} +11 -11
- package/dist/wirings/{persona/validate-personas.js → virtual-user/validate-virtual-users.js} +24 -24
- package/dist/wirings/virtual-user/virtual-user-agents.d.ts +7 -7
- package/dist/wirings/virtual-user/virtual-user-agents.js +2 -2
- package/dist/wirings/{persona/persona-app-scopes.d.ts → virtual-user/virtual-user-app-scopes.d.ts} +10 -10
- package/dist/wirings/{persona/persona-app-scopes.js → virtual-user/virtual-user-app-scopes.js} +12 -12
- package/dist/wirings/virtual-user/virtual-user-catalogue.d.ts +3 -3
- package/dist/wirings/virtual-user/virtual-user-catalogue.js +2 -2
- package/dist/wirings/{persona/persona.types.d.ts → virtual-user/virtual-user-declaration.types.d.ts} +24 -24
- package/dist/wirings/virtual-user/virtual-user-derive.js +13 -7
- package/dist/wirings/virtual-user/virtual-user-dispositions.d.ts +1 -1
- package/dist/wirings/{persona/persona-email.d.ts → virtual-user/virtual-user-email.d.ts} +11 -11
- package/dist/wirings/{persona/persona-email.js → virtual-user/virtual-user-email.js} +19 -19
- package/dist/wirings/{persona/persona-environments.d.ts → virtual-user/virtual-user-environments.d.ts} +10 -10
- package/dist/wirings/{persona/persona-environments.js → virtual-user/virtual-user-environments.js} +21 -21
- package/dist/wirings/virtual-user/virtual-user-intents.d.ts +3 -3
- package/dist/wirings/virtual-user/virtual-user-intents.js +3 -3
- package/dist/wirings/{persona/persona-mailbox.d.ts → virtual-user/virtual-user-mailbox.d.ts} +4 -4
- package/dist/wirings/virtual-user/virtual-user-run-store.d.ts +4 -4
- package/dist/wirings/virtual-user/virtual-user-scaffold.d.ts +42 -61
- package/dist/wirings/virtual-user/virtual-user-scaffold.js +69 -117
- package/dist/wirings/virtual-user/virtual-user-schedule-store.d.ts +15 -15
- package/dist/wirings/virtual-user/virtual-user-schedule.d.ts +6 -6
- package/dist/wirings/virtual-user/virtual-user-schedule.js +11 -11
- package/dist/wirings/virtual-user/virtual-user-target.d.ts +6 -6
- package/dist/wirings/virtual-user/virtual-user-target.js +5 -5
- package/dist/wirings/virtual-user/virtual-user.types.d.ts +11 -11
- package/dist/wirings/virtual-user/virtual-user.types.js +1 -1
- package/dist/wirings/workflow/dsl/workflow-dsl.types.d.ts +9 -9
- package/dist/wirings/workflow/pikku-scenario-service.d.ts +28 -28
- package/dist/wirings/workflow/pikku-scenario-service.js +77 -77
- package/dist/wirings/workflow/pikku-workflow-service.d.ts +2 -2
- package/dist/wirings/workflow/pikku-workflow-service.js +4 -4
- package/dist/wirings/workflow/scenario-prose.d.ts +8 -8
- package/dist/wirings/workflow/scenario-prose.js +12 -8
- package/dist/wirings/workflow/scenario-run.types.d.ts +13 -13
- package/dist/wirings/workflow/scenario-step.types.d.ts +23 -23
- package/dist/wirings/workflow/workflow.types.d.ts +2 -2
- package/knowledge/decisions/index.md +0 -2
- package/knowledge/decisions/internals/a-credential-name-declared-twice-must-agree-on-schema-and-type.md +29 -0
- package/knowledge/decisions/internals/a-grant-can-name-a-scope-declared-without-its-addon.md +29 -0
- package/knowledge/decisions/internals/a-scheduled-task-writes-its-caller-session-before-it-runs.md +22 -0
- package/knowledge/decisions/internals/a-secret-set-in-memory-shadows-the-variable-of-the-same-name.md +28 -0
- package/knowledge/decisions/internals/a-virtual-user-acts-through-invokeraw-so-a-refusal-is-data.md +32 -0
- package/knowledge/decisions/internals/a-virtual-user-cadence-is-a-row-not-a-timer.md +12 -12
- package/knowledge/decisions/internals/a-virtual-user-converse-signs-in-before-its-first-turn.md +24 -0
- package/knowledge/decisions/internals/a-virtual-user-email-is-computed-with-the-run-id-as-a-sub-address.md +34 -0
- package/knowledge/decisions/internals/a-virtual-user-run-is-not-a-workflow-but-it-needs-a-trigger.md +2 -2
- package/knowledge/decisions/internals/an-agent-is-offered-under-its-registry-key-and-only-within-the-virtual-users-scopes.md +27 -0
- package/knowledge/decisions/internals/an-oauth2-credential-implies-an-optional-app-secret.md +26 -0
- package/knowledge/decisions/internals/analytics-identity-resolvers-run-in-declared-order.md +22 -0
- package/knowledge/decisions/internals/feature-flag-declarations-refuse-an-empty-anyof-and-conflicting-scopes.md +22 -0
- package/knowledge/decisions/internals/feature-flags-fail-open-and-never-enforce-access.md +28 -0
- package/knowledge/decisions/internals/flag-sync-rewrites-only-the-declared-text.md +24 -0
- package/knowledge/decisions/internals/in-a-scenario-a-4xx-is-data-not-an-exception.md +3 -3
- package/knowledge/decisions/internals/index.md +46 -31
- package/knowledge/decisions/internals/one-project-shape-check-two-validators.md +10 -10
- package/knowledge/decisions/internals/role-names-cannot-contain-the-scope-separator.md +30 -0
- package/knowledge/decisions/internals/scenario-agent-calls-sign-in-on-401-only.md +23 -19
- package/knowledge/decisions/internals/scenario-steps-return-drained-response-records.md +2 -2
- package/knowledge/decisions/internals/scenarios-live-in-files-named-for-them.md +12 -12
- package/knowledge/decisions/internals/the-core-scope-validator-refuses-separator-wildcard-and-empty-segments.md +22 -0
- package/knowledge/decisions/internals/the-pikku-user-id-resolves-in-a-fixed-order.md +24 -0
- 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
- 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
- package/knowledge/decisions/internals/validate-checks-virtual-users-through-a-shared-module.md +43 -0
- 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
- package/knowledge/decisions/internals/virtual-user-conversation-verdicts-are-llm-self-evaluations.md +25 -0
- package/knowledge/decisions/internals/virtual-user-conversations-seed-a-hidden-kickoff-message.md +23 -0
- package/knowledge/decisions/internals/virtual-users-are-declared-in-one-definevirtualusers-call-per-codebase.md +30 -0
- package/knowledge/decisions/internals/workflow-core-never-imports-a-browser-driver.md +5 -5
- package/knowledge/decisions/internals/workflow-run-capabilities-are-extensions-not-subclasses.md +2 -2
- package/knowledge/decisions/internals/workflow-scenario-assertions-never-retry-and-record-one-step.md +4 -4
- package/knowledge/decisions/internals/workflow-scenario-hooks-are-a-scenario-only-affordance.md +1 -1
- package/knowledge/decisions/internals/workflow-scenario-prose-is-rendered-from-typed-calls-not-parsed-from-english.md +2 -2
- package/knowledge/decisions/internals/workflow-scenario-step-targets-are-string-literals-for-the-inspector.md +1 -1
- package/knowledge/decisions/internals/workflow-suspended-runs-keep-their-in-process-context.md +1 -1
- package/knowledge/decisions/security/a-bearer-token-checked-against-a-secret-reads-it-per-request.md +26 -0
- package/knowledge/decisions/security/a-call-without-a-session-raises-missing-session-not-forbidden.md +20 -0
- package/knowledge/decisions/security/a-caller-cookie-wins-over-the-jar-for-the-same-name.md +23 -0
- package/knowledge/decisions/security/a-channel-lifecycle-acts-as-the-socket-upgrade-session.md +16 -0
- package/knowledge/decisions/security/a-channel-requires-a-session-unless-it-says-auth-false.md +26 -0
- package/knowledge/decisions/security/a-credential-read-path-follows-its-declared-type-not-absence.md +32 -0
- package/knowledge/decisions/security/a-credential-type-is-a-literal-the-inspector-checks-at-declaration.md +27 -0
- package/knowledge/decisions/security/a-first-party-cookie-is-written-only-after-its-purposes-consent.md +33 -0
- package/knowledge/decisions/security/a-readonly-session-reaches-only-readonly-functions.md +25 -0
- package/knowledge/decisions/security/a-scaffold-flag-says-a-surface-exists-not-who-may-call-it.md +10 -5
- package/knowledge/decisions/security/a-scheduled-virtual-user-run-needs-no-admin-at-run-time.md +27 -0
- package/knowledge/decisions/security/a-scoped-credential-view-is-limited-to-declared-names.md +26 -0
- package/knowledge/decisions/security/a-scoped-secret-view-reads-only-declared-keys-and-never-writes.md +25 -0
- package/knowledge/decisions/security/a-secret-is-sent-only-to-the-hosts-it-declares.md +29 -0
- package/knowledge/decisions/security/a-step-can-name-an-identity-no-virtual-user-holds-through-per-call-headers.md +25 -0
- package/knowledge/decisions/security/a-stopped-run-by-id-skips-the-ownership-check.md +24 -0
- package/knowledge/decisions/security/a-vault-secret-redacts-on-serialization-and-throws-on-coercion.md +29 -0
- package/knowledge/decisions/security/a-virtual-user-catalogue-entry-is-filtered-by-scope-and-approval-is-denied-by-default.md +30 -0
- package/knowledge/decisions/security/a-virtual-user-environment-fails-closed.md +38 -0
- package/knowledge/decisions/security/a-virtual-user-never-runs-against-production.md +23 -0
- package/knowledge/decisions/security/a-virtual-user-reads-its-roles-from-the-apps-own-rpc-before-better-auth.md +27 -0
- package/knowledge/decisions/security/a-virtual-user-run-treats-an-unresolved-environment-as-production.md +41 -0
- package/knowledge/decisions/security/a-virtual-user-scopes-are-expanded-from-its-roles-once-at-run-preparation.md +25 -0
- package/knowledge/decisions/security/a-virtual-user-whose-roles-drift-from-the-declaration-refuses-to-run.md +32 -0
- package/knowledge/decisions/security/addon-screen-scopes-hide-and-do-not-enforce.md +20 -0
- package/knowledge/decisions/security/an-addon-header-can-only-narrow-the-session.md +26 -0
- package/knowledge/decisions/security/an-addon-instance-reads-only-the-secrets-and-credentials-it-is-named.md +33 -0
- package/knowledge/decisions/security/an-addon-may-reach-only-itself-and-the-addons-it-uses.md +16 -0
- package/knowledge/decisions/security/an-api-key-is-jwt-decoded-into-the-session.md +26 -0
- package/knowledge/decisions/security/an-external-virtual-user-caller-presents-one-signed-statement.md +38 -0
- package/knowledge/decisions/security/an-inbound-webhook-source-dispatches-only-verified-requests.md +31 -0
- package/knowledge/decisions/security/an-mcp-call-with-no-http-request-stays-anonymous.md +18 -0
- package/knowledge/decisions/security/an-omitted-user-id-addresses-the-platform-credential.md +32 -0
- package/knowledge/decisions/security/analytics-identity-is-stamped-from-the-session-not-the-event.md +24 -0
- package/knowledge/decisions/security/app-access-is-a-scope-tree-synthesised-from-virtual-users.md +26 -0
- package/knowledge/decisions/security/approval-policy-decides-who-answers-a-tool-call.md +27 -0
- package/knowledge/decisions/security/auth-false-on-a-session-function-warns-and-still-requires-one.md +25 -0
- package/knowledge/decisions/security/bearer-token-hashes-are-unpeppered-sha256.md +18 -0
- package/knowledge/decisions/security/consent-is-read-from-a-cookie-and-a-recognised-denial-counts-as-refusal.md +29 -0
- package/knowledge/decisions/security/cors-defaults-to-any-origin-and-refuses-the-wildcard-with-credentials.md +22 -0
- package/knowledge/decisions/security/credential-backends-share-one-conformance-suite-for-per-user-isolation.md +29 -0
- package/knowledge/decisions/security/each-stage-has-one-virtual-user-key-pair.md +27 -0
- package/knowledge/decisions/security/hmac-verification-fails-closed-on-empty-inputs.md +25 -0
- package/knowledge/decisions/security/index.md +88 -24
- package/knowledge/decisions/security/local-content-refuses-private-buckets-under-the-public-segment.md +21 -0
- package/knowledge/decisions/security/mail-is-allowlisted-before-a-virtual-user-run-sees-it.md +38 -0
- package/knowledge/decisions/security/mcp-resources-tools-and-prompts-take-sessionless-functions.md +18 -0
- package/knowledge/decisions/security/origin-locked-routes-match-the-origin-exactly.md +22 -0
- package/knowledge/decisions/security/oss-auth-works-without-pikku-studio-cloud.md +26 -0
- package/knowledge/decisions/security/outbound-webhooks-use-an-allowlist-when-configured.md +24 -0
- package/knowledge/decisions/security/pikku-carries-virtual-user-scopes-as-data-and-the-app-grants-them.md +35 -0
- package/knowledge/decisions/security/scoped-content-access-is-a-root-folder-with-longest-prefix-grants.md +31 -0
- package/knowledge/decisions/security/services-without-secrets-get-a-throwing-secrets-accessor.md +22 -0
- package/knowledge/decisions/security/shared-guards-refuse-to-mutate-or-shadow-a-system-role.md +34 -0
- package/knowledge/decisions/security/signed-uploads-are-verified-by-the-same-check-as-signed-reads.md +26 -0
- package/knowledge/decisions/security/system-role-scopes-must-be-declared-with-define-scope.md +24 -0
- package/knowledge/decisions/security/system-roles-are-additive-and-immutable.md +16 -0
- package/knowledge/decisions/security/the-channel-rpc-does-not-send-the-callers-session.md +21 -0
- package/knowledge/decisions/security/the-remote-jobs-inbox-fails-closed-without-a-secret.md +24 -0
- package/knowledge/decisions/security/the-remote-rpc-gate-fails-closed-and-binds-the-token-to-its-function.md +34 -0
- package/knowledge/decisions/security/the-workflow-status-stream-rechecks-ownership-on-every-poll.md +16 -0
- package/knowledge/decisions/security/two-credentials-on-one-key-must-share-a-read-mode.md +29 -0
- package/knowledge/decisions/security/verified-bearer-claims-are-pass-through-data-that-core-never-fills.md +23 -0
- package/knowledge/decisions/security/virtual-user-catalogue-excludes-the-machinery-that-runs-virtual-users.md +25 -0
- 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
- package/knowledge/decisions/security/virtual-user-run-records-are-gated-behind-a-read-scope.md +18 -0
- package/knowledge/decisions/security/virtual-user-sign-in-does-not-exist-in-production.md +25 -0
- package/knowledge/decisions/security/virtual-user-sign-in-has-one-door.md +42 -0
- 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
- package/knowledge/decisions/security/virtual-user-sign-in-only-works-for-virtual-user-flagged-users.md +29 -0
- package/knowledge/decisions/security/workflow-helper-functions-are-sessionless-by-declaration.md +18 -0
- 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
- package/knowledge/decisions/security/workflow-scenario-steps-are-never-network-invocable.md +5 -5
- 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
- package/knowledge/decisions/security/writing-a-virtual-user-schedule-is-gated-by-its-own-scope.md +18 -0
- package/knowledge/index.md +0 -2
- package/knowledge/questions/a-bearer-jwt-with-no-identity-becomes-a-session.md +24 -0
- package/knowledge/questions/dev-quick-login-as-impersonation.md +25 -0
- package/knowledge/questions/does-a-delayed-rpc-run-as-the-session-it-was-scheduled-with.md +26 -0
- package/knowledge/questions/does-a-user-id-with-a-colon-read-another-users-credentials.md +27 -0
- package/knowledge/questions/does-a-wirecli-command-need-a-session-by-default.md +26 -0
- package/knowledge/questions/does-an-adversarial-in-scope-success-count-as-a-finding.md +26 -0
- package/knowledge/questions/does-approve-step-need-the-run-owners-session-or-the-gates-approvers.md +34 -0
- package/knowledge/questions/does-cors-origin-true-reflect-any-origin-with-credentials.md +20 -0
- package/knowledge/questions/does-skip-user-session-do-anything-on-a-channel.md +24 -0
- package/knowledge/questions/does-the-store-session-match-the-other-two.md +19 -0
- package/knowledge/questions/index.md +25 -2
- package/knowledge/questions/is-the-agent-score-worker-reachable-without-a-session.md +21 -0
- package/knowledge/questions/live-scheduled-tests-may-write.md +19 -0
- package/knowledge/questions/provision-virtual-users-has-no-caller-in-this-repo.md +23 -0
- package/knowledge/questions/remote-secret-refuses-requests-without-a-remote-token-on-every-path.md +32 -0
- package/knowledge/questions/should-a-middleware-set-session-persist-to-the-store.md +25 -0
- package/knowledge/questions/should-a-scoped-secret-view-throw-from-has-secret-for-a-disallowed-key.md +23 -0
- package/knowledge/questions/should-a-wrong-bearer-token-be-anonymous-or-refused.md +35 -0
- package/knowledge/questions/should-an-empty-webhook-signing-secret-send-unsigned.md +23 -0
- package/knowledge/questions/should-http-virtual-user-sessions-reset-between-scenarios-in-one-run.md +27 -0
- package/knowledge/questions/should-the-cookie-jar-scope-cookies-to-the-api-origin.md +26 -0
- package/knowledge/questions/should-voice-input-fetch-audio-urls-without-a-host-allowlist.md +20 -0
- package/knowledge/questions/staging-impersonation-rules.md +27 -0
- package/knowledge/questions/virtual-user-keys-on-cloud-stages-are-pikku-studios-to-build.md +34 -0
- package/knowledge/questions/virtual-user-statement-replay-is-per-process.md +20 -0
- package/knowledge/questions/what-an-impersonated-session-carries.md +22 -0
- package/package.json +2 -3
- package/src/public-surface.json +38 -47
- package/dist/services/persona-actor-secret.d.ts +0 -38
- package/dist/services/persona-actor-secret.js +0 -39
- package/dist/services/persona-sign-in.d.ts +0 -117
- package/dist/services/persona-sign-in.js +0 -154
- package/dist/wirings/actor-flow/index.d.ts +0 -2
- package/dist/wirings/actor-flow/run-conversation.d.ts +0 -36
- package/dist/wirings/persona/define-personas.d.ts +0 -32
- package/dist/wirings/persona/index.d.ts +0 -23
- package/dist/wirings/persona/index.js +0 -19
- package/knowledge/decisions/internals/actor-flow-conversations-seed-a-hidden-kickoff-message.md +0 -23
- package/knowledge/decisions/internals/actor-flow-verdicts-are-llm-self-evaluations.md +0 -25
- package/knowledge/decisions/internals/validate-checks-personas-through-a-shared-module.md +0 -43
- package/knowledge/decisions/security/actor-sign-in-only-works-for-actor-flagged-users.md +0 -31
- package/knowledge/decisions/security/an-actor-credential-is-derived-per-persona.md +0 -42
- package/knowledge/decisions/security/pikku-carries-actor-scopes-as-data-and-the-app-grants-them.md +0 -26
- /package/dist/wirings/{actor-flow/actor-flow.types.js → virtual-user/conversation/conversation.types.js} +0 -0
- /package/dist/wirings/{actor-flow → virtual-user/conversation}/index.js +0 -0
- /package/dist/wirings/{persona/persona.types.js → virtual-user/virtual-user-declaration.types.js} +0 -0
- /package/dist/wirings/{persona/persona-mailbox.js → virtual-user/virtual-user-mailbox.js} +0 -0
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* No-op function for declaring
|
|
2
|
+
* No-op function for declaring virtual users.
|
|
3
3
|
* This exists purely for TypeScript type checking and will be tree-shaken.
|
|
4
|
-
* The CLI extracts metadata via AST parsing and generates a `
|
|
5
|
-
* and a `
|
|
4
|
+
* The CLI extracts metadata via AST parsing and generates a `VirtualUserId` union
|
|
5
|
+
* and a `virtual-users.gen.json` the console and `pikku virtual-user run` read off disk —
|
|
6
6
|
* describing, listing or running one never has to load the app.
|
|
7
7
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
8
|
+
* Declaring is not running: a virtual user that exists only to be acted upon
|
|
9
|
+
* (the account an admin bans, the colleague a document is shared with) is
|
|
10
|
+
* seeded and never run.
|
|
11
11
|
*
|
|
12
|
-
* Exactly one `
|
|
12
|
+
* Exactly one `defineVirtualUsers(...)` is allowed per codebase, so there is one
|
|
13
13
|
* place to read the cast from and one place to add to. A second call — even in
|
|
14
14
|
* the same file — fails the build.
|
|
15
15
|
*
|
|
16
16
|
* @example
|
|
17
17
|
* ```typescript
|
|
18
|
-
*
|
|
18
|
+
* defineVirtualUsers({
|
|
19
19
|
* susan: {
|
|
20
20
|
* name: 'Susan',
|
|
21
21
|
* jobTitle: 'Buys for a small café',
|
|
@@ -28,4 +28,4 @@
|
|
|
28
28
|
* })
|
|
29
29
|
* ```
|
|
30
30
|
*/
|
|
31
|
-
export const
|
|
31
|
+
export const defineVirtualUsers = (_config) => { };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Virtual user module exports.
|
|
3
3
|
*
|
|
4
|
-
* A virtual user is an LLM
|
|
4
|
+
* A virtual user is an LLM that signs into a *running* stage
|
|
5
5
|
* and works its API in character — non-deterministically, with a budget, and
|
|
6
6
|
* with oracles watching what comes back. It is not a test runner: nothing here
|
|
7
7
|
* asserts an expected outcome, because the point is the outcomes nobody thought
|
|
@@ -9,24 +9,47 @@
|
|
|
9
9
|
*
|
|
10
10
|
* Everything it needs is derived from what a pikku project already generates —
|
|
11
11
|
* the function meta becomes the catalogue, the scenario meta becomes the
|
|
12
|
-
* intents, the declared
|
|
12
|
+
* intents, the declared virtual users become the identities — so adopting it is
|
|
13
13
|
* configuration rather than authoring.
|
|
14
14
|
*
|
|
15
15
|
* The engine is transport-agnostic: the virtual user drives an
|
|
16
|
-
* {@link VirtualUserTarget}, which in production is an `
|
|
16
|
+
* {@link VirtualUserTarget}, which in production is an `HttpVirtualUser`
|
|
17
17
|
* signed in as a real user against staging or production.
|
|
18
18
|
*/
|
|
19
19
|
export type { ApiCatalogueEntry, IntentRecord, IntentSource, StepRecord, VirtualUserBudget, VirtualUserDisposition, VirtualUserFinding, VirtualUserRunResult, VirtualUserTarget, } from './virtual-user.types.js';
|
|
20
20
|
export { PRODUCTION_DISPOSITION } from './virtual-user.types.js';
|
|
21
21
|
export { runVirtualUser, type RunVirtualUserParams, } from './run-virtual-user.js';
|
|
22
|
-
export {
|
|
22
|
+
export { virtualUserScopes, prepareVirtualUserRun, type VirtualUserPreparation, } from './prepare-virtual-user-run.js';
|
|
23
23
|
export type { VirtualUserRunOutcome, VirtualUserRunRecord, VirtualUserRunStart, VirtualUserRunStore, } from './virtual-user-run-store.js';
|
|
24
24
|
export type { VirtualUserScheduleInput, VirtualUserScheduleRecord, VirtualUserScheduleStore, } from './virtual-user-schedule-store.js';
|
|
25
25
|
export { DEFAULT_MAX_INTERVAL_MS, DEFAULT_MIN_INTERVAL_MS, isDue, nextRunAt, STALE_RUN_AFTER_MS, tickVirtualUserSchedules, type VirtualUserTickParams, type VirtualUserTickResult, } from './virtual-user-schedule.js';
|
|
26
26
|
export { DISPOSITIONS, dispositionProfile, type DispositionProfile, type VirtualUserTuning, } from './virtual-user-dispositions.js';
|
|
27
27
|
export { catalogueClassification, catalogueLookup, isReadOnly, reachableCatalogue, unreachableCatalogue, } from './virtual-user-catalogue.js';
|
|
28
28
|
export { type AgentReachability, type ReachableAgent, } from './virtual-user-agents.js';
|
|
29
|
-
export { IntentStack,
|
|
29
|
+
export { IntentStack, intentsForVirtualUser } from './virtual-user-intents.js';
|
|
30
30
|
export { deriveCatalogue, deriveIntents, type SchemaMap, } from './virtual-user-derive.js';
|
|
31
|
-
export {
|
|
32
|
-
export { executeVirtualUserRun, logVirtualUserTick, requireVirtualUserRunStore, requireVirtualUserScheduleStore,
|
|
31
|
+
export { virtualUserTarget, type VirtualUserTargetOptions, } from './virtual-user-target.js';
|
|
32
|
+
export { executeVirtualUserRun, logVirtualUserTick, requireVirtualUserRunStore, requireVirtualUserScheduleStore, runnableVirtualUser, serializeVirtualUserRun, serializeVirtualUserSchedule, serializeVirtualUserSteps, startVirtualUserRun, VIRTUAL_USER_VARIABLES, virtualUserScheduleRunInput, writeVirtualUserSchedule, type ExecuteVirtualUserRunParams, type ScaffoldVirtualUsers, type StartedVirtualUserRun, type StartVirtualUserRunParams, type WriteVirtualUserScheduleParams, } from './virtual-user-scaffold.js';
|
|
33
|
+
export { defineVirtualUsers } from './define-virtual-users.js';
|
|
34
|
+
export { isRunnableVirtualUser, roleMismatchMessage, runnableVirtualUsers, validateAndBuildVirtualUsersMeta, verifyVirtualUserRoles, } from './validate-virtual-users.js';
|
|
35
|
+
export type { RoleVerification } from './validate-virtual-users.js';
|
|
36
|
+
export { virtualUserEnvironmentErrors, virtualUserEnvironmentRefusal, } from './virtual-user-environments.js';
|
|
37
|
+
export type { VirtualUserEnvironment, VirtualUserEnvironmentSubject, } from './virtual-user-environments.js';
|
|
38
|
+
export { virtualUserEmail, virtualUserEmails } from './virtual-user-email.js';
|
|
39
|
+
export { APP_SCOPE_ROOT, appScopeId, buildAppScopeDefinition, } from './virtual-user-app-scopes.js';
|
|
40
|
+
export type { MailboxAllowlist, VirtualUserMailbox, ReceivedEmail, } from './virtual-user-mailbox.js';
|
|
41
|
+
export type { CoreVirtualUser, CoreVirtualUsers, VirtualUserAccountMeta, VirtualUserDefinitions, VirtualUserMeta, VirtualUsersMeta, } from './virtual-user-declaration.types.js';
|
|
42
|
+
/**
|
|
43
|
+
* Signing in as a virtual user and talking to a stage over HTTP.
|
|
44
|
+
*
|
|
45
|
+
* Deliberately reached through this entry point rather than `@pikku/core/services`.
|
|
46
|
+
* `HttpVirtualUser` pulls in the conversation runner and, through it, the
|
|
47
|
+
* agent runner — none of which a production server has any use for. Exporting it
|
|
48
|
+
* from the services barrel put that chain in the module graph of every app that
|
|
49
|
+
* imports services, which only a bundler could remove and an unbundled Node or
|
|
50
|
+
* Lambda deploy would load outright.
|
|
51
|
+
*/
|
|
52
|
+
export { HttpVirtualUser, createHttpVirtualUsers, type HttpVirtualUsersConfig, } from '../../services/http-virtual-users.js';
|
|
53
|
+
export { statementSignIn, inProcessSignIn, authMount, type VirtualUserSignIn, type VirtualUserStatementMint, type StatementSignInOptions, type InProcessAuth, } from '../../services/virtual-user-sign-in.js';
|
|
54
|
+
export { createCookieJar, type ScenarioCookieJar, } from '../../services/scenario-cookie-jar.js';
|
|
55
|
+
export { postScenarioJson, readScenarioHttpResponse, } from '../../services/virtual-users-service.js';
|
|
@@ -1,10 +1,29 @@
|
|
|
1
1
|
export { PRODUCTION_DISPOSITION } from './virtual-user.types.js';
|
|
2
2
|
export { runVirtualUser, } from './run-virtual-user.js';
|
|
3
|
-
export {
|
|
3
|
+
export { virtualUserScopes, prepareVirtualUserRun, } from './prepare-virtual-user-run.js';
|
|
4
4
|
export { DEFAULT_MAX_INTERVAL_MS, DEFAULT_MIN_INTERVAL_MS, isDue, nextRunAt, STALE_RUN_AFTER_MS, tickVirtualUserSchedules, } from './virtual-user-schedule.js';
|
|
5
5
|
export { DISPOSITIONS, dispositionProfile, } from './virtual-user-dispositions.js';
|
|
6
6
|
export { catalogueClassification, catalogueLookup, isReadOnly, reachableCatalogue, unreachableCatalogue, } from './virtual-user-catalogue.js';
|
|
7
|
-
export { IntentStack,
|
|
7
|
+
export { IntentStack, intentsForVirtualUser } from './virtual-user-intents.js';
|
|
8
8
|
export { deriveCatalogue, deriveIntents, } from './virtual-user-derive.js';
|
|
9
|
-
export {
|
|
10
|
-
export { executeVirtualUserRun, logVirtualUserTick, requireVirtualUserRunStore, requireVirtualUserScheduleStore,
|
|
9
|
+
export { virtualUserTarget, } from './virtual-user-target.js';
|
|
10
|
+
export { executeVirtualUserRun, logVirtualUserTick, requireVirtualUserRunStore, requireVirtualUserScheduleStore, runnableVirtualUser, serializeVirtualUserRun, serializeVirtualUserSchedule, serializeVirtualUserSteps, startVirtualUserRun, VIRTUAL_USER_VARIABLES, virtualUserScheduleRunInput, writeVirtualUserSchedule, } from './virtual-user-scaffold.js';
|
|
11
|
+
export { defineVirtualUsers } from './define-virtual-users.js';
|
|
12
|
+
export { isRunnableVirtualUser, roleMismatchMessage, runnableVirtualUsers, validateAndBuildVirtualUsersMeta, verifyVirtualUserRoles, } from './validate-virtual-users.js';
|
|
13
|
+
export { virtualUserEnvironmentErrors, virtualUserEnvironmentRefusal, } from './virtual-user-environments.js';
|
|
14
|
+
export { virtualUserEmail, virtualUserEmails } from './virtual-user-email.js';
|
|
15
|
+
export { APP_SCOPE_ROOT, appScopeId, buildAppScopeDefinition, } from './virtual-user-app-scopes.js';
|
|
16
|
+
/**
|
|
17
|
+
* Signing in as a virtual user and talking to a stage over HTTP.
|
|
18
|
+
*
|
|
19
|
+
* Deliberately reached through this entry point rather than `@pikku/core/services`.
|
|
20
|
+
* `HttpVirtualUser` pulls in the conversation runner and, through it, the
|
|
21
|
+
* agent runner — none of which a production server has any use for. Exporting it
|
|
22
|
+
* from the services barrel put that chain in the module graph of every app that
|
|
23
|
+
* imports services, which only a bundler could remove and an unbundled Node or
|
|
24
|
+
* Lambda deploy would load outright.
|
|
25
|
+
*/
|
|
26
|
+
export { HttpVirtualUser, createHttpVirtualUsers, } from '../../services/http-virtual-users.js';
|
|
27
|
+
export { statementSignIn, inProcessSignIn, authMount, } from '../../services/virtual-user-sign-in.js';
|
|
28
|
+
export { createCookieJar, } from '../../services/scenario-cookie-jar.js';
|
|
29
|
+
export { postScenarioJson, readScenarioHttpResponse, } from '../../services/virtual-users-service.js';
|
|
@@ -5,19 +5,19 @@ import { type SchemaMap } from './virtual-user-derive.js';
|
|
|
5
5
|
import { type AgentReachability, type ReachableAgent } from './virtual-user-agents.js';
|
|
6
6
|
import type { ApiCatalogueEntry, IntentSource } from './virtual-user.types.js';
|
|
7
7
|
/**
|
|
8
|
-
* The scopes a
|
|
8
|
+
* The scopes a virtual user holds, resolved through its roles.
|
|
9
9
|
*
|
|
10
|
-
* Roles are the only thing a
|
|
10
|
+
* Roles are the only thing a virtual user declares; scopes are what a function
|
|
11
11
|
* checks. Narrowing a virtual user's catalogue needs the second, so the
|
|
12
12
|
* expansion happens once, here, against the same `defineSystemRole` definitions
|
|
13
13
|
* the seed grants from.
|
|
14
14
|
*/
|
|
15
|
-
export declare const
|
|
15
|
+
export declare const virtualUserScopes: (virtualUser: {
|
|
16
16
|
roles?: readonly string[];
|
|
17
17
|
}, roleScopes: Record<string, readonly string[]>) => string[];
|
|
18
18
|
/** Everything a run needs that is derived rather than decided. */
|
|
19
19
|
export interface VirtualUserPreparation {
|
|
20
|
-
/** Every RPC the app exposes, narrowed to what this
|
|
20
|
+
/** Every RPC the app exposes, narrowed to what this virtual user can reach. */
|
|
21
21
|
catalogue: ApiCatalogueEntry[];
|
|
22
22
|
intents: IntentSource[];
|
|
23
23
|
scopes: string[];
|
|
@@ -27,18 +27,18 @@ export interface VirtualUserPreparation {
|
|
|
27
27
|
* Derive what a virtual user needs from what the project already generates.
|
|
28
28
|
*
|
|
29
29
|
* Shared because there are two callers with the same problem and different
|
|
30
|
-
* sources for it: `pikku
|
|
30
|
+
* sources for it: `pikku virtual-user run` reads the inspector state at build time,
|
|
31
31
|
* and the scaffolded `runVirtualUser` RPC reads `metaService` at runtime. They
|
|
32
|
-
* must agree — a
|
|
32
|
+
* must agree — a virtual user whose catalogue is narrower over RPC than on the CLI
|
|
33
33
|
* finds different things from the same seed, which is exactly the property a
|
|
34
34
|
* seed exists to give.
|
|
35
35
|
*
|
|
36
36
|
* Nothing here is authored for the virtual user's benefit: the function meta is
|
|
37
37
|
* the catalogue, the scenario meta is the intents, and the role definitions are
|
|
38
|
-
* what turn a
|
|
38
|
+
* what turn a virtual user's declared roles into the scopes a function checks.
|
|
39
39
|
*/
|
|
40
40
|
export declare const prepareVirtualUserRun: (input: {
|
|
41
|
-
|
|
41
|
+
virtualUser: {
|
|
42
42
|
roles?: readonly string[];
|
|
43
43
|
};
|
|
44
44
|
functionsMeta: FunctionsMeta;
|
|
@@ -2,16 +2,16 @@ import { flattenSystemRoleDefinitions } from '../role/validate-role-definitions.
|
|
|
2
2
|
import { deriveCatalogue, deriveIntents, } from './virtual-user-derive.js';
|
|
3
3
|
import { reachableAgents, } from './virtual-user-agents.js';
|
|
4
4
|
/**
|
|
5
|
-
* The scopes a
|
|
5
|
+
* The scopes a virtual user holds, resolved through its roles.
|
|
6
6
|
*
|
|
7
|
-
* Roles are the only thing a
|
|
7
|
+
* Roles are the only thing a virtual user declares; scopes are what a function
|
|
8
8
|
* checks. Narrowing a virtual user's catalogue needs the second, so the
|
|
9
9
|
* expansion happens once, here, against the same `defineSystemRole` definitions
|
|
10
10
|
* the seed grants from.
|
|
11
11
|
*/
|
|
12
|
-
export const
|
|
12
|
+
export const virtualUserScopes = (virtualUser, roleScopes) => {
|
|
13
13
|
const scopes = new Set();
|
|
14
|
-
for (const role of
|
|
14
|
+
for (const role of virtualUser.roles ?? []) {
|
|
15
15
|
for (const scope of roleScopes[role] ?? []) {
|
|
16
16
|
scopes.add(scope);
|
|
17
17
|
}
|
|
@@ -22,15 +22,15 @@ export const personaScopes = (persona, roleScopes) => {
|
|
|
22
22
|
* Derive what a virtual user needs from what the project already generates.
|
|
23
23
|
*
|
|
24
24
|
* Shared because there are two callers with the same problem and different
|
|
25
|
-
* sources for it: `pikku
|
|
25
|
+
* sources for it: `pikku virtual-user run` reads the inspector state at build time,
|
|
26
26
|
* and the scaffolded `runVirtualUser` RPC reads `metaService` at runtime. They
|
|
27
|
-
* must agree — a
|
|
27
|
+
* must agree — a virtual user whose catalogue is narrower over RPC than on the CLI
|
|
28
28
|
* finds different things from the same seed, which is exactly the property a
|
|
29
29
|
* seed exists to give.
|
|
30
30
|
*
|
|
31
31
|
* Nothing here is authored for the virtual user's benefit: the function meta is
|
|
32
32
|
* the catalogue, the scenario meta is the intents, and the role definitions are
|
|
33
|
-
* what turn a
|
|
33
|
+
* what turn a virtual user's declared roles into the scopes a function checks.
|
|
34
34
|
*/
|
|
35
35
|
export const prepareVirtualUserRun = (input) => {
|
|
36
36
|
const catalogue = deriveCatalogue(input.functionsMeta, input.schemas ?? {});
|
|
@@ -40,10 +40,10 @@ export const prepareVirtualUserRun = (input) => {
|
|
|
40
40
|
for (const role of flattenSystemRoleDefinitions(Array.isArray(declared) ? declared : Object.values(declared))) {
|
|
41
41
|
roleScopes[role.name] = role.scopes;
|
|
42
42
|
}
|
|
43
|
-
const scopes =
|
|
43
|
+
const scopes = virtualUserScopes(input.virtualUser, roleScopes);
|
|
44
44
|
// Gated by the same scopes as the RPCs, because an agent is reached rather
|
|
45
45
|
// than declared: `CoreAgent.scopes` is checked against the session, so a
|
|
46
|
-
//
|
|
46
|
+
// virtual user finds the specialists its roles unlock and no others.
|
|
47
47
|
const agents = reachableAgents(input.agentsMeta ?? {}, scopes);
|
|
48
48
|
return { catalogue, intents, scopes, agents };
|
|
49
49
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type {
|
|
1
|
+
import type { ResolvedVirtualUser } from '../../services/virtual-users-service.js';
|
|
2
|
+
import type { VirtualUserLLM } from './conversation/run-conversation.js';
|
|
3
3
|
import { type VirtualUserTuning } from './virtual-user-dispositions.js';
|
|
4
4
|
import type { ApiCatalogueEntry, IntentSource, VirtualUserBudget, VirtualUserDisposition, VirtualUserFinding, VirtualUserRunResult, VirtualUserTally, VirtualUserTarget } from './virtual-user.types.js';
|
|
5
5
|
/** What an app-specific oracle is shown about one call. */
|
|
@@ -15,9 +15,9 @@ export interface VirtualUserCallContext {
|
|
|
15
15
|
}
|
|
16
16
|
export interface RunVirtualUserParams {
|
|
17
17
|
/** The person being run — name, job title, personality, goals. */
|
|
18
|
-
|
|
18
|
+
virtualUser: ResolvedVirtualUser;
|
|
19
19
|
/** The id they were declared under, as the intent sources refer to them. */
|
|
20
|
-
|
|
20
|
+
virtualUserId: string;
|
|
21
21
|
/** How it behaves. Default `realistic`. */
|
|
22
22
|
disposition?: VirtualUserDisposition;
|
|
23
23
|
/** Per-user overrides for that disposition's dials. */
|
|
@@ -31,7 +31,7 @@ export interface RunVirtualUserParams {
|
|
|
31
31
|
/** The transport it acts through. */
|
|
32
32
|
target: VirtualUserTarget;
|
|
33
33
|
/** The user's own LLM. */
|
|
34
|
-
llm:
|
|
34
|
+
llm: VirtualUserLLM;
|
|
35
35
|
model: string;
|
|
36
36
|
budget?: VirtualUserBudget;
|
|
37
37
|
/**
|
|
@@ -60,7 +60,7 @@ export interface RunVirtualUserParams {
|
|
|
60
60
|
/** Ids and slugs remembered from an earlier run, for a `stale` user. */
|
|
61
61
|
memory?: Record<string, string>;
|
|
62
62
|
/**
|
|
63
|
-
* The scopes this
|
|
63
|
+
* The scopes this virtual user actually holds, resolved from its roles at sign-in.
|
|
64
64
|
*
|
|
65
65
|
* Used two ways: to narrow a large catalogue to what it is entitled to reach,
|
|
66
66
|
* and as an oracle — a call whose scopes it does not hold that nonetheless
|
|
@@ -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,
|
|
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
|
|
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 ${
|
|
114
|
-
|
|
115
|
-
|
|
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 {
|
|
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
|
-
...
|
|
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 =
|
|
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
|
|
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 '${
|
|
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,
|
package/dist/wirings/{persona/validate-personas.d.ts → virtual-user/validate-virtual-users.d.ts}
RENAMED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { VirtualUserDefinitions, VirtualUserMeta, VirtualUsersMeta } from './virtual-user-declaration.types.js';
|
|
2
2
|
/**
|
|
3
|
-
* Whether a
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
53
|
+
export declare const runnableVirtualUsers: (meta: VirtualUsersMeta) => VirtualUserMeta[];
|
package/dist/wirings/{persona/validate-personas.js → virtual-user/validate-virtual-users.js}
RENAMED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Whether a
|
|
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
|
|
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
|
|
14
|
-
if (
|
|
13
|
+
export const isRunnableVirtualUser = (virtualUser) => {
|
|
14
|
+
if (virtualUser.runnable === false) {
|
|
15
15
|
return false;
|
|
16
16
|
}
|
|
17
|
-
return
|
|
17
|
+
return virtualUser.account?.provider === undefined;
|
|
18
18
|
};
|
|
19
19
|
/**
|
|
20
|
-
* Validates declared
|
|
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
|
|
25
|
+
export function validateAndBuildVirtualUsersMeta(definitions) {
|
|
26
26
|
const meta = {};
|
|
27
|
-
for (const
|
|
28
|
-
if (!
|
|
29
|
-
throw new Error('A
|
|
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 (!
|
|
32
|
-
throw new Error(`
|
|
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[
|
|
34
|
+
const existing = meta[virtualUser.id];
|
|
35
35
|
if (existing) {
|
|
36
36
|
const same = JSON.stringify({ ...existing, sourceFile: undefined }) ===
|
|
37
|
-
JSON.stringify({ ...
|
|
37
|
+
JSON.stringify({ ...virtualUser, sourceFile: undefined });
|
|
38
38
|
if (!same) {
|
|
39
|
-
throw new Error(`
|
|
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: ${
|
|
41
|
+
` Second declaration: ${virtualUser.sourceFile ?? 'unknown'}`);
|
|
42
42
|
}
|
|
43
43
|
continue;
|
|
44
44
|
}
|
|
45
|
-
meta[
|
|
45
|
+
meta[virtualUser.id] = virtualUser;
|
|
46
46
|
}
|
|
47
47
|
return meta;
|
|
48
48
|
}
|
|
49
49
|
/**
|
|
50
|
-
* Compares a
|
|
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
|
|
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
|
|
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
|
-
|
|
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 (`
|
|
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
|
|
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
|
|
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
|
|
2
|
+
* Which of the product's agents a virtual user can actually talk to.
|
|
3
3
|
*
|
|
4
|
-
* An agent is not something a
|
|
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
|
|
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
|
|
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
|
-
*
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
package/dist/wirings/{persona/persona-app-scopes.d.ts → virtual-user/virtual-user-app-scopes.d.ts}
RENAMED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ScopeDefinitionMeta } from '../scope/scope.types.js';
|
|
2
|
-
import type {
|
|
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
|
|
13
|
+
* The apps named across the declared virtual users, sorted and deduplicated.
|
|
14
14
|
*
|
|
15
|
-
* The
|
|
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
|
-
*
|
|
18
|
+
* virtual user and they name it.
|
|
19
19
|
*/
|
|
20
|
-
export declare const declaredApps: (
|
|
20
|
+
export declare const declaredApps: (virtualUsers: VirtualUserMeta[]) => string[];
|
|
21
21
|
/**
|
|
22
|
-
* The `app` scope tree, synthesised from the
|
|
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
|
|
35
|
-
* a
|
|
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
|
|
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: (
|
|
41
|
+
export declare const buildAppScopeDefinition: (virtualUsers: VirtualUserMeta[]) => ScopeDefinitionMeta | null;
|