@pikku/core 0.12.72 → 0.12.74
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 +793 -0
- package/dist/crypto-utils.d.ts +30 -5
- package/dist/crypto-utils.js +146 -41
- package/dist/dev/hot-reload.js +11 -30
- package/dist/dev/module-runner.d.ts +3 -7
- package/dist/dev/module-runner.js +4 -10
- package/dist/dev/reload-meta.d.ts +8 -20
- package/dist/dev/reload-meta.js +9 -29
- package/dist/errors/error-handler.d.ts +5 -30
- package/dist/errors/error-handler.js +16 -32
- package/dist/errors/errors.d.ts +32 -151
- package/dist/errors/errors.js +55 -157
- package/dist/function/abort-scope.d.ts +47 -0
- package/dist/function/abort-scope.js +63 -0
- package/dist/function/function-runner.js +37 -32
- package/dist/function/functions.types.d.ts +44 -136
- package/dist/function/functions.types.js +0 -58
- package/dist/function/list.types.d.ts +12 -62
- package/dist/function/list.types.js +4 -25
- package/dist/handle-error.d.ts +0 -13
- package/dist/handle-error.js +0 -18
- package/dist/index.d.ts +5 -2
- package/dist/index.js +4 -1
- package/dist/middleware/auth-apikey.d.ts +3 -18
- package/dist/middleware/auth-apikey.js +0 -17
- package/dist/middleware/auth-bearer.d.ts +6 -41
- package/dist/middleware/auth-bearer.js +3 -40
- package/dist/middleware/auth-cookie.d.ts +5 -27
- package/dist/middleware/auth-cookie.js +2 -26
- package/dist/middleware/cors.d.ts +7 -34
- package/dist/middleware/cors.js +7 -34
- package/dist/middleware/remote-auth.d.ts +3 -1
- package/dist/middleware/remote-auth.js +3 -2
- package/dist/middleware/telemetry.d.ts +8 -33
- package/dist/middleware/telemetry.js +2 -31
- package/dist/middleware-runner.d.ts +4 -55
- package/dist/middleware-runner.js +5 -74
- package/dist/permissions.d.ts +3 -44
- package/dist/permissions.js +19 -71
- package/dist/pikku-request.d.ts +0 -6
- package/dist/pikku-request.js +0 -6
- package/dist/pikku-state.d.ts +0 -26
- package/dist/pikku-state.js +2 -30
- package/dist/remote.d.ts +3 -5
- package/dist/remote.js +8 -7
- package/dist/schema.d.ts +5 -39
- package/dist/schema.js +5 -39
- package/dist/scopes.d.ts +4 -23
- package/dist/scopes.js +7 -48
- package/dist/services/ai-agent-runner-service.d.ts +20 -0
- package/dist/services/ai-embedding-service.d.ts +2 -25
- package/dist/services/audit-service.js +1 -2
- package/dist/services/content-service.d.ts +1 -46
- package/dist/services/credential-service.d.ts +3 -40
- package/dist/services/deployment-service.d.ts +3 -9
- package/dist/services/gateway-service.d.ts +0 -15
- package/dist/services/http-personas.d.ts +80 -0
- package/dist/services/http-personas.js +233 -0
- package/dist/services/in-memory-queue-service.d.ts +0 -14
- package/dist/services/in-memory-queue-service.js +1 -15
- package/dist/services/in-memory-trigger-service.d.ts +0 -18
- package/dist/services/in-memory-trigger-service.js +1 -18
- package/dist/services/in-memory-workflow-service.d.ts +0 -16
- package/dist/services/in-memory-workflow-service.js +4 -33
- package/dist/services/index.d.ts +5 -6
- package/dist/services/index.js +2 -5
- package/dist/services/istanbul-coverage-service.d.ts +1 -5
- package/dist/services/istanbul-coverage-service.js +2 -8
- package/dist/services/jwt-service.d.ts +1 -16
- package/dist/services/local-content.d.ts +13 -2
- package/dist/services/local-content.js +40 -13
- package/dist/services/local-gateway-service.d.ts +0 -16
- package/dist/services/local-gateway-service.js +2 -17
- package/dist/services/local-secrets.d.ts +0 -4
- package/dist/services/local-secrets.js +0 -4
- package/dist/services/logger-console.d.ts +3 -7
- package/dist/services/logger-console.js +3 -7
- package/dist/services/logger.d.ts +2 -37
- package/dist/services/meta-service.d.ts +23 -26
- package/dist/services/meta-service.js +22 -36
- package/dist/services/personas-service.d.ts +134 -0
- package/dist/services/personas-service.js +40 -0
- package/dist/services/pikku-user-id.js +0 -4
- package/dist/services/queue-webhook-service.d.ts +2 -36
- package/dist/services/queue-webhook-service.js +9 -41
- package/dist/services/scheduler-service.d.ts +1 -50
- package/dist/services/scheduler-service.js +0 -10
- package/dist/services/schema-service.d.ts +1 -24
- package/dist/services/scope-service.d.ts +49 -34
- package/dist/services/scoped-secret-service.d.ts +0 -4
- package/dist/services/scoped-secret-service.js +0 -4
- package/dist/services/secret-host-binding.d.ts +8 -0
- package/dist/services/secret-host-binding.js +36 -0
- package/dist/services/secret-service.d.ts +5 -33
- package/dist/services/secretless.d.ts +6 -0
- package/dist/services/secretless.js +21 -0
- package/dist/services/stub-tracker.d.ts +7 -18
- package/dist/services/stub-tracker.js +8 -18
- package/dist/services/system-role-guard.d.ts +33 -0
- package/dist/services/system-role-guard.js +38 -0
- package/dist/services/trigger-service.d.ts +0 -12
- package/dist/services/typed-secret-service.d.ts +0 -7
- package/dist/services/typed-secret-service.js +1 -7
- package/dist/services/v8-coverage-service.d.ts +2 -3
- package/dist/services/v8-coverage-service.js +1 -2
- package/dist/services/variables-service.d.ts +1 -8
- package/dist/services/webhook-service.d.ts +19 -63
- package/dist/services/webhook-service.js +6 -20
- package/dist/services/workflow-service.d.ts +3 -15
- package/dist/testing/service-tests.js +0 -17
- package/dist/time-utils.d.ts +0 -16
- package/dist/time-utils.js +1 -19
- package/dist/types/core.types.d.ts +99 -219
- package/dist/types/core.types.js +0 -42
- package/dist/types/state.types.d.ts +4 -9
- package/dist/utils/hmac.d.ts +4 -10
- package/dist/utils/hmac.js +4 -10
- package/dist/utils/safe-fetch.d.ts +7 -35
- package/dist/utils/safe-fetch.js +13 -53
- package/dist/utils.d.ts +1 -6
- package/dist/utils.js +6 -15
- package/dist/wirings/actor-flow/actor-flow.types.d.ts +1 -34
- package/dist/wirings/actor-flow/index.d.ts +0 -9
- package/dist/wirings/actor-flow/run-conversation.d.ts +5 -5
- package/dist/wirings/actor-flow/run-conversation.js +14 -7
- package/dist/wirings/ai-agent/ai-agent-agui.d.ts +0 -5
- package/dist/wirings/ai-agent/ai-agent-agui.js +34 -12
- package/dist/wirings/ai-agent/ai-agent-helpers.d.ts +7 -0
- package/dist/wirings/ai-agent/ai-agent-helpers.js +7 -0
- package/dist/wirings/ai-agent/ai-agent-interrupt.d.ts +153 -0
- package/dist/wirings/ai-agent/ai-agent-interrupt.js +256 -0
- package/dist/wirings/ai-agent/ai-agent-memory.js +0 -2
- package/dist/wirings/ai-agent/ai-agent-model-config.d.ts +0 -9
- package/dist/wirings/ai-agent/ai-agent-model-config.js +1 -9
- package/dist/wirings/ai-agent/ai-agent-prepare.d.ts +10 -99
- package/dist/wirings/ai-agent/ai-agent-prepare.js +58 -131
- package/dist/wirings/ai-agent/ai-agent-registry.d.ts +2 -1
- package/dist/wirings/ai-agent/ai-agent-registry.js +5 -1
- package/dist/wirings/ai-agent/ai-agent-runner.js +49 -20
- package/dist/wirings/ai-agent/ai-agent-stream.d.ts +25 -2
- package/dist/wirings/ai-agent/ai-agent-stream.js +153 -63
- package/dist/wirings/ai-agent/ai-agent.types.d.ts +84 -4
- package/dist/wirings/ai-agent/index.d.ts +6 -4
- package/dist/wirings/ai-agent/index.js +5 -4
- package/dist/wirings/ai-agent/voice-input.d.ts +39 -1
- package/dist/wirings/ai-agent/voice-input.js +46 -3
- package/dist/wirings/ai-agent/voice-output.d.ts +54 -1
- package/dist/wirings/ai-agent/voice-output.js +153 -50
- package/dist/wirings/channel/channel-common.d.ts +7 -20
- package/dist/wirings/channel/channel-common.js +7 -21
- package/dist/wirings/channel/channel-handler.js +25 -6
- package/dist/wirings/channel/channel-host-rpc.d.ts +25 -0
- package/dist/wirings/channel/channel-host-rpc.js +38 -0
- package/dist/wirings/channel/channel-middleware-runner.d.ts +0 -12
- package/dist/wirings/channel/channel-middleware-runner.js +0 -12
- package/dist/wirings/channel/channel-rpc-registry.d.ts +31 -0
- package/dist/wirings/channel/channel-rpc-registry.js +89 -0
- package/dist/wirings/channel/channel-rpc-responder.d.ts +15 -0
- package/dist/wirings/channel/channel-rpc-responder.js +71 -0
- package/dist/wirings/channel/channel-rpc-service.d.ts +40 -0
- package/dist/wirings/channel/channel-rpc-service.js +106 -0
- package/dist/wirings/channel/channel-rpc-validators.d.ts +14 -0
- package/dist/wirings/channel/channel-rpc-validators.js +30 -0
- package/dist/wirings/channel/channel-rpc.d.ts +5 -0
- package/dist/wirings/channel/channel-rpc.js +5 -0
- package/dist/wirings/channel/channel-rpc.types.d.ts +90 -0
- package/dist/wirings/channel/channel-rpc.types.js +50 -0
- package/dist/wirings/channel/channel-runner.d.ts +0 -4
- package/dist/wirings/channel/channel-runner.js +0 -14
- package/dist/wirings/channel/channel-store.d.ts +0 -10
- package/dist/wirings/channel/channel.types.d.ts +12 -1
- package/dist/wirings/channel/define-channel-routes.d.ts +0 -20
- package/dist/wirings/channel/define-channel-routes.js +0 -20
- package/dist/wirings/channel/eventhub-service.d.ts +0 -18
- package/dist/wirings/channel/index.d.ts +4 -1
- package/dist/wirings/channel/index.js +2 -0
- package/dist/wirings/channel/local/local-channel-runner.js +3 -1
- package/dist/wirings/channel/local/local-eventhub-service.d.ts +0 -33
- package/dist/wirings/channel/local/local-eventhub-service.js +2 -36
- package/dist/wirings/channel/log-channels.d.ts +0 -4
- package/dist/wirings/channel/log-channels.js +0 -4
- package/dist/wirings/channel/pikku-abstract-channel-handler.js +6 -0
- package/dist/wirings/channel/serverless/serverless-channel-runner.js +2 -5
- package/dist/wirings/cli/channel/cli-approval.d.ts +41 -0
- package/dist/wirings/cli/channel/cli-approval.js +81 -0
- package/dist/wirings/cli/channel/cli-channel-runner.d.ts +0 -4
- package/dist/wirings/cli/channel/cli-channel-runner.js +3 -25
- package/dist/wirings/cli/channel/cli-raw-channel-runner.d.ts +47 -9
- package/dist/wirings/cli/channel/cli-raw-channel-runner.js +24 -16
- package/dist/wirings/cli/channel/cli-raw-client-runner.d.ts +20 -0
- package/dist/wirings/cli/channel/cli-raw-client-runner.js +121 -0
- package/dist/wirings/cli/channel/index.d.ts +4 -0
- package/dist/wirings/cli/channel/index.js +2 -0
- package/dist/wirings/cli/cli-runner.d.ts +20 -20
- package/dist/wirings/cli/cli-runner.js +28 -89
- package/dist/wirings/cli/cli.types.d.ts +14 -3
- package/dist/wirings/cli/command-parser.d.ts +1 -10
- package/dist/wirings/cli/command-parser.js +10 -87
- package/dist/wirings/cli/define-cli-commands.d.ts +1 -17
- package/dist/wirings/cli/define-cli-commands.js +1 -17
- package/dist/wirings/credential/credential.types.d.ts +0 -12
- package/dist/wirings/credential/define-credential.d.ts +48 -0
- package/dist/wirings/credential/define-credential.js +47 -0
- package/dist/wirings/credential/index.d.ts +1 -1
- package/dist/wirings/credential/index.js +1 -1
- package/dist/wirings/credential/validate-credential-definitions.d.ts +2 -4
- package/dist/wirings/gateway/gateway-runner.d.ts +1 -20
- package/dist/wirings/gateway/gateway-runner.js +8 -105
- package/dist/wirings/gateway/gateway.types.d.ts +7 -80
- package/dist/wirings/http/http-routes.d.ts +0 -63
- package/dist/wirings/http/http-routes.js +0 -63
- package/dist/wirings/http/http-runner.d.ts +0 -99
- package/dist/wirings/http/http-runner.js +9 -165
- package/dist/wirings/http/http.types.d.ts +14 -55
- package/dist/wirings/http/log-http-routes.d.ts +0 -4
- package/dist/wirings/http/log-http-routes.js +0 -4
- package/dist/wirings/http/pikku-fetch-http-request.d.ts +0 -40
- package/dist/wirings/http/pikku-fetch-http-request.js +0 -58
- package/dist/wirings/http/pikku-fetch-http-response.js +0 -3
- package/dist/wirings/http/routers/path-to-regex.js +2 -13
- package/dist/wirings/http/web-request.d.ts +0 -8
- package/dist/wirings/http/web-request.js +25 -17
- package/dist/wirings/mcp/mcp-runner.d.ts +1 -4
- package/dist/wirings/mcp/mcp-runner.js +1 -14
- package/dist/wirings/mcp/mcp.types.d.ts +2 -35
- package/dist/wirings/oauth2/oauth2.types.d.ts +0 -28
- package/dist/wirings/oauth2/oauth2.types.js +0 -3
- package/dist/wirings/persona/define-personas.d.ts +28 -0
- package/dist/wirings/persona/define-personas.js +27 -0
- package/dist/wirings/persona/index.d.ts +21 -0
- package/dist/wirings/persona/index.js +17 -0
- package/dist/wirings/persona/persona-email.d.ts +37 -0
- package/dist/wirings/persona/persona-email.js +69 -0
- package/dist/wirings/persona/persona-environments.d.ts +45 -0
- package/dist/wirings/persona/persona-environments.js +81 -0
- package/dist/wirings/persona/persona-mailbox.d.ts +101 -0
- package/dist/wirings/persona/persona-mailbox.js +53 -0
- package/dist/wirings/persona/persona.types.d.ts +125 -0
- package/dist/wirings/persona/persona.types.js +1 -0
- package/dist/wirings/persona/validate-personas.d.ts +53 -0
- package/dist/wirings/persona/validate-personas.js +94 -0
- package/dist/wirings/queue/index.d.ts +3 -0
- package/dist/wirings/queue/index.js +2 -3
- package/dist/wirings/queue/queue-identity.d.ts +28 -0
- package/dist/wirings/queue/queue-identity.js +102 -0
- package/dist/wirings/queue/queue-runner.d.ts +0 -19
- package/dist/wirings/queue/queue-runner.js +9 -30
- package/dist/wirings/queue/queue.types.d.ts +18 -89
- package/dist/wirings/queue/register-queue-helper.d.ts +0 -12
- package/dist/wirings/queue/register-queue-helper.js +0 -11
- package/dist/wirings/queue/signed-queue-service.d.ts +16 -0
- package/dist/wirings/queue/signed-queue-service.js +42 -0
- package/dist/wirings/queue/validate-worker-config.d.ts +2 -23
- package/dist/wirings/queue/validate-worker-config.js +0 -14
- package/dist/wirings/role/define-system-role.d.ts +32 -0
- package/dist/wirings/role/define-system-role.js +31 -0
- package/dist/wirings/role/index.d.ts +3 -0
- package/dist/wirings/role/index.js +2 -0
- package/dist/wirings/role/role.types.d.ts +43 -0
- package/dist/wirings/role/role.types.js +1 -0
- package/dist/wirings/role/validate-role-definitions.d.ts +21 -0
- package/dist/wirings/role/validate-role-definitions.js +71 -0
- package/dist/wirings/rpc/addon-runner.d.ts +0 -19
- package/dist/wirings/rpc/addon-runner.js +0 -51
- package/dist/wirings/rpc/remote-addon-auth.d.ts +1 -12
- package/dist/wirings/rpc/remote-addon-auth.js +1 -9
- package/dist/wirings/rpc/rpc-runner.d.ts +11 -18
- package/dist/wirings/rpc/rpc-runner.js +88 -105
- package/dist/wirings/rpc/rpc-types.d.ts +7 -6
- package/dist/wirings/rpc/wire-addon.d.ts +25 -0
- package/dist/wirings/rpc/wire-addon.js +62 -0
- package/dist/wirings/rpc/wire-remote-addon.d.ts +3 -28
- package/dist/wirings/rpc/wire-remote-addon.js +0 -8
- package/dist/wirings/scheduler/log-schedulers.d.ts +0 -4
- package/dist/wirings/scheduler/log-schedulers.js +0 -4
- package/dist/wirings/scheduler/scheduler-runner.d.ts +0 -1
- package/dist/wirings/scheduler/scheduler-runner.js +0 -1
- package/dist/wirings/scheduler/scheduler.types.d.ts +1 -14
- package/dist/wirings/scope/define-scope.d.ts +32 -0
- package/dist/wirings/scope/define-scope.js +31 -0
- package/dist/wirings/scope/index.d.ts +1 -1
- package/dist/wirings/scope/index.js +1 -1
- package/dist/wirings/scope/scope.types.d.ts +7 -9
- package/dist/wirings/scope/validate-scope-definitions.d.ts +2 -21
- package/dist/wirings/scope/validate-scope-definitions.js +3 -21
- package/dist/wirings/secret/index.d.ts +1 -1
- package/dist/wirings/secret/index.js +1 -1
- package/dist/wirings/secret/secret.types.d.ts +19 -15
- package/dist/wirings/secret/secret.types.js +1 -1
- package/dist/wirings/secret/validate-secret-definitions.d.ts +2 -4
- package/dist/wirings/trigger/trigger-runner.d.ts +0 -27
- package/dist/wirings/trigger/trigger-runner.js +1 -24
- package/dist/wirings/trigger/trigger.types.d.ts +1 -82
- package/dist/wirings/trigger/trigger.types.js +0 -34
- package/dist/wirings/variable/index.d.ts +1 -1
- package/dist/wirings/variable/index.js +1 -1
- package/dist/wirings/variable/validate-variable-definitions.d.ts +2 -4
- package/dist/wirings/variable/variable.types.d.ts +1 -13
- package/dist/wirings/variable/variable.types.js +1 -1
- package/dist/wirings/virtual-user/index.d.ts +27 -0
- package/dist/wirings/virtual-user/index.js +8 -0
- package/dist/wirings/virtual-user/run-virtual-user.d.ts +92 -0
- package/dist/wirings/virtual-user/run-virtual-user.js +478 -0
- package/dist/wirings/virtual-user/virtual-user-agents.d.ts +38 -0
- package/dist/wirings/virtual-user/virtual-user-agents.js +24 -0
- package/dist/wirings/virtual-user/virtual-user-catalogue.d.ts +92 -0
- package/dist/wirings/virtual-user/virtual-user-catalogue.js +134 -0
- package/dist/wirings/virtual-user/virtual-user-derive.d.ts +26 -0
- package/dist/wirings/virtual-user/virtual-user-derive.js +137 -0
- package/dist/wirings/virtual-user/virtual-user-dispositions.d.ts +79 -0
- package/dist/wirings/virtual-user/virtual-user-dispositions.js +128 -0
- package/dist/wirings/virtual-user/virtual-user-intents.d.ts +78 -0
- package/dist/wirings/virtual-user/virtual-user-intents.js +142 -0
- package/dist/wirings/virtual-user/virtual-user-rng.d.ts +24 -0
- package/dist/wirings/virtual-user/virtual-user-rng.js +44 -0
- package/dist/wirings/virtual-user/virtual-user-target.d.ts +21 -0
- package/dist/wirings/virtual-user/virtual-user-target.js +34 -0
- package/dist/wirings/virtual-user/virtual-user.types.d.ts +199 -0
- package/dist/wirings/virtual-user/virtual-user.types.js +8 -0
- package/dist/wirings/workflow/dsl/workflow-dsl.types.d.ts +5 -5
- package/dist/wirings/workflow/dsl/workflow-runner.d.ts +0 -4
- package/dist/wirings/workflow/dsl/workflow-runner.js +0 -4
- package/dist/wirings/workflow/feature.d.ts +0 -19
- package/dist/wirings/workflow/feature.js +0 -19
- package/dist/wirings/workflow/graph/graph-node.d.ts +0 -98
- package/dist/wirings/workflow/graph/graph-node.js +0 -34
- package/dist/wirings/workflow/graph/graph-runner.js +6 -41
- package/dist/wirings/workflow/graph/wire-workflow-graph.d.ts +0 -4
- package/dist/wirings/workflow/graph/workflow-graph.types.d.ts +0 -58
- package/dist/wirings/workflow/graph/workflow-graph.types.js +0 -6
- package/dist/wirings/workflow/index.d.ts +5 -7
- package/dist/wirings/workflow/index.js +3 -17
- package/dist/wirings/workflow/pikku-scenario-service.d.ts +87 -5
- package/dist/wirings/workflow/pikku-scenario-service.js +204 -42
- package/dist/wirings/workflow/pikku-workflow-service.d.ts +7 -459
- package/dist/wirings/workflow/pikku-workflow-service.js +58 -551
- package/dist/wirings/workflow/run-timeline.d.ts +0 -47
- package/dist/wirings/workflow/run-timeline.js +0 -22
- package/dist/wirings/workflow/scenario-cookie-jar.d.ts +0 -23
- package/dist/wirings/workflow/scenario-cookie-jar.js +0 -16
- package/dist/wirings/workflow/scenario-poll.d.ts +0 -15
- package/dist/wirings/workflow/scenario-poll.js +0 -12
- package/dist/wirings/workflow/scenario-prose.d.ts +0 -28
- package/dist/wirings/workflow/scenario-prose.js +0 -18
- package/dist/wirings/workflow/scenario-step-guards.d.ts +0 -13
- package/dist/wirings/workflow/scenario-step-guards.js +1 -14
- package/dist/wirings/workflow/scenario-step.types.d.ts +72 -6
- package/dist/wirings/workflow/scenario-step.types.js +5 -1
- package/dist/wirings/workflow/scenario-surface.d.ts +16 -0
- package/dist/wirings/workflow/scenario-surface.js +56 -0
- package/dist/wirings/workflow/workflow-invocation-id.d.ts +0 -18
- package/dist/wirings/workflow/workflow-invocation-id.js +2 -22
- package/dist/wirings/workflow/workflow-queue-workers.d.ts +0 -20
- package/dist/wirings/workflow/workflow-queue-workers.js +0 -19
- package/dist/wirings/workflow/workflow.types.d.ts +0 -197
- package/knowledge/decisions/index.md +19 -0
- package/knowledge/decisions/internals/a-secret-that-fails-to-decrypt-fails-the-whole-read.md +49 -0
- package/knowledge/decisions/internals/actor-flow-conversations-seed-a-hidden-kickoff-message.md +23 -0
- package/knowledge/decisions/internals/actor-flow-drives-the-target-through-a-transport-seam.md +24 -0
- package/knowledge/decisions/internals/actor-flow-verdicts-are-llm-self-evaluations.md +25 -0
- package/knowledge/decisions/internals/addon-package-roots-resolve-by-walking-node-module-search-paths.md +26 -0
- package/knowledge/decisions/internals/addon-singleton-services-are-cached-per-namespace-not-per-package.md +33 -0
- package/knowledge/decisions/internals/addon-workflow-names-are-prefixed-with-the-consumer-namespace.md +28 -0
- package/knowledge/decisions/internals/ai-agent-agui-bridge-obeys-the-client-ordering-contract.md +29 -0
- package/knowledge/decisions/internals/ai-agent-audio-chunks-carry-the-format-the-provider-returned.md +20 -0
- package/knowledge/decisions/internals/ai-agent-credential-suspensions-hide-the-tool-result.md +26 -0
- package/knowledge/decisions/internals/ai-agent-delegate-and-supervise-hide-different-text.md +26 -0
- package/knowledge/decisions/internals/ai-agent-llm-tool-arguments-have-nulls-stripped.md +23 -0
- package/knowledge/decisions/internals/ai-agent-model-config-stays-a-single-resolution-seam.md +25 -0
- package/knowledge/decisions/internals/ai-agent-onerror-hooks-cannot-change-the-failure.md +22 -0
- package/knowledge/decisions/internals/ai-agent-runner-methods-must-keep-their-receiver.md +22 -0
- package/knowledge/decisions/internals/ai-agent-stream-persistence-is-best-effort.md +27 -0
- package/knowledge/decisions/internals/ai-agent-sub-agents-inherit-the-parent-context-block.md +26 -0
- package/knowledge/decisions/internals/ai-agent-tool-execute-failures-are-logged-unconditionally.md +25 -0
- package/knowledge/decisions/internals/ai-agent-voice-input-transcribes-audio-parts-in-place.md +22 -0
- package/knowledge/decisions/internals/ai-agent-working-memory-is-persisted-only-when-valid.md +25 -0
- package/knowledge/decisions/internals/channel-message-handlers-accept-three-config-shapes.md +30 -0
- package/knowledge/decisions/internals/channel-middleware-caches-only-statically-resolved-middleware.md +31 -0
- package/knowledge/decisions/internals/channel-state-is-per-socket-session-state-is-per-user.md +29 -0
- package/knowledge/decisions/internals/channel-user-id-is-persisted-after-onconnect-middleware-runs.md +28 -0
- package/knowledge/decisions/internals/cli-option-names-are-camelcase-in-state-and-kebab-on-the-command-line.md +27 -0
- package/knowledge/decisions/internals/cli-parse-errors-are-routed-by-message-prefix.md +28 -0
- package/knowledge/decisions/internals/cli-stdout-is-reserved-for-machine-readable-output.md +34 -0
- package/knowledge/decisions/internals/cli-unknown-long-options-warn-instead-of-failing.md +29 -0
- package/knowledge/decisions/internals/core-data-classification-brand-is-an-optional-property.md +34 -0
- package/knowledge/decisions/internals/core-function-runner-restores-the-wire-fields-it-overwrites.md +44 -0
- package/knowledge/decisions/internals/core-hot-reload-merges-generated-meta-never-replaces-it.md +39 -0
- package/knowledge/decisions/internals/core-hot-reload-owns-its-module-registry.md +42 -0
- package/knowledge/decisions/internals/core-middleware-order-is-scope-then-priority.md +39 -0
- package/knowledge/decisions/internals/core-schema-defaults-apply-on-every-transport.md +43 -0
- package/knowledge/decisions/internals/core-scopes-are-an-and-gate-separate-from-permissions.md +38 -0
- package/knowledge/decisions/internals/core-state-is-a-global-map-written-only-at-registration-time.md +44 -0
- package/knowledge/decisions/internals/email-meta-is-read-uncached-because-codegen-rewrites-it-mid-session.md +27 -0
- package/knowledge/decisions/internals/gateway-adapters-resolve-lazily-and-are-promise-cached.md +32 -0
- package/knowledge/decisions/internals/gateway-webhook-challenges-echo-bytes-not-json.md +28 -0
- package/knowledge/decisions/internals/gateway-wiring-is-a-meta-wiring-over-http-and-channels.md +31 -0
- package/knowledge/decisions/internals/generated-src-paths-in-pikku-meta-are-absolute.md +26 -0
- package/knowledge/decisions/internals/http-request-bodies-are-read-once-and-shared.md +32 -0
- package/knowledge/decisions/internals/http-route-groups-cascade-config-in-a-fixed-order.md +28 -0
- package/knowledge/decisions/internals/http-router-matches-normalized-paths-but-returns-registered-ones.md +30 -0
- package/knowledge/decisions/internals/http-runner-logs-through-a-trace-scoped-logger-functions-do-not.md +26 -0
- package/knowledge/decisions/internals/http-set-cookie-headers-are-appended-never-joined.md +28 -0
- package/knowledge/decisions/internals/http-sse-streams-flush-headers-only-after-middleware.md +32 -0
- package/knowledge/decisions/internals/http-wiring-without-metadata-is-skipped-not-fatal.md +26 -0
- package/knowledge/decisions/internals/in-a-scenario-a-4xx-is-data-not-an-exception.md +25 -0
- package/knowledge/decisions/internals/in-memory-workflow-history-aliases-the-live-step-object.md +26 -0
- package/knowledge/decisions/internals/index.md +113 -0
- package/knowledge/decisions/internals/istanbul-statement-counts-attach-to-the-start-line-only.md +25 -0
- package/knowledge/decisions/internals/local-trigger-and-gateway-services-assume-a-single-process.md +26 -0
- package/knowledge/decisions/internals/node-only-builtins-are-imported-dynamically.md +24 -0
- package/knowledge/decisions/internals/queue-group-concurrency-keeps-one-shared-queue-fair.md +28 -0
- package/knowledge/decisions/internals/queue-jobs-always-carry-an-explicit-attempts-count.md +27 -0
- package/knowledge/decisions/internals/remote-addons-dispatch-over-http-instead-of-local-meta.md +31 -0
- package/knowledge/decisions/internals/rpc-names-resolve-through-package-scope-before-root.md +32 -0
- package/knowledge/decisions/internals/scenario-agent-calls-sign-in-on-401-only.md +27 -0
- package/knowledge/decisions/internals/scenario-meta-lives-apart-from-app-meta-but-merges-when-read-off-disk.md +26 -0
- package/knowledge/decisions/internals/scenario-steps-return-drained-response-records.md +27 -0
- package/knowledge/decisions/internals/scope-roots-may-be-co-declared-by-an-addon-and-its-host-app.md +30 -0
- package/knowledge/decisions/internals/serverless-channel-disconnect-must-tolerate-a-missing-channel.md +28 -0
- package/knowledge/decisions/internals/the-dev-queue-copies-prod-timing-and-serialization-semantics.md +30 -0
- package/knowledge/decisions/internals/the-embedding-model-is-pinned-per-service-and-doc-query-embedding-is-split.md +29 -0
- package/knowledge/decisions/internals/the-in-memory-workflow-service-is-inline-only-and-single-process.md +27 -0
- package/knowledge/decisions/internals/the-kek-salt-is-scoped-to-the-key-version.md +40 -0
- package/knowledge/decisions/internals/the-schema-service-is-never-stubbed.md +26 -0
- package/knowledge/decisions/internals/trigger-declaration-is-split-from-trigger-source.md +33 -0
- package/knowledge/decisions/internals/typed-secret-service-caches-for-the-process-lifetime.md +26 -0
- package/knowledge/decisions/internals/webhook-delivery-history-records-every-attempt-best-effort.md +26 -0
- package/knowledge/decisions/internals/webhook-service-collaborators-are-constructor-args-not-locator-lookups.md +25 -0
- package/knowledge/decisions/internals/whether-a-run-is-inline-is-read-from-the-run-record.md +58 -0
- package/knowledge/decisions/internals/workflow-approval-expiry-is-decided-from-a-recorded-deadline.md +34 -0
- package/knowledge/decisions/internals/workflow-core-never-imports-a-browser-driver.md +42 -0
- package/knowledge/decisions/internals/workflow-dsl-meta-separates-runtime-expressions-from-literals.md +38 -0
- package/knowledge/decisions/internals/workflow-features-resolve-scenarios-by-object-identity.md +29 -0
- package/knowledge/decisions/internals/workflow-graph-inline-and-queued-runs-share-one-planner.md +42 -0
- package/knowledge/decisions/internals/workflow-graph-node-notes-are-excluded-from-the-graph-hash.md +25 -0
- package/knowledge/decisions/internals/workflow-inline-runs-report-their-run-id-before-they-can-fail.md +29 -0
- package/knowledge/decisions/internals/workflow-invocation-id-is-the-dedupe-key-not-step-id.md +43 -0
- package/knowledge/decisions/internals/workflow-queued-step-dispatch-requires-an-explicit-opt-in.md +29 -0
- package/knowledge/decisions/internals/workflow-queues-are-per-workflow-by-default.md +42 -0
- package/knowledge/decisions/internals/workflow-repeated-step-names-get-an-ordinal-suffix.md +33 -0
- package/knowledge/decisions/internals/workflow-replay-reads-its-steps-once-and-caches-only-the-immutable-half.md +32 -0
- package/knowledge/decisions/internals/workflow-retries-are-owned-by-the-workflow-not-the-queue.md +31 -0
- package/knowledge/decisions/internals/workflow-run-capabilities-are-extensions-not-subclasses.md +39 -0
- package/knowledge/decisions/internals/workflow-run-mirror-is-never-a-source-of-truth.md +29 -0
- package/knowledge/decisions/internals/workflow-run-polling-backs-off-to-the-callers-ceiling.md +33 -0
- package/knowledge/decisions/internals/workflow-run-timeline-is-a-pure-fold-over-durable-history.md +37 -0
- package/knowledge/decisions/internals/workflow-scenario-assertions-never-retry-and-record-one-step.md +50 -0
- package/knowledge/decisions/internals/workflow-scenario-hooks-are-a-scenario-only-affordance.md +43 -0
- package/knowledge/decisions/internals/workflow-scenario-prose-is-rendered-from-typed-calls-not-parsed-from-english.md +32 -0
- package/knowledge/decisions/internals/workflow-scenario-quarantine-reason-lives-in-code.md +18 -0
- package/knowledge/decisions/internals/workflow-scenario-step-targets-are-string-literals-for-the-inspector.md +34 -0
- package/knowledge/decisions/internals/workflow-step-compensation-runs-as-its-own-durable-step.md +26 -0
- package/knowledge/decisions/internals/workflow-step-dispatch-failure-is-transient-not-a-run-failure.md +33 -0
- package/knowledge/decisions/internals/workflow-step-lock-is-held-only-to-claim-the-step.md +27 -0
- package/knowledge/decisions/internals/workflow-step-rpc-name-is-provenance-only.md +34 -0
- package/knowledge/decisions/internals/workflow-suspend-and-approval-reasons-are-durable-step-identities.md +38 -0
- package/knowledge/decisions/internals/workflow-suspended-runs-keep-their-in-process-context.md +30 -0
- package/knowledge/decisions/security/a-dropped-audit-write-is-always-logged.md +26 -0
- package/knowledge/decisions/security/actor-flow-missing-approval-decisions-default-to-denied.md +22 -0
- package/knowledge/decisions/security/actor-sign-in-is-proven-by-set-cookie-not-a-non-empty-jar.md +27 -0
- package/knowledge/decisions/security/actor-sign-in-only-works-for-actor-flagged-users.md +27 -0
- package/knowledge/decisions/security/addon-auth-and-tags-only-tighten.md +43 -0
- package/knowledge/decisions/security/addon-config-gates-apply-only-at-the-namespaced-rpc-boundary.md +52 -0
- package/knowledge/decisions/security/addon-scopes-are-resolved-where-the-function-runs.md +46 -0
- package/knowledge/decisions/security/ai-agent-approval-forwarding-requires-a-symbol-brand.md +27 -0
- package/knowledge/decisions/security/ai-agent-credential-requests-are-symbol-branded.md +37 -0
- package/knowledge/decisions/security/ai-agent-gate-requires-a-session-only-when-auth-is-true.md +31 -0
- package/knowledge/decisions/security/ai-agent-ownership-failures-never-echo-the-resource.md +23 -0
- package/knowledge/decisions/security/ai-agent-resume-re-runs-the-authorization-gate.md +22 -0
- package/knowledge/decisions/security/ai-agent-sessionless-deployments-have-no-thread-ownership.md +39 -0
- package/knowledge/decisions/security/ai-agent-thread-ownership-composes-the-session-principal.md +30 -0
- package/knowledge/decisions/security/ai-agent-tool-filtering-reads-the-live-function-config.md +24 -0
- package/knowledge/decisions/security/an-empty-owners-constraint-matches-nothing.md +30 -0
- package/knowledge/decisions/security/an-exposed-ungated-function-is-a-codegen-warning.md +49 -0
- package/knowledge/decisions/security/console-addon-privileged-functions-gate-themselves.md +76 -0
- package/knowledge/decisions/security/core-safe-fetch-blocks-ssrf-by-host-literal-not-dns.md +39 -0
- package/knowledge/decisions/security/core-secrets-use-a-per-secret-dek-wrapped-by-a-kek.md +37 -0
- package/knowledge/decisions/security/gateway-handlers-run-through-the-function-runner-gate.md +31 -0
- package/knowledge/decisions/security/gateway-middleware-sessions-must-be-bridged-onto-the-wire.md +30 -0
- package/knowledge/decisions/security/global-permissions-and-function-permissions-are-independent-gates.md +40 -0
- package/knowledge/decisions/security/http-error-detail-is-withheld-from-clients-in-production.md +33 -0
- package/knowledge/decisions/security/http-request-bodies-are-bounded-before-they-are-buffered.md +46 -0
- package/knowledge/decisions/security/index.md +55 -0
- package/knowledge/decisions/security/mcp-internal-error-details-are-double-gated-on-production.md +27 -0
- package/knowledge/decisions/security/passphrases-are-stretched-key-material-is-expanded.md +40 -0
- package/knowledge/decisions/security/permission-auth-filtering-requires-live-permission-functions.md +31 -0
- package/knowledge/decisions/security/pikku-carries-actor-scopes-as-data-and-the-app-grants-them.md +26 -0
- package/knowledge/decisions/security/queue-job-identities-are-signed-at-enqueue.md +69 -0
- package/knowledge/decisions/security/queue-jobs-carry-the-producers-pikku-user-id.md +39 -0
- package/knowledge/decisions/security/remote-addon-tokens-are-client-credentials-not-mesh-trust.md +34 -0
- package/knowledge/decisions/security/scaffold-features-are-authenticated-unless-opted-out.md +49 -0
- package/knowledge/decisions/security/scenario-step-functions-are-never-externally-invocable.md +30 -0
- package/knowledge/decisions/security/scope-resolution-happens-at-the-session-boundary-and-sync-never-deletes.md +28 -0
- package/knowledge/decisions/security/self-authentication-is-declared-not-detected.md +34 -0
- package/knowledge/decisions/security/signed-content-urls-bind-the-request-path.md +37 -0
- package/knowledge/decisions/security/webhook-bodies-are-signed-before-they-are-enqueued.md +25 -0
- package/knowledge/decisions/security/workflow-actor-steps-always-use-the-real-transport.md +34 -0
- package/knowledge/decisions/security/workflow-approval-payloads-are-validated-on-replay-inside-the-workflow.md +40 -0
- package/knowledge/decisions/security/workflow-queued-steps-rehydrate-their-session-from-the-run-wire.md +32 -0
- package/knowledge/decisions/security/workflow-scenario-sessions-are-isolated-per-actor-and-per-scenario.md +32 -0
- package/knowledge/decisions/security/workflow-scenario-steps-are-never-network-invocable.md +31 -0
- package/knowledge/index.md +24 -0
- package/knowledge/questions/index.md +15 -0
- package/package.json +4 -1
- package/run-tests.sh +0 -0
- package/src/crypto-utils.test.ts +460 -19
- package/src/crypto-utils.ts +283 -55
- package/src/data-classification.ts +1 -7
- package/src/dev/hot-reload.test.ts +0 -4
- package/src/dev/hot-reload.ts +11 -30
- package/src/dev/module-runner.ts +7 -32
- package/src/dev/reload-meta.ts +9 -29
- package/src/errors/error-handler.ts +20 -35
- package/src/errors/error.test.ts +30 -1
- package/src/errors/errors.ts +73 -157
- package/src/function/abort-scope.test.ts +97 -0
- package/src/function/abort-scope.ts +80 -0
- package/src/function/function-runner.test.ts +0 -7
- package/src/function/function-runner.ts +62 -32
- package/src/function/functions.types.ts +56 -136
- package/src/function/list.types.test.ts +3 -25
- package/src/function/list.types.ts +12 -62
- package/src/handle-error.ts +0 -18
- package/src/index.ts +60 -1
- package/src/middleware/auth-apikey.test.ts +0 -1
- package/src/middleware/auth-apikey.ts +0 -17
- package/src/middleware/auth-bearer.test.ts +0 -3
- package/src/middleware/auth-bearer.ts +3 -40
- package/src/middleware/auth-cookie.test.ts +0 -6
- package/src/middleware/auth-cookie.ts +2 -26
- package/src/middleware/cors.test.ts +34 -0
- package/src/middleware/cors.ts +12 -33
- package/src/middleware/remote-auth.test.ts +24 -9
- package/src/middleware/remote-auth.ts +10 -2
- package/src/middleware/telemetry.ts +2 -31
- package/src/middleware-runner.test.ts +0 -2
- package/src/middleware-runner.ts +5 -74
- package/src/permissions.test.ts +30 -0
- package/src/permissions.ts +24 -74
- package/src/pikku-request.ts +0 -6
- package/src/pikku-state.ts +2 -30
- package/src/production-barrels-stay-lean.test.ts +110 -0
- package/src/remote.test.ts +172 -0
- package/src/remote.ts +17 -7
- package/src/schema.ts +5 -39
- package/src/scopes.ts +7 -48
- package/src/services/ai-agent-runner-service.ts +20 -0
- package/src/services/ai-embedding-service.ts +3 -25
- package/src/services/audit-service.ts +1 -2
- package/src/services/content-service.ts +1 -46
- package/src/services/credential-service.ts +3 -40
- package/src/services/credential-wire-service.test.ts +0 -2
- package/src/services/deployment-service.ts +3 -9
- package/src/services/gateway-service.ts +0 -15
- package/src/services/{http-scenario-actors-converse.test.ts → http-personas-converse.test.ts} +38 -9
- package/src/services/{http-scenario-actors.test.ts → http-personas.test.ts} +39 -22
- package/src/services/{http-scenario-actors.ts → http-personas.ts} +85 -45
- package/src/services/in-memory-queue-service.ts +1 -15
- package/src/services/in-memory-trigger-service.ts +1 -18
- package/src/services/in-memory-workflow-service.test.ts +0 -13
- package/src/services/in-memory-workflow-service.ts +4 -38
- package/src/services/index.ts +20 -15
- package/src/services/istanbul-coverage-service.ts +2 -8
- package/src/services/jwt-service.ts +1 -16
- package/src/services/local-content.test.ts +159 -27
- package/src/services/local-content.ts +55 -23
- package/src/services/local-gateway-service.ts +2 -17
- package/src/services/local-secrets.ts +0 -4
- package/src/services/logger-console.test.ts +0 -1
- package/src/services/logger-console.ts +3 -7
- package/src/services/logger.ts +2 -37
- package/src/services/meta-service.test.ts +1 -5
- package/src/services/meta-service.ts +41 -61
- package/src/services/{scenario-actors-service.ts → personas-service.ts} +48 -43
- package/src/services/pikku-user-id.ts +0 -4
- package/src/services/queue-webhook-service.ts +9 -41
- package/src/services/scheduler-service.ts +1 -50
- package/src/services/schema-service.ts +1 -24
- package/src/services/scope-service.ts +50 -34
- package/src/services/scoped-secret-service.ts +0 -4
- package/src/services/secret-host-binding.test.ts +138 -0
- package/src/services/secret-host-binding.ts +51 -0
- package/src/services/secret-service.ts +5 -33
- package/src/services/secretless.test.ts +54 -0
- package/src/services/secretless.ts +29 -0
- package/src/services/stub-tracker.ts +8 -18
- package/src/services/system-role-guard.test.ts +93 -0
- package/src/services/system-role-guard.ts +71 -0
- package/src/services/trigger-service.ts +0 -12
- package/src/services/typed-secret-service.ts +1 -7
- package/src/services/v8-coverage-service.ts +3 -6
- package/src/services/variables-service.ts +1 -8
- package/src/services/webhook-service.ts +19 -63
- package/src/services/workflow-service.ts +3 -20
- package/src/testing/service-tests.ts +0 -26
- package/src/time-utils.ts +1 -19
- package/src/types/core.types.ts +116 -229
- package/src/types/state.types.ts +7 -9
- package/src/utils/hmac.ts +4 -10
- package/src/utils/safe-fetch.ts +13 -54
- package/src/utils.test.ts +11 -2
- package/src/utils.ts +6 -15
- package/src/wirings/actor-flow/actor-flow.types.ts +1 -34
- package/src/wirings/actor-flow/index.ts +0 -9
- package/src/wirings/actor-flow/run-conversation.test.ts +11 -6
- package/src/wirings/actor-flow/run-conversation.ts +19 -12
- package/src/wirings/ai-agent/ai-agent-agui.test.ts +75 -10
- package/src/wirings/ai-agent/ai-agent-agui.ts +36 -17
- package/src/wirings/ai-agent/ai-agent-helpers.ts +20 -0
- package/src/wirings/ai-agent/ai-agent-interrupt.test.ts +842 -0
- package/src/wirings/ai-agent/ai-agent-interrupt.ts +399 -0
- package/src/wirings/ai-agent/ai-agent-memory.ts +0 -2
- package/src/wirings/ai-agent/ai-agent-model-config.ts +1 -9
- package/src/wirings/ai-agent/ai-agent-prepare.test.ts +202 -31
- package/src/wirings/ai-agent/ai-agent-prepare.ts +82 -138
- package/src/wirings/ai-agent/ai-agent-registry.test.ts +191 -6
- package/src/wirings/ai-agent/ai-agent-registry.ts +18 -1
- package/src/wirings/ai-agent/ai-agent-resume-authorization.test.ts +0 -2
- package/src/wirings/ai-agent/ai-agent-runner.test.ts +11 -9
- package/src/wirings/ai-agent/ai-agent-runner.ts +67 -33
- package/src/wirings/ai-agent/ai-agent-stream.test.ts +205 -103
- package/src/wirings/ai-agent/ai-agent-stream.ts +192 -75
- package/src/wirings/ai-agent/ai-agent-thread-ownership.test.ts +301 -0
- package/src/wirings/ai-agent/ai-agent.types.ts +85 -4
- package/src/wirings/ai-agent/index.ts +35 -3
- package/src/wirings/ai-agent/voice-input.test.ts +72 -7
- package/src/wirings/ai-agent/voice-input.ts +48 -3
- package/src/wirings/ai-agent/voice-output.test.ts +422 -0
- package/src/wirings/ai-agent/voice-output.ts +216 -56
- package/src/wirings/channel/channel-common.ts +15 -20
- package/src/wirings/channel/channel-handler.test.ts +50 -0
- package/src/wirings/channel/channel-handler.ts +32 -11
- package/src/wirings/channel/channel-host-rpc.test.ts +150 -0
- package/src/wirings/channel/channel-host-rpc.ts +69 -0
- package/src/wirings/channel/channel-middleware-runner.test.ts +0 -1
- package/src/wirings/channel/channel-middleware-runner.ts +0 -12
- package/src/wirings/channel/channel-rpc-registry.ts +116 -0
- package/src/wirings/channel/channel-rpc-responder.ts +117 -0
- package/src/wirings/channel/channel-rpc-service.ts +146 -0
- package/src/wirings/channel/channel-rpc-validators.ts +65 -0
- package/src/wirings/channel/channel-rpc.test.ts +820 -0
- package/src/wirings/channel/channel-rpc.ts +5 -0
- package/src/wirings/channel/channel-rpc.types.ts +150 -0
- package/src/wirings/channel/channel-runner.ts +0 -14
- package/src/wirings/channel/channel-store.ts +0 -10
- package/src/wirings/channel/channel.types.ts +19 -8
- package/src/wirings/channel/define-channel-routes.ts +0 -20
- package/src/wirings/channel/eventhub-service.ts +0 -18
- package/src/wirings/channel/index.ts +35 -0
- package/src/wirings/channel/local/local-channel-handler.ts +3 -1
- package/src/wirings/channel/local/local-channel-runner.test.ts +0 -10
- package/src/wirings/channel/local/local-channel-runner.ts +3 -1
- package/src/wirings/channel/local/local-eventhub-service.test.ts +0 -13
- package/src/wirings/channel/local/local-eventhub-service.ts +2 -37
- package/src/wirings/channel/log-channels.ts +0 -4
- package/src/wirings/channel/pikku-abstract-channel-handler.test.ts +83 -2
- package/src/wirings/channel/pikku-abstract-channel-handler.ts +7 -0
- package/src/wirings/channel/serverless/serverless-channel-runner.ts +2 -5
- package/src/wirings/cli/channel/cli-approval.test.ts +177 -0
- package/src/wirings/cli/channel/cli-approval.ts +135 -0
- package/src/wirings/cli/channel/cli-channel-runner.ts +4 -26
- package/src/wirings/cli/channel/cli-raw-channel-runner.test.ts +169 -0
- package/src/wirings/cli/channel/cli-raw-channel-runner.ts +59 -16
- package/src/wirings/cli/channel/cli-raw-client-runner.test.ts +480 -0
- package/src/wirings/cli/channel/cli-raw-client-runner.ts +155 -0
- package/src/wirings/cli/channel/index.ts +9 -0
- package/src/wirings/cli/cli-runner.test.ts +0 -1
- package/src/wirings/cli/cli-runner.ts +46 -88
- package/src/wirings/cli/cli.types.ts +14 -3
- package/src/wirings/cli/command-parser.test.ts +0 -4
- package/src/wirings/cli/command-parser.ts +11 -91
- package/src/wirings/cli/define-cli-commands.ts +1 -17
- package/src/wirings/credential/credential.types.ts +0 -12
- package/src/wirings/credential/{wire-credential.ts → define-credential.ts} +7 -7
- package/src/wirings/credential/index.ts +1 -1
- package/src/wirings/credential/validate-credential-definitions.ts +2 -4
- package/src/wirings/gateway/gateway-runner.test.ts +1 -21
- package/src/wirings/gateway/gateway-runner.ts +8 -110
- package/src/wirings/gateway/gateway.types.ts +7 -80
- package/src/wirings/http/http-routes.test.ts +0 -3
- package/src/wirings/http/http-routes.ts +0 -86
- package/src/wirings/http/http-runner.test.ts +0 -1
- package/src/wirings/http/http-runner.ts +8 -167
- package/src/wirings/http/http.types.ts +15 -62
- package/src/wirings/http/log-http-routes.ts +0 -4
- package/src/wirings/http/pikku-fetch-http-request.test.ts +2 -10
- package/src/wirings/http/pikku-fetch-http-request.ts +0 -58
- package/src/wirings/http/pikku-fetch-http-response.test.ts +1 -1
- package/src/wirings/http/pikku-fetch-http-response.ts +0 -3
- package/src/wirings/http/routers/path-to-regex.test.ts +4 -17
- package/src/wirings/http/routers/path-to-regex.ts +2 -13
- package/src/wirings/http/web-request.test.ts +33 -2
- package/src/wirings/http/web-request.ts +30 -17
- package/src/wirings/mcp/mcp-endpoint-registry.test.ts +0 -1
- package/src/wirings/mcp/mcp-runner.ts +2 -17
- package/src/wirings/mcp/mcp.types.ts +7 -42
- package/src/wirings/oauth2/oauth2.types.ts +0 -30
- package/src/wirings/persona/define-personas.ts +29 -0
- package/src/wirings/persona/index.ts +62 -0
- package/src/wirings/persona/persona-email.ts +87 -0
- package/src/wirings/persona/persona-environments.test.ts +183 -0
- package/src/wirings/persona/persona-environments.ts +138 -0
- package/src/wirings/persona/persona-mailbox.ts +156 -0
- package/src/wirings/persona/persona.test.ts +220 -0
- package/src/wirings/persona/persona.types.ts +131 -0
- package/src/wirings/persona/validate-personas.ts +133 -0
- package/src/wirings/queue/index.ts +13 -3
- package/src/wirings/queue/queue-identity.test.ts +453 -0
- package/src/wirings/queue/queue-identity.ts +173 -0
- package/src/wirings/queue/queue-runner.ts +12 -31
- package/src/wirings/queue/queue.types.ts +19 -89
- package/src/wirings/queue/register-queue-helper.ts +0 -14
- package/src/wirings/queue/signed-queue-service.ts +59 -0
- package/src/wirings/queue/validate-worker-config.ts +2 -28
- package/src/wirings/role/define-system-role.ts +33 -0
- package/src/wirings/role/index.ts +13 -0
- package/src/wirings/role/role.test.ts +104 -0
- package/src/wirings/role/role.types.ts +47 -0
- package/src/wirings/role/validate-role-definitions.ts +93 -0
- package/src/wirings/rpc/addon-auth-tags.test.ts +223 -0
- package/src/wirings/rpc/addon-runner.ts +0 -56
- package/src/wirings/rpc/addon-scopes.test.ts +225 -0
- package/src/wirings/rpc/remote-addon-auth.ts +1 -13
- package/src/wirings/rpc/rpc-runner.test.ts +186 -2
- package/src/wirings/rpc/rpc-runner.ts +145 -127
- package/src/wirings/rpc/rpc-types.ts +11 -6
- package/src/wirings/rpc/wire-addon.test.ts +43 -1
- package/src/wirings/rpc/wire-addon.ts +99 -0
- package/src/wirings/rpc/wire-remote-addon.ts +9 -29
- package/src/wirings/scheduler/log-schedulers.ts +0 -4
- package/src/wirings/scheduler/scheduler-runner.test.ts +1 -8
- package/src/wirings/scheduler/scheduler-runner.ts +0 -2
- package/src/wirings/scheduler/scheduler.types.ts +1 -14
- package/src/wirings/scope/{wire-scope.ts → define-scope.ts} +5 -6
- package/src/wirings/scope/index.ts +1 -1
- package/src/wirings/scope/scope.test.ts +1 -2
- package/src/wirings/scope/scope.types.ts +7 -9
- package/src/wirings/scope/validate-scope-definitions.ts +3 -21
- package/src/wirings/secret/index.ts +1 -1
- package/src/wirings/secret/secret.types.ts +19 -15
- package/src/wirings/secret/validate-secret-definitions.ts +2 -4
- package/src/wirings/trigger/trigger-runner.ts +1 -27
- package/src/wirings/trigger/trigger.types.ts +1 -82
- package/src/wirings/variable/index.ts +1 -1
- package/src/wirings/variable/validate-variable-definitions.ts +2 -4
- package/src/wirings/variable/variable.types.ts +1 -13
- package/src/wirings/virtual-user/index.ts +76 -0
- package/src/wirings/virtual-user/run-virtual-user.test.ts +765 -0
- package/src/wirings/virtual-user/run-virtual-user.ts +671 -0
- package/src/wirings/virtual-user/virtual-user-agents.test.ts +65 -0
- package/src/wirings/virtual-user/virtual-user-agents.ts +57 -0
- package/src/wirings/virtual-user/virtual-user-catalogue.test.ts +215 -0
- package/src/wirings/virtual-user/virtual-user-catalogue.ts +184 -0
- package/src/wirings/virtual-user/virtual-user-derive.test.ts +398 -0
- package/src/wirings/virtual-user/virtual-user-derive.ts +173 -0
- package/src/wirings/virtual-user/virtual-user-dispositions.test.ts +63 -0
- package/src/wirings/virtual-user/virtual-user-dispositions.ts +213 -0
- package/src/wirings/virtual-user/virtual-user-intents.test.ts +208 -0
- package/src/wirings/virtual-user/virtual-user-intents.ts +185 -0
- package/src/wirings/virtual-user/virtual-user-rng.test.ts +72 -0
- package/src/wirings/virtual-user/virtual-user-rng.ts +50 -0
- package/src/wirings/virtual-user/virtual-user-target.ts +47 -0
- package/src/wirings/virtual-user/virtual-user.types.ts +219 -0
- package/src/wirings/workflow/dsl/workflow-dsl.types.ts +5 -4
- package/src/wirings/workflow/dsl/workflow-runner.ts +0 -4
- package/src/wirings/workflow/feature.ts +0 -19
- package/src/wirings/workflow/graph/graph-node.ts +0 -136
- package/src/wirings/workflow/graph/graph-runner.test.ts +20 -19
- package/src/wirings/workflow/graph/graph-runner.ts +6 -41
- package/src/wirings/workflow/graph/wire-workflow-graph.ts +0 -4
- package/src/wirings/workflow/graph/workflow-graph.types.ts +0 -58
- package/src/wirings/workflow/index.ts +10 -44
- package/src/wirings/workflow/pikku-scenario-service.ts +235 -52
- package/src/wirings/workflow/pikku-workflow-service.test.ts +0 -39
- package/src/wirings/workflow/pikku-workflow-service.ts +77 -674
- package/src/wirings/workflow/run-timeline.test.ts +7 -19
- package/src/wirings/workflow/run-timeline.ts +0 -56
- package/src/wirings/workflow/scenario-cookie-jar.test.ts +0 -1
- package/src/wirings/workflow/scenario-cookie-jar.ts +0 -25
- package/src/wirings/workflow/scenario-expectations.test.ts +2 -7
- package/src/wirings/workflow/scenario-hooks.test.ts +2 -7
- package/src/wirings/workflow/scenario-poll.test.ts +0 -2
- package/src/wirings/workflow/scenario-poll.ts +0 -15
- package/src/wirings/workflow/scenario-prose.ts +0 -28
- package/src/wirings/workflow/scenario-service.test.ts +2 -9
- package/src/wirings/workflow/scenario-step-guards.ts +1 -14
- package/src/wirings/workflow/scenario-step.test.ts +159 -14
- package/src/wirings/workflow/scenario-step.types.ts +82 -6
- package/src/wirings/workflow/scenario-surface.test.ts +145 -0
- package/src/wirings/workflow/scenario-surface.ts +71 -0
- package/src/wirings/workflow/workflow-dispatch-durability.test.ts +14 -15
- package/src/wirings/workflow/workflow-dispatch-payload.test.ts +0 -4
- package/src/wirings/workflow/workflow-inline-authority.test.ts +169 -0
- package/src/wirings/workflow/workflow-invocation-id.test.ts +0 -2
- package/src/wirings/workflow/workflow-invocation-id.ts +2 -22
- package/src/wirings/workflow/workflow-mirror.test.ts +0 -7
- package/src/wirings/workflow/workflow-on-error.test.ts +0 -9
- package/src/wirings/workflow/workflow-queue-workers.ts +0 -21
- package/src/wirings/workflow/workflow-replay-snapshot.test.ts +8 -7
- package/src/wirings/workflow/workflow-retry-policy.test.ts +0 -5
- package/src/wirings/workflow/workflow-run-context.test.ts +5 -10
- package/src/wirings/workflow/workflow-run-polling.test.ts +0 -5
- package/src/wirings/workflow/workflow-step-ordinal.test.ts +19 -4
- package/src/wirings/workflow/workflow-step-session.test.ts +0 -7
- package/src/wirings/workflow/workflow.types.ts +0 -203
- package/tsconfig.tsbuildinfo +1 -1
- package/src/middleware/timeout.ts +0 -22
- package/src/pikku-response.ts +0 -5
- package/src/wirings/mcp/mcp-endpoint-registry.test.d.ts +0 -1
- package/src/wirings/workflow/dsl/index.ts +0 -36
- package/src/wirings/workflow/graph/index.ts +0 -15
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import type { VirtualUserDisposition } from './virtual-user.types.js'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The dials a disposition turns. Everything here changes what the engine does;
|
|
5
|
+
* `instructions` is the only field the model ever sees, and it describes
|
|
6
|
+
* behaviour the dials cannot express on their own.
|
|
7
|
+
*/
|
|
8
|
+
export interface DispositionProfile {
|
|
9
|
+
/** Appended to the persona instructions. Behaviour, never identity. */
|
|
10
|
+
instructions: string
|
|
11
|
+
/**
|
|
12
|
+
* Relative weights for the scheduler's four moves. `continue` dominating is
|
|
13
|
+
* what makes a run look like work rather than channel-surfing.
|
|
14
|
+
*/
|
|
15
|
+
moves: {
|
|
16
|
+
continue: number
|
|
17
|
+
suspend: number
|
|
18
|
+
resume: number
|
|
19
|
+
abandon: number
|
|
20
|
+
}
|
|
21
|
+
/** Sampling temperature for the model's own turns. */
|
|
22
|
+
temperature: number
|
|
23
|
+
/** Chance of re-issuing the previous call unchanged — the double-click. */
|
|
24
|
+
repeatRate: number
|
|
25
|
+
/** Chance of re-reading before acting instead of trusting memory. */
|
|
26
|
+
reReadRate: number
|
|
27
|
+
/** Start with nothing remembered, whatever the caller seeded. */
|
|
28
|
+
emptyMemory: boolean
|
|
29
|
+
/** Non-read calls are refused by the engine, not merely discouraged. */
|
|
30
|
+
readOnly: boolean
|
|
31
|
+
/**
|
|
32
|
+
* A 2xx is a finding rather than a success. The engine cannot know *whose*
|
|
33
|
+
* resource an id belongs to, so it flags the success and leaves ownership to
|
|
34
|
+
* the app's classifier — the honest split, since ownership is app knowledge.
|
|
35
|
+
*/
|
|
36
|
+
invertedOracle: boolean
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Per-user overrides for a disposition's dials.
|
|
41
|
+
*
|
|
42
|
+
* The six profiles below are starting points, not laws — the weights are a
|
|
43
|
+
* considered guess about how people behave, and your product is allowed to know
|
|
44
|
+
* better. A user that declares `disposition: 'careless'` with a higher
|
|
45
|
+
* `repeatRate` is still a careless user; it is the same behaviour tuned to a
|
|
46
|
+
* product where double-submits actually happen.
|
|
47
|
+
*
|
|
48
|
+
* `instructions` is the one field that appends rather than replaces. Everything
|
|
49
|
+
* else is a number the engine reads, so overriding it changes the run and
|
|
50
|
+
* nothing else; instructions are the model's sense of who it is, and letting a
|
|
51
|
+
* declaration replace them wholesale is how you end up with a `careless` user
|
|
52
|
+
* that is not careless — which makes the name, and every run labelled with it,
|
|
53
|
+
* a lie.
|
|
54
|
+
*/
|
|
55
|
+
export interface VirtualUserTuning {
|
|
56
|
+
/** Relative weights for the scheduler's moves. Merged over the profile's. */
|
|
57
|
+
moves?: Partial<DispositionProfile['moves']>
|
|
58
|
+
temperature?: number
|
|
59
|
+
repeatRate?: number
|
|
60
|
+
reReadRate?: number
|
|
61
|
+
emptyMemory?: boolean
|
|
62
|
+
readOnly?: boolean
|
|
63
|
+
invertedOracle?: boolean
|
|
64
|
+
/** Extra behaviour, appended to the disposition's own instructions. */
|
|
65
|
+
instructions?: string
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const REALISTIC: DispositionProfile = {
|
|
69
|
+
instructions: [
|
|
70
|
+
'Work towards your current goal the way a competent user would.',
|
|
71
|
+
'Read an endpoint schema before you use it, and prefer values that a real person would plausibly enter.',
|
|
72
|
+
].join(' '),
|
|
73
|
+
moves: { continue: 88, suspend: 6, resume: 5, abandon: 1 },
|
|
74
|
+
temperature: 0.7,
|
|
75
|
+
repeatRate: 0,
|
|
76
|
+
reReadRate: 0.1,
|
|
77
|
+
emptyMemory: false,
|
|
78
|
+
readOnly: false,
|
|
79
|
+
invertedOracle: false,
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* The profiles, keyed by disposition. `careless` is the one worth reading twice:
|
|
84
|
+
* it is not malice, it is an ordinary person being interrupted and impatient,
|
|
85
|
+
* and it is where most production bugs actually live.
|
|
86
|
+
*/
|
|
87
|
+
export const DISPOSITIONS: Readonly<
|
|
88
|
+
Record<VirtualUserDisposition, DispositionProfile>
|
|
89
|
+
> = {
|
|
90
|
+
realistic: REALISTIC,
|
|
91
|
+
|
|
92
|
+
careless: {
|
|
93
|
+
...REALISTIC,
|
|
94
|
+
instructions: [
|
|
95
|
+
'You are busy and easily distracted. You act on what you half-remember rather than checking first,',
|
|
96
|
+
'you sometimes submit the same thing twice because you are not sure it went through,',
|
|
97
|
+
'and you are comfortable entering odd-but-legal values — a very long name, an empty optional field, an emoji, a huge page size.',
|
|
98
|
+
'Stay within what the schema allows; you are careless, not malicious.',
|
|
99
|
+
].join(' '),
|
|
100
|
+
moves: { continue: 62, suspend: 18, resume: 12, abandon: 8 },
|
|
101
|
+
temperature: 1,
|
|
102
|
+
repeatRate: 0.18,
|
|
103
|
+
reReadRate: 0.03,
|
|
104
|
+
},
|
|
105
|
+
|
|
106
|
+
newcomer: {
|
|
107
|
+
...REALISTIC,
|
|
108
|
+
instructions: [
|
|
109
|
+
'This is your first time in this product. You know nothing about it and hold no ids or names in your head.',
|
|
110
|
+
'Find your way from whatever lists or lookups exist, and say plainly when there is no path forward from where you are.',
|
|
111
|
+
].join(' '),
|
|
112
|
+
moves: { continue: 80, suspend: 8, resume: 6, abandon: 6 },
|
|
113
|
+
temperature: 0.8,
|
|
114
|
+
reReadRate: 0.4,
|
|
115
|
+
emptyMemory: true,
|
|
116
|
+
},
|
|
117
|
+
|
|
118
|
+
stale: {
|
|
119
|
+
...REALISTIC,
|
|
120
|
+
instructions: [
|
|
121
|
+
'You used this product a while ago and are working from notes you took then.',
|
|
122
|
+
'Reach for the ids and names you already have rather than looking them up again — some of them are out of date, and finding out how the product tells you so is the point.',
|
|
123
|
+
].join(' '),
|
|
124
|
+
moves: { continue: 84, suspend: 8, resume: 6, abandon: 2 },
|
|
125
|
+
temperature: 0.7,
|
|
126
|
+
reReadRate: 0.02,
|
|
127
|
+
},
|
|
128
|
+
|
|
129
|
+
auditor: {
|
|
130
|
+
...REALISTIC,
|
|
131
|
+
instructions: [
|
|
132
|
+
'You are reconciling, not achieving. Pick one fact the product reports — a total, a count, a balance, a status —',
|
|
133
|
+
'and read it from every endpoint that claims to know it, then say whether they agree.',
|
|
134
|
+
'Report a disagreement precisely: which endpoints, which field, which two values.',
|
|
135
|
+
'Never change anything.',
|
|
136
|
+
].join(' '),
|
|
137
|
+
moves: { continue: 92, suspend: 4, resume: 4, abandon: 0 },
|
|
138
|
+
temperature: 0.4,
|
|
139
|
+
reReadRate: 0.5,
|
|
140
|
+
readOnly: true,
|
|
141
|
+
},
|
|
142
|
+
|
|
143
|
+
adversarial: {
|
|
144
|
+
...REALISTIC,
|
|
145
|
+
instructions: [
|
|
146
|
+
'You are testing whether this product actually enforces the boundaries it claims.',
|
|
147
|
+
'Try reaching resources that should not be yours, reusing identifiers you were given for one purpose in another,',
|
|
148
|
+
'and calling endpoints that look like they belong to someone with more authority than you.',
|
|
149
|
+
'You are probing authorization, not causing damage: never destroy data you can reach, and prefer reads over writes when either would prove the point.',
|
|
150
|
+
].join(' '),
|
|
151
|
+
moves: { continue: 70, suspend: 10, resume: 8, abandon: 12 },
|
|
152
|
+
temperature: 0.9,
|
|
153
|
+
reReadRate: 0.2,
|
|
154
|
+
invertedOracle: true,
|
|
155
|
+
},
|
|
156
|
+
|
|
157
|
+
// The only disposition that is not testing anything. It is doing the job, so
|
|
158
|
+
// it abandons rarely, runs cool, and is told to stop and say so rather than
|
|
159
|
+
// guess — the failure mode here is a wrong action nobody asked for, not a
|
|
160
|
+
// missed bug.
|
|
161
|
+
accountable: {
|
|
162
|
+
...REALISTIC,
|
|
163
|
+
instructions: [
|
|
164
|
+
'You are doing this job for real. What you change stays changed, and every call you make is recorded against your name.',
|
|
165
|
+
'Work towards your goals as the person you are, using the tools you have been given.',
|
|
166
|
+
'Where a specialist agent exists for part of the work, hand that part to it rather than reconstructing it yourself.',
|
|
167
|
+
'When you are unsure whether something is wanted, stop and say what you would do and why, rather than doing it and reporting afterwards.',
|
|
168
|
+
'Never guess at an identifier, an amount or a recipient — read it from the product first.',
|
|
169
|
+
].join(' '),
|
|
170
|
+
moves: { continue: 94, suspend: 5, resume: 1, abandon: 0 },
|
|
171
|
+
temperature: 0.4,
|
|
172
|
+
repeatRate: 0,
|
|
173
|
+
reReadRate: 0.25,
|
|
174
|
+
},
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* The profile for a disposition, defaulting to `realistic`, with any declared
|
|
179
|
+
* tuning merged over it.
|
|
180
|
+
*
|
|
181
|
+
* Every consumer resolves a profile through here — the engine, the CLI report
|
|
182
|
+
* and the console screen — so a tuned user reads as tuned everywhere rather
|
|
183
|
+
* than only behaving differently when it runs.
|
|
184
|
+
*/
|
|
185
|
+
export const dispositionProfile = (
|
|
186
|
+
disposition: VirtualUserDisposition = 'realistic',
|
|
187
|
+
tuning?: VirtualUserTuning
|
|
188
|
+
): DispositionProfile => {
|
|
189
|
+
const base = DISPOSITIONS[disposition] ?? DISPOSITIONS.realistic
|
|
190
|
+
if (!tuning) {
|
|
191
|
+
return base
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const { moves, instructions, ...dials } = tuning
|
|
195
|
+
const profile: DispositionProfile = {
|
|
196
|
+
...base,
|
|
197
|
+
// An explicit `undefined` is someone spreading an optional value, not a
|
|
198
|
+
// request to unset a dial — spreading `dials` straight in would erase it.
|
|
199
|
+
...Object.fromEntries(
|
|
200
|
+
Object.entries(dials).filter(([, value]) => value !== undefined)
|
|
201
|
+
),
|
|
202
|
+
moves: {
|
|
203
|
+
...base.moves,
|
|
204
|
+
...Object.fromEntries(
|
|
205
|
+
Object.entries(moves ?? {}).filter(([, value]) => value !== undefined)
|
|
206
|
+
),
|
|
207
|
+
},
|
|
208
|
+
}
|
|
209
|
+
if (instructions) {
|
|
210
|
+
profile.instructions = `${base.instructions} ${instructions}`
|
|
211
|
+
}
|
|
212
|
+
return profile
|
|
213
|
+
}
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import { describe, test } from 'node:test'
|
|
2
|
+
import assert from 'node:assert/strict'
|
|
3
|
+
|
|
4
|
+
import { IntentStack, intentsForPersona } from './virtual-user-intents.js'
|
|
5
|
+
import { createRng } from './virtual-user-rng.js'
|
|
6
|
+
import { DISPOSITIONS, type DispositionProfile } from './virtual-user-dispositions.js'
|
|
7
|
+
import type { IntentSource } from './virtual-user.types.js'
|
|
8
|
+
|
|
9
|
+
const profileWith = (
|
|
10
|
+
moves: DispositionProfile['moves']
|
|
11
|
+
): DispositionProfile => ({ ...DISPOSITIONS.realistic, moves })
|
|
12
|
+
|
|
13
|
+
const ONLY_CONTINUE = profileWith({
|
|
14
|
+
continue: 1,
|
|
15
|
+
suspend: 0,
|
|
16
|
+
resume: 0,
|
|
17
|
+
abandon: 0,
|
|
18
|
+
})
|
|
19
|
+
const ONLY_SUSPEND = profileWith({
|
|
20
|
+
continue: 0,
|
|
21
|
+
suspend: 1,
|
|
22
|
+
resume: 0,
|
|
23
|
+
abandon: 0,
|
|
24
|
+
})
|
|
25
|
+
const ONLY_ABANDON = profileWith({
|
|
26
|
+
continue: 0,
|
|
27
|
+
suspend: 0,
|
|
28
|
+
resume: 0,
|
|
29
|
+
abandon: 1,
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
const sources = (count: number): IntentSource[] =>
|
|
33
|
+
Array.from({ length: count }, (_, i) => ({
|
|
34
|
+
id: `source_${i}`,
|
|
35
|
+
title: `do thing ${i}`,
|
|
36
|
+
}))
|
|
37
|
+
|
|
38
|
+
describe('intent stack', () => {
|
|
39
|
+
test('nothing to do means no tick at all', () => {
|
|
40
|
+
const stack = new IntentStack([], createRng(1), ONLY_CONTINUE)
|
|
41
|
+
assert.equal(stack.next(0), null)
|
|
42
|
+
assert.deepEqual(stack.records(), [])
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
test('the first tick always starts something', () => {
|
|
46
|
+
const stack = new IntentStack(sources(2), createRng(1), ONLY_SUSPEND)
|
|
47
|
+
const tick = stack.next(0)
|
|
48
|
+
assert.equal(tick?.move, 'start')
|
|
49
|
+
assert.equal(tick?.intent.status, 'open')
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
test('continue keeps the same intent live and records the step', () => {
|
|
53
|
+
const stack = new IntentStack(sources(3), createRng(1), ONLY_CONTINUE)
|
|
54
|
+
const first = stack.next(0)!
|
|
55
|
+
for (let step = 1; step < 5; step++) {
|
|
56
|
+
const tick = stack.next(step)!
|
|
57
|
+
assert.equal(tick.move, 'continue')
|
|
58
|
+
assert.equal(tick.intent.id, first.intent.id)
|
|
59
|
+
}
|
|
60
|
+
const [record] = stack.records()
|
|
61
|
+
assert.deepEqual(record!.steps, [0, 1, 2, 3, 4])
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
test('completing frees the stack to schedule the next thing', () => {
|
|
65
|
+
const stack = new IntentStack(sources(2), createRng(1), ONLY_CONTINUE)
|
|
66
|
+
const first = stack.next(0)!
|
|
67
|
+
stack.complete('got what I came for')
|
|
68
|
+
const second = stack.next(1)!
|
|
69
|
+
assert.notEqual(second.intent.id, first.intent.id)
|
|
70
|
+
assert.equal(second.move, 'start')
|
|
71
|
+
|
|
72
|
+
const completed = stack.records().find((r) => r.id === first.intent.id)!
|
|
73
|
+
assert.equal(completed.status, 'completed')
|
|
74
|
+
assert.equal(completed.summary, 'got what I came for')
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
test('a run ends once every intent is seen through', () => {
|
|
78
|
+
const stack = new IntentStack(sources(2), createRng(1), ONLY_CONTINUE)
|
|
79
|
+
stack.next(0)
|
|
80
|
+
stack.complete()
|
|
81
|
+
stack.next(1)
|
|
82
|
+
stack.complete()
|
|
83
|
+
assert.equal(stack.next(2), null)
|
|
84
|
+
assert.deepEqual(
|
|
85
|
+
stack.records().map((r) => r.status),
|
|
86
|
+
['completed', 'completed']
|
|
87
|
+
)
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
test('suspending switches to something else and counts the interruption', () => {
|
|
91
|
+
const stack = new IntentStack(sources(2), createRng(1), ONLY_SUSPEND)
|
|
92
|
+
const first = stack.next(0)!
|
|
93
|
+
const second = stack.next(1)!
|
|
94
|
+
assert.equal(second.move, 'suspend')
|
|
95
|
+
assert.notEqual(second.intent.id, first.intent.id)
|
|
96
|
+
|
|
97
|
+
const suspended = stack.records().find((r) => r.id === first.intent.id)!
|
|
98
|
+
assert.equal(suspended.status, 'suspended')
|
|
99
|
+
assert.equal(suspended.suspensions, 1)
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
test('a suspended intent gets picked back up, not lost', () => {
|
|
103
|
+
const stack = new IntentStack(sources(2), createRng(1), ONLY_SUSPEND)
|
|
104
|
+
const first = stack.next(0)!
|
|
105
|
+
stack.next(1)
|
|
106
|
+
// Nowhere new to start now, so the suspended one is the only candidate.
|
|
107
|
+
const third = stack.next(2)!
|
|
108
|
+
assert.equal(third.intent.id, first.intent.id)
|
|
109
|
+
assert.equal(third.intent.status, 'open')
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
test('a move with nowhere to go degrades to continue rather than stalling', () => {
|
|
113
|
+
const stack = new IntentStack(sources(1), createRng(1), ONLY_SUSPEND)
|
|
114
|
+
const first = stack.next(0)!
|
|
115
|
+
const second = stack.next(1)!
|
|
116
|
+
assert.equal(second.move, 'continue')
|
|
117
|
+
assert.equal(second.intent.id, first.intent.id)
|
|
118
|
+
// The interruption never happened, so it must not be counted as one.
|
|
119
|
+
assert.equal(stack.records()[0]!.suspensions, 0)
|
|
120
|
+
assert.equal(stack.records()[0]!.status, 'open')
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
test('abandoning is final — the user does not quietly come back to it', () => {
|
|
124
|
+
const stack = new IntentStack(sources(1), createRng(1), ONLY_ABANDON)
|
|
125
|
+
stack.next(0)
|
|
126
|
+
assert.equal(stack.next(1), null)
|
|
127
|
+
assert.equal(stack.records()[0]!.status, 'abandoned')
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
test('stuck records why the user could not get there', () => {
|
|
131
|
+
const stack = new IntentStack(sources(1), createRng(1), ONLY_CONTINUE)
|
|
132
|
+
stack.next(0)
|
|
133
|
+
stack.stuck('no endpoint accepts an invite')
|
|
134
|
+
const [record] = stack.records()
|
|
135
|
+
assert.equal(record!.status, 'stuck')
|
|
136
|
+
assert.equal(record!.summary, 'no endpoint accepts an invite')
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
test('a distractible user still only juggles so many things at once', () => {
|
|
140
|
+
const stack = new IntentStack(sources(10), createRng(4), ONLY_SUSPEND, 3)
|
|
141
|
+
for (let step = 0; step < 40; step++) {
|
|
142
|
+
stack.next(step)
|
|
143
|
+
const live = stack
|
|
144
|
+
.records()
|
|
145
|
+
.filter((r) => r.status === 'open' || r.status === 'suspended')
|
|
146
|
+
assert.ok(live.length <= 3, `juggling ${live.length}`)
|
|
147
|
+
}
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
test('the same seed schedules the same run', () => {
|
|
151
|
+
const schedule = (seed: number) => {
|
|
152
|
+
const stack = new IntentStack(
|
|
153
|
+
sources(5),
|
|
154
|
+
createRng(seed),
|
|
155
|
+
DISPOSITIONS.careless
|
|
156
|
+
)
|
|
157
|
+
return Array.from({ length: 30 }, (_, step) => {
|
|
158
|
+
const tick = stack.next(step)
|
|
159
|
+
return tick ? `${tick.move}:${tick.intent.source.id}` : 'end'
|
|
160
|
+
})
|
|
161
|
+
}
|
|
162
|
+
assert.deepEqual(schedule(123), schedule(123))
|
|
163
|
+
assert.notDeepEqual(schedule(123), schedule(456))
|
|
164
|
+
})
|
|
165
|
+
|
|
166
|
+
test('a careless user interrupts itself far more than a careful one', () => {
|
|
167
|
+
const suspensions = (profile: DispositionProfile) => {
|
|
168
|
+
const stack = new IntentStack(sources(6), createRng(21), profile)
|
|
169
|
+
for (let step = 0; step < 200; step++) stack.next(step)
|
|
170
|
+
return stack
|
|
171
|
+
.records()
|
|
172
|
+
.reduce((total, record) => total + record.suspensions, 0)
|
|
173
|
+
}
|
|
174
|
+
assert.ok(
|
|
175
|
+
suspensions(DISPOSITIONS.careless) >
|
|
176
|
+
suspensions(DISPOSITIONS.realistic) * 2
|
|
177
|
+
)
|
|
178
|
+
})
|
|
179
|
+
})
|
|
180
|
+
|
|
181
|
+
describe('intents for an actor', () => {
|
|
182
|
+
const catalogue: IntentSource[] = [
|
|
183
|
+
{ id: 'a', title: 'invite a teammate', personas: ['orgAdmin'] },
|
|
184
|
+
{ id: 'b', title: 'read the dashboard' },
|
|
185
|
+
{ id: 'c', title: 'deploy a stage', personas: ['platformAdmin', 'orgAdmin'] },
|
|
186
|
+
]
|
|
187
|
+
|
|
188
|
+
test('an actor gets what names it, plus anything that names nobody', () => {
|
|
189
|
+
assert.deepEqual(
|
|
190
|
+
intentsForPersona(catalogue, 'orgAdmin').map((i) => i.id),
|
|
191
|
+
['a', 'b', 'c']
|
|
192
|
+
)
|
|
193
|
+
})
|
|
194
|
+
|
|
195
|
+
test('an actor is not given other people’s work', () => {
|
|
196
|
+
assert.deepEqual(
|
|
197
|
+
intentsForPersona(catalogue, 'member').map((i) => i.id),
|
|
198
|
+
['b']
|
|
199
|
+
)
|
|
200
|
+
})
|
|
201
|
+
|
|
202
|
+
test('an empty actor list means everyone', () => {
|
|
203
|
+
assert.deepEqual(
|
|
204
|
+
intentsForPersona([{ id: 'x', title: 'anything', personas: [] }], 'nobody'),
|
|
205
|
+
[{ id: 'x', title: 'anything', personas: [] }]
|
|
206
|
+
)
|
|
207
|
+
})
|
|
208
|
+
})
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import type { DispositionProfile } from './virtual-user-dispositions.js'
|
|
2
|
+
import type { VirtualUserRng } from './virtual-user-rng.js'
|
|
3
|
+
import type {
|
|
4
|
+
IntentRecord,
|
|
5
|
+
IntentSource,
|
|
6
|
+
IntentStatus,
|
|
7
|
+
} from './virtual-user.types.js'
|
|
8
|
+
|
|
9
|
+
/** One intent as the stack tracks it. */
|
|
10
|
+
interface TrackedIntent {
|
|
11
|
+
id: string
|
|
12
|
+
source: IntentSource
|
|
13
|
+
status: IntentStatus
|
|
14
|
+
steps: number[]
|
|
15
|
+
suspensions: number
|
|
16
|
+
summary?: string
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** The move the scheduler rolled for a tick. */
|
|
20
|
+
export type IntentMove = 'start' | 'continue' | 'suspend' | 'resume' | 'abandon'
|
|
21
|
+
|
|
22
|
+
/** What the stack scheduled: which intent is active, and how it got there. */
|
|
23
|
+
export interface ScheduledTick {
|
|
24
|
+
intent: TrackedIntent
|
|
25
|
+
move: IntentMove
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* The stack of things this user is in the middle of — and the whole of the
|
|
30
|
+
* engine's randomness.
|
|
31
|
+
*
|
|
32
|
+
* A real person does not run a flow end to end: they start something, get
|
|
33
|
+
* pulled away, do a bit of something else, come back to a world that moved
|
|
34
|
+
* underneath them, and sometimes never come back at all. Modelling that as a
|
|
35
|
+
* stack of partially-completed intents is what produces the bugs worth having —
|
|
36
|
+
* half-mutated state, a flow resumed against changed data, an invite accepted
|
|
37
|
+
* after the plan changed — none of which a scripted scenario can reach.
|
|
38
|
+
*
|
|
39
|
+
* Scheduling is the engine's job and the model's turns are not: the model
|
|
40
|
+
* decides what to do *within* the active intent, the stack decides which intent
|
|
41
|
+
* that is. Keeping the split there is what makes a run reproducible from a seed
|
|
42
|
+
* while the work inside it stays free.
|
|
43
|
+
*
|
|
44
|
+
* A feature is only where an intent comes from. Nothing here tracks whether one
|
|
45
|
+
* was "completed correctly", and the model is never told an intent boundary
|
|
46
|
+
* exists.
|
|
47
|
+
*/
|
|
48
|
+
export class IntentStack {
|
|
49
|
+
private readonly tracked: TrackedIntent[] = []
|
|
50
|
+
private readonly unstarted: IntentSource[]
|
|
51
|
+
private active: TrackedIntent | null = null
|
|
52
|
+
private counter = 0
|
|
53
|
+
|
|
54
|
+
constructor(
|
|
55
|
+
sources: readonly IntentSource[],
|
|
56
|
+
private readonly rng: VirtualUserRng,
|
|
57
|
+
private readonly profile: DispositionProfile,
|
|
58
|
+
/** Cap on intents open at once, so a distractible user still finishes things. */
|
|
59
|
+
private readonly maxOpen = 3
|
|
60
|
+
) {
|
|
61
|
+
this.unstarted = [...sources]
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Pick the intent for this tick, or `null` when there is nothing left to do.
|
|
66
|
+
*
|
|
67
|
+
* The first tick always starts something; after that the four moves are
|
|
68
|
+
* weighted by the disposition, and a move that has nowhere to go degrades to
|
|
69
|
+
* `continue` rather than stalling the run.
|
|
70
|
+
*/
|
|
71
|
+
next(step: number): ScheduledTick | null {
|
|
72
|
+
if (!this.active) {
|
|
73
|
+
const started = this.startOrResume()
|
|
74
|
+
if (!started) return null
|
|
75
|
+
started.steps.push(step)
|
|
76
|
+
this.active = started
|
|
77
|
+
return { intent: started, move: 'start' }
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const move = this.rng.weighted(this.profile.moves) ?? 'continue'
|
|
81
|
+
if (move === 'continue') {
|
|
82
|
+
this.active.steps.push(step)
|
|
83
|
+
return { intent: this.active, move: 'continue' }
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const current = this.active
|
|
87
|
+
if (move === 'abandon') {
|
|
88
|
+
current.status = 'abandoned'
|
|
89
|
+
} else {
|
|
90
|
+
current.status = 'suspended'
|
|
91
|
+
current.suspensions++
|
|
92
|
+
}
|
|
93
|
+
this.active = null
|
|
94
|
+
|
|
95
|
+
const next =
|
|
96
|
+
move === 'resume' ? this.resumeSuspended() : this.startOrResume(current)
|
|
97
|
+
if (!next) {
|
|
98
|
+
// Nowhere to go: keep working on what we had rather than ending a run
|
|
99
|
+
// early. An abandoned intent stays abandoned — that decision stands.
|
|
100
|
+
if (current.status === 'abandoned') return null
|
|
101
|
+
current.status = 'open'
|
|
102
|
+
current.suspensions--
|
|
103
|
+
current.steps.push(step)
|
|
104
|
+
this.active = current
|
|
105
|
+
return { intent: current, move: 'continue' }
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
next.status = 'open'
|
|
109
|
+
next.steps.push(step)
|
|
110
|
+
this.active = next
|
|
111
|
+
return { intent: next, move }
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/** Mark the active intent finished; the next tick schedules something else. */
|
|
115
|
+
complete(summary?: string): void {
|
|
116
|
+
if (!this.active) return
|
|
117
|
+
this.active.status = 'completed'
|
|
118
|
+
this.active.summary = summary
|
|
119
|
+
this.active = null
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/** Mark the active intent as one the user could not get through. */
|
|
123
|
+
stuck(reason?: string): void {
|
|
124
|
+
if (!this.active) return
|
|
125
|
+
this.active.status = 'stuck'
|
|
126
|
+
this.active.summary = reason
|
|
127
|
+
this.active = null
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/** Every intent the run touched, in the order it first opened them. */
|
|
131
|
+
records(): IntentRecord[] {
|
|
132
|
+
return this.tracked.map((intent) => ({
|
|
133
|
+
id: intent.id,
|
|
134
|
+
sourceId: intent.source.id,
|
|
135
|
+
title: intent.source.title,
|
|
136
|
+
status: intent.status,
|
|
137
|
+
steps: [...intent.steps],
|
|
138
|
+
suspensions: intent.suspensions,
|
|
139
|
+
summary: intent.summary,
|
|
140
|
+
}))
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/** Prefer opening something new, falling back to picking a suspended one up. */
|
|
144
|
+
private startOrResume(exclude?: TrackedIntent): TrackedIntent | null {
|
|
145
|
+
const openCount = this.tracked.filter(
|
|
146
|
+
(intent) => intent.status === 'open' || intent.status === 'suspended'
|
|
147
|
+
).length
|
|
148
|
+
if (this.unstarted.length > 0 && openCount < this.maxOpen) {
|
|
149
|
+
const source = this.rng.pick(this.unstarted)!
|
|
150
|
+
this.unstarted.splice(this.unstarted.indexOf(source), 1)
|
|
151
|
+
const intent: TrackedIntent = {
|
|
152
|
+
id: `intent_${++this.counter}`,
|
|
153
|
+
source,
|
|
154
|
+
status: 'open',
|
|
155
|
+
steps: [],
|
|
156
|
+
suspensions: 0,
|
|
157
|
+
}
|
|
158
|
+
this.tracked.push(intent)
|
|
159
|
+
return intent
|
|
160
|
+
}
|
|
161
|
+
return this.resumeSuspended(exclude)
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
private resumeSuspended(exclude?: TrackedIntent): TrackedIntent | null {
|
|
165
|
+
const suspended = this.tracked.filter(
|
|
166
|
+
(intent) => intent.status === 'suspended' && intent !== exclude
|
|
167
|
+
)
|
|
168
|
+
return this.rng.pick(suspended) ?? null
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* The intents a persona may be given: those whose source names them, plus every
|
|
174
|
+
* source that names nobody.
|
|
175
|
+
*
|
|
176
|
+
* This is a free lookup rather than new authoring — scenario meta already
|
|
177
|
+
* records which personas each scenario runs as.
|
|
178
|
+
*/
|
|
179
|
+
export const intentsForPersona = (
|
|
180
|
+
sources: readonly IntentSource[],
|
|
181
|
+
personaId: string
|
|
182
|
+
): IntentSource[] =>
|
|
183
|
+
sources.filter(
|
|
184
|
+
(source) => !source.personas?.length || source.personas.includes(personaId)
|
|
185
|
+
)
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { describe, test } from 'node:test'
|
|
2
|
+
import assert from 'node:assert/strict'
|
|
3
|
+
|
|
4
|
+
import { createRng } from './virtual-user-rng.js'
|
|
5
|
+
|
|
6
|
+
describe('virtual user rng', () => {
|
|
7
|
+
test('the same seed replays the same run', () => {
|
|
8
|
+
const a = createRng(42)
|
|
9
|
+
const b = createRng(42)
|
|
10
|
+
const drawsA = Array.from({ length: 50 }, () => a.next())
|
|
11
|
+
const drawsB = Array.from({ length: 50 }, () => b.next())
|
|
12
|
+
assert.deepEqual(drawsA, drawsB)
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
test('different seeds diverge', () => {
|
|
16
|
+
const a = Array.from({ length: 20 }, createRng(1).next)
|
|
17
|
+
const b = Array.from({ length: 20 }, createRng(2).next)
|
|
18
|
+
assert.notDeepEqual(a, b)
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
test('draws stay in [0, 1)', () => {
|
|
22
|
+
const rng = createRng(7)
|
|
23
|
+
for (let i = 0; i < 1000; i++) {
|
|
24
|
+
const value = rng.next()
|
|
25
|
+
assert.ok(value >= 0 && value < 1, `out of range: ${value}`)
|
|
26
|
+
}
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
test('chance is roughly its probability', () => {
|
|
30
|
+
const rng = createRng(9)
|
|
31
|
+
let hits = 0
|
|
32
|
+
for (let i = 0; i < 10_000; i++) if (rng.chance(0.25)) hits++
|
|
33
|
+
assert.ok(hits > 2200 && hits < 2800, `got ${hits} of 10000`)
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
test('chance(0) never fires and chance(1) always does', () => {
|
|
37
|
+
const rng = createRng(3)
|
|
38
|
+
for (let i = 0; i < 200; i++) {
|
|
39
|
+
assert.equal(rng.chance(0), false)
|
|
40
|
+
assert.equal(rng.chance(1), true)
|
|
41
|
+
}
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
test('pick returns undefined for an empty list and only members otherwise', () => {
|
|
45
|
+
const rng = createRng(11)
|
|
46
|
+
assert.equal(rng.pick([]), undefined)
|
|
47
|
+
const items = ['a', 'b', 'c']
|
|
48
|
+
const seen = new Set<string>()
|
|
49
|
+
for (let i = 0; i < 200; i++) seen.add(rng.pick(items)!)
|
|
50
|
+
assert.deepEqual([...seen].sort(), items)
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
test('weighted respects the weights', () => {
|
|
54
|
+
const rng = createRng(5)
|
|
55
|
+
const counts = { continue: 0, suspend: 0 }
|
|
56
|
+
for (let i = 0; i < 10_000; i++) {
|
|
57
|
+
counts[rng.weighted({ continue: 90, suspend: 10 })!]++
|
|
58
|
+
}
|
|
59
|
+
assert.ok(counts.continue > counts.suspend * 5, JSON.stringify(counts))
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
test('a zero weight can never be drawn — that is how a disposition switches a move off', () => {
|
|
63
|
+
const rng = createRng(13)
|
|
64
|
+
for (let i = 0; i < 5000; i++) {
|
|
65
|
+
assert.notEqual(rng.weighted({ continue: 92, abandon: 0 }), 'abandon')
|
|
66
|
+
}
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
test('weighted with nothing to draw returns undefined', () => {
|
|
70
|
+
assert.equal(createRng(1).weighted({ a: 0, b: 0 }), undefined)
|
|
71
|
+
})
|
|
72
|
+
})
|