@pikku/core 0.12.72 → 0.12.77
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 +1171 -0
- package/dist/column-form.d.ts +32 -0
- package/dist/column-form.js +42 -0
- package/dist/crypto-utils.d.ts +43 -7
- package/dist/crypto-utils.js +163 -42
- package/dist/data-classification.d.ts +44 -0
- 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 +57 -136
- package/dist/function/functions.types.js +0 -58
- package/dist/function/index.d.ts +1 -1
- 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 +11 -5
- package/dist/index.js +7 -2
- 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 +5 -41
- 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 +4 -3
- 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 +9 -8
- 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/secret-value.d.ts +56 -0
- package/dist/secret-value.js +46 -0
- 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.d.ts +74 -4
- package/dist/services/audit-service.js +8 -7
- package/dist/services/content-service.d.ts +1 -46
- package/dist/services/credential-service.d.ts +3 -40
- package/dist/services/credential-wire-service.d.ts +5 -0
- package/dist/services/credential-wire-service.js +9 -1
- package/dist/services/deployment-service.d.ts +3 -9
- package/dist/services/email-service.d.ts +2 -1
- 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 +8 -9
- package/dist/services/index.js +3 -6
- 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-request-handler.d.ts +29 -0
- package/dist/services/local-content-request-handler.js +176 -0
- 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 +4 -7
- package/dist/services/local-secrets.js +7 -7
- package/dist/services/logger-console.d.ts +3 -7
- package/dist/services/logger-console.js +3 -7
- package/dist/services/logger.d.ts +22 -40
- 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 +10 -42
- 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 +4 -7
- 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 +12 -35
- 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 +5 -11
- 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 +21 -64
- package/dist/services/webhook-service.js +6 -20
- package/dist/services/workflow-service.d.ts +3 -15
- package/dist/testing/service-tests.js +6 -23
- package/dist/time-utils.d.ts +0 -16
- package/dist/time-utils.js +1 -19
- package/dist/types/core.types.d.ts +120 -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 +46 -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 +65 -132
- 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 +63 -22
- package/dist/wirings/ai-agent/ai-agent-stream.d.ts +25 -2
- package/dist/wirings/ai-agent/ai-agent-stream.js +180 -64
- package/dist/wirings/ai-agent/ai-agent.types.d.ts +124 -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 +59 -1
- package/dist/wirings/ai-agent/voice-input.js +90 -12
- package/dist/wirings/ai-agent/voice-output.d.ts +69 -1
- package/dist/wirings/ai-agent/voice-output.js +162 -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 +38 -0
- package/dist/wirings/cli/channel/cli-raw-client-runner.js +129 -0
- package/dist/wirings/cli/channel/index.d.ts +5 -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 +32 -0
- package/dist/wirings/persona/define-personas.js +31 -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 +136 -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 +103 -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 +20 -90
- 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 +17 -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 +3 -13
- package/dist/wirings/rpc/remote-addon-auth.js +7 -11
- 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 +30 -0
- package/dist/wirings/virtual-user/index.js +10 -0
- package/dist/wirings/virtual-user/prepare-virtual-user-run.d.ts +54 -0
- package/dist/wirings/virtual-user/prepare-virtual-user-run.js +49 -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-run-store.d.ts +90 -0
- package/dist/wirings/virtual-user/virtual-user-run-store.js +1 -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 +19 -15
- 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 -43
- 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 +1 -19
- 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 +84 -12
- 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 +5 -195
- 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/a-virtual-user-run-is-not-a-workflow-and-not-a-queued-job.md +48 -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-column-form-is-an-axis-of-its-own.md +84 -0
- package/knowledge/decisions/internals/core-data-classification-brand-is-an-optional-property.md +41 -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 +117 -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/one-project-shape-check-two-validators.md +53 -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/scenarios-live-in-files-named-for-them.md +48 -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/validate-checks-personas-through-a-shared-module.md +43 -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 +6 -2
- package/run-tests.sh +0 -0
- package/src/column-form.test.ts +97 -0
- package/src/column-form.ts +58 -0
- package/src/crypto-utils.test.ts +460 -19
- package/src/crypto-utils.ts +306 -59
- package/src/data-classification.ts +45 -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 +95 -138
- package/src/function/index.ts +1 -0
- 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 +84 -3
- package/src/middleware/auth-apikey.test.ts +0 -1
- package/src/middleware/auth-apikey.ts +0 -17
- package/src/middleware/auth-bearer.test.ts +3 -5
- package/src/middleware/auth-bearer.ts +5 -41
- 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 +26 -10
- package/src/middleware/remote-auth.ts +11 -3
- 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 +173 -0
- package/src/remote.ts +18 -8
- package/src/schema.ts +5 -39
- package/src/scopes.ts +7 -48
- package/src/secret-value.test.ts +204 -0
- package/src/secret-value.ts +111 -0
- 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 +88 -11
- 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/credential-wire-service.ts +9 -1
- package/src/services/deployment-service.ts +3 -9
- package/src/services/email-service.ts +3 -1
- 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 +23 -18
- package/src/services/istanbul-coverage-service.ts +2 -8
- package/src/services/jwt-service.ts +1 -16
- package/src/services/local-content-request-handler.test.ts +202 -0
- package/src/services/local-content-request-handler.ts +267 -0
- 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.test.ts +20 -5
- package/src/services/local-secrets.ts +15 -11
- package/src/services/logger-console.test.ts +0 -1
- package/src/services/logger-console.ts +3 -7
- package/src/services/logger.ts +31 -46
- 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.test.ts +2 -1
- package/src/services/queue-webhook-service.ts +10 -42
- 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 +4 -7
- 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 +12 -35
- 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 +12 -14
- package/src/services/v8-coverage-service.ts +3 -6
- package/src/services/variables-service.ts +1 -8
- package/src/services/webhook-service.ts +23 -64
- package/src/services/workflow-service.ts +3 -20
- package/src/testing/service-tests.ts +6 -32
- package/src/time-utils.ts +1 -19
- package/src/types/core.types.ts +137 -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 +91 -10
- package/src/wirings/ai-agent/ai-agent-agui.ts +49 -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 +89 -139
- 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 +85 -35
- package/src/wirings/ai-agent/ai-agent-stream.test.ts +205 -103
- package/src/wirings/ai-agent/ai-agent-stream.ts +224 -76
- package/src/wirings/ai-agent/ai-agent-thread-ownership.test.ts +301 -0
- package/src/wirings/ai-agent/ai-agent.types.ts +130 -5
- package/src/wirings/ai-agent/index.ts +37 -3
- package/src/wirings/ai-agent/voice-input.test.ts +137 -7
- package/src/wirings/ai-agent/voice-input.ts +96 -12
- package/src/wirings/ai-agent/voice-output.test.ts +512 -0
- package/src/wirings/ai-agent/voice-output.ts +243 -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 +185 -0
- package/src/wirings/cli/channel/index.ts +13 -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 +33 -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 +142 -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 +454 -0
- package/src/wirings/queue/queue-identity.ts +176 -0
- package/src/wirings/queue/queue-runner.ts +12 -31
- package/src/wirings/queue/queue.types.ts +25 -90
- package/src/wirings/queue/register-queue-helper.ts +0 -14
- package/src/wirings/queue/signed-queue-service.ts +60 -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 +9 -16
- package/src/wirings/rpc/rpc-runner.test.ts +192 -6
- 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 +88 -0
- package/src/wirings/virtual-user/prepare-virtual-user-run.test.ts +115 -0
- package/src/wirings/virtual-user/prepare-virtual-user-run.ts +95 -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-run-store.ts +98 -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 +19 -20
- 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 -61
- 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.test.ts +5 -7
- package/src/wirings/workflow/scenario-prose.ts +1 -29
- package/src/wirings/workflow/scenario-service.test.ts +2 -10
- package/src/wirings/workflow/scenario-step-guards.ts +1 -14
- package/src/wirings/workflow/scenario-step.test.ts +163 -19
- package/src/wirings/workflow/scenario-step.types.ts +94 -12
- package/src/wirings/workflow/scenario-surface.test.ts +146 -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 +5 -201
- package/tsconfig.tsbuildinfo +1 -1
- package/tsconfig.type-tests.json +12 -0
- 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,49 @@
|
|
|
1
|
+
import { flattenSystemRoleDefinitions } from '../role/validate-role-definitions.js';
|
|
2
|
+
import { deriveCatalogue, deriveIntents, } from './virtual-user-derive.js';
|
|
3
|
+
import { reachableAgents, } from './virtual-user-agents.js';
|
|
4
|
+
/**
|
|
5
|
+
* The scopes a persona holds, resolved through its roles.
|
|
6
|
+
*
|
|
7
|
+
* Roles are the only thing a persona declares; scopes are what a function
|
|
8
|
+
* checks. Narrowing a virtual user's catalogue needs the second, so the
|
|
9
|
+
* expansion happens once, here, against the same `defineSystemRole` definitions
|
|
10
|
+
* the seed grants from.
|
|
11
|
+
*/
|
|
12
|
+
export const personaScopes = (persona, roleScopes) => {
|
|
13
|
+
const scopes = new Set();
|
|
14
|
+
for (const role of persona.roles ?? []) {
|
|
15
|
+
for (const scope of roleScopes[role] ?? []) {
|
|
16
|
+
scopes.add(scope);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return [...scopes].sort();
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Derive what a virtual user needs from what the project already generates.
|
|
23
|
+
*
|
|
24
|
+
* Shared because there are two callers with the same problem and different
|
|
25
|
+
* sources for it: `pikku persona run` reads the inspector state at build time,
|
|
26
|
+
* and the scaffolded `runVirtualUser` RPC reads `metaService` at runtime. They
|
|
27
|
+
* must agree — a persona whose catalogue is narrower over RPC than on the CLI
|
|
28
|
+
* finds different things from the same seed, which is exactly the property a
|
|
29
|
+
* seed exists to give.
|
|
30
|
+
*
|
|
31
|
+
* Nothing here is authored for the virtual user's benefit: the function meta is
|
|
32
|
+
* the catalogue, the scenario meta is the intents, and the role definitions are
|
|
33
|
+
* what turn a persona's declared roles into the scopes a function checks.
|
|
34
|
+
*/
|
|
35
|
+
export const prepareVirtualUserRun = (input) => {
|
|
36
|
+
const catalogue = deriveCatalogue(input.functionsMeta, input.schemas ?? {});
|
|
37
|
+
const intents = deriveIntents(input.workflowsMeta ?? {}, input.functionsMeta);
|
|
38
|
+
const declared = input.systemRoles ?? [];
|
|
39
|
+
const roleScopes = {};
|
|
40
|
+
for (const role of flattenSystemRoleDefinitions(Array.isArray(declared) ? declared : Object.values(declared))) {
|
|
41
|
+
roleScopes[role.name] = role.scopes;
|
|
42
|
+
}
|
|
43
|
+
const scopes = personaScopes(input.persona, roleScopes);
|
|
44
|
+
// Gated by the same scopes as the RPCs, because an agent is reached rather
|
|
45
|
+
// than declared: `CoreAIAgent.scopes` is checked against the session, so a
|
|
46
|
+
// persona finds the specialists its roles unlock and no others.
|
|
47
|
+
const agents = reachableAgents(input.agentsMeta ?? {}, scopes);
|
|
48
|
+
return { catalogue, intents, scopes, agents };
|
|
49
|
+
};
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import type { ResolvedPersona } from '../../services/personas-service.js';
|
|
2
|
+
import type { ActorLLM } from '../actor-flow/run-conversation.js';
|
|
3
|
+
import { type VirtualUserTuning } from './virtual-user-dispositions.js';
|
|
4
|
+
import type { ApiCatalogueEntry, IntentSource, VirtualUserBudget, VirtualUserDisposition, VirtualUserFinding, VirtualUserRunResult, VirtualUserTally, VirtualUserTarget } from './virtual-user.types.js';
|
|
5
|
+
/** What an app-specific oracle is shown about one call. */
|
|
6
|
+
export interface VirtualUserCallContext {
|
|
7
|
+
rpcName: string;
|
|
8
|
+
args: unknown;
|
|
9
|
+
status: number;
|
|
10
|
+
ok: boolean;
|
|
11
|
+
body: unknown;
|
|
12
|
+
intentId?: string;
|
|
13
|
+
step: number;
|
|
14
|
+
disposition: VirtualUserDisposition;
|
|
15
|
+
}
|
|
16
|
+
export interface RunVirtualUserParams {
|
|
17
|
+
/** The person being run — name, job title, personality, goals. */
|
|
18
|
+
persona: ResolvedPersona;
|
|
19
|
+
/** The id they were declared under, as the intent sources refer to them. */
|
|
20
|
+
personaId: string;
|
|
21
|
+
/** How it behaves. Default `realistic`. */
|
|
22
|
+
disposition?: VirtualUserDisposition;
|
|
23
|
+
/** Per-user overrides for that disposition's dials. */
|
|
24
|
+
tuning?: VirtualUserTuning;
|
|
25
|
+
/** Every RPC the app exposes. Narrowed internally; nothing is hidden by default. */
|
|
26
|
+
catalogue: readonly ApiCatalogueEntry[];
|
|
27
|
+
/** Features and scenarios in the app's own words — never their step graphs. */
|
|
28
|
+
intents: readonly IntentSource[];
|
|
29
|
+
/** Extra goals in the user's own words, run alongside the derived intents. */
|
|
30
|
+
goals?: readonly string[];
|
|
31
|
+
/** The transport it acts through. */
|
|
32
|
+
target: VirtualUserTarget;
|
|
33
|
+
/** The user's own LLM. */
|
|
34
|
+
llm: ActorLLM;
|
|
35
|
+
model: string;
|
|
36
|
+
budget?: VirtualUserBudget;
|
|
37
|
+
/**
|
|
38
|
+
* The app's own stopping rule, given the running tally. Pikku counts steps,
|
|
39
|
+
* calls, mutations and tokens because only the engine sees them; what they
|
|
40
|
+
* cost is the app's to decide, which is why no price appears in core.
|
|
41
|
+
*/
|
|
42
|
+
stop?: (tally: VirtualUserTally) => boolean | Promise<boolean>;
|
|
43
|
+
/** Fixed seed, so a run replays into the same finding. */
|
|
44
|
+
seed?: number;
|
|
45
|
+
/**
|
|
46
|
+
* Check a response against the RPC's own output schema. Supplied by the CLI
|
|
47
|
+
* from generated schemas; a mismatch means the contract and the implementation
|
|
48
|
+
* disagree, which nothing else in a normal test suite looks for.
|
|
49
|
+
*/
|
|
50
|
+
validateOutput?: (rpcName: string, body: unknown) => string | null;
|
|
51
|
+
/** App-specific oracle — ownership, invariants, anything core cannot know. */
|
|
52
|
+
classify?: (context: VirtualUserCallContext) => VirtualUserFinding[] | null | undefined;
|
|
53
|
+
/** Files this user may upload. */
|
|
54
|
+
fixtures?: readonly string[];
|
|
55
|
+
/** Agents it may talk to. */
|
|
56
|
+
agents?: readonly {
|
|
57
|
+
name: string;
|
|
58
|
+
description?: string;
|
|
59
|
+
}[];
|
|
60
|
+
/** Ids and slugs remembered from an earlier run, for a `stale` user. */
|
|
61
|
+
memory?: Record<string, string>;
|
|
62
|
+
/**
|
|
63
|
+
* The scopes this persona actually holds, resolved from its roles at sign-in.
|
|
64
|
+
*
|
|
65
|
+
* Used two ways: to narrow a large catalogue to what it is entitled to reach,
|
|
66
|
+
* and as an oracle — a call whose scopes it does not hold that nonetheless
|
|
67
|
+
* succeeds is authorization drift.
|
|
68
|
+
*/
|
|
69
|
+
scopes?: readonly string[];
|
|
70
|
+
/**
|
|
71
|
+
* Offer the endpoints the app marked as needing a human's approval. Off by
|
|
72
|
+
* default: those are the ones that spend money and move real traffic.
|
|
73
|
+
*/
|
|
74
|
+
allowApprovalRequired?: boolean;
|
|
75
|
+
maxStepsPerIntent?: number;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Pull identifiers out of a response so later intents can refer to them.
|
|
79
|
+
*
|
|
80
|
+
* This is what makes a run cohesive without ever handing the user a dataflow
|
|
81
|
+
* map: it discovers that a project has an id by looking at one, the same way a
|
|
82
|
+
* person reads it off the screen.
|
|
83
|
+
*/
|
|
84
|
+
export declare const rememberIds: (body: unknown, into: Record<string, string>, depth?: number) => Record<string, string>;
|
|
85
|
+
/**
|
|
86
|
+
* Run one virtual user against a live target until its budget runs out.
|
|
87
|
+
*
|
|
88
|
+
* The engine schedules which intent is live and the model decides what to do
|
|
89
|
+
* within it — that split is what lets a run be replayed from its seed while the
|
|
90
|
+
* work inside it stays genuinely open-ended.
|
|
91
|
+
*/
|
|
92
|
+
export declare const runVirtualUser: (params: RunVirtualUserParams) => Promise<VirtualUserRunResult>;
|
|
@@ -0,0 +1,478 @@
|
|
|
1
|
+
import { getDurationInMilliseconds } from '../../time-utils.js';
|
|
2
|
+
import { catalogueIndex, catalogueLookup, describeEntry, isReadOnly, reachableCatalogue, renderCatalogue, } from './virtual-user-catalogue.js';
|
|
3
|
+
import { dispositionProfile, } from './virtual-user-dispositions.js';
|
|
4
|
+
import { IntentStack, intentsForPersona } from './virtual-user-intents.js';
|
|
5
|
+
import { createRng } from './virtual-user-rng.js';
|
|
6
|
+
/** One turn: what the user decided to do next, and why. */
|
|
7
|
+
const ACTION_SCHEMA = {
|
|
8
|
+
type: 'object',
|
|
9
|
+
properties: {
|
|
10
|
+
thought: { type: 'string' },
|
|
11
|
+
kind: {
|
|
12
|
+
type: 'string',
|
|
13
|
+
enum: ['describe', 'call', 'talkTo', 'upload', 'complete', 'stuck'],
|
|
14
|
+
},
|
|
15
|
+
rpcName: { type: 'string' },
|
|
16
|
+
/**
|
|
17
|
+
* A JSON object, as a string. Not `type: 'object'`: structured output is
|
|
18
|
+
* strict, and a schema-less object is the one thing strict mode cannot
|
|
19
|
+
* express — OpenAI rejects the whole request with "'additionalProperties'
|
|
20
|
+
* is required to be supplied and to be false", so every turn 400s and the
|
|
21
|
+
* run never takes a step. The arguments differ per endpoint and are only
|
|
22
|
+
* known from the `describe` the user just did, so there is no schema to
|
|
23
|
+
* give here; a string is how a free-form object crosses that boundary.
|
|
24
|
+
*/
|
|
25
|
+
args: { type: 'string' },
|
|
26
|
+
agent: { type: 'string' },
|
|
27
|
+
task: { type: 'string' },
|
|
28
|
+
file: { type: 'string' },
|
|
29
|
+
summary: { type: 'string' },
|
|
30
|
+
},
|
|
31
|
+
required: ['kind'],
|
|
32
|
+
};
|
|
33
|
+
const DEFAULT_MAX_STEPS = 40;
|
|
34
|
+
const DEFAULT_MAX_STEPS_PER_INTENT = 20;
|
|
35
|
+
const RESPONSE_EXCERPT = 600;
|
|
36
|
+
const msg = (role, content) => ({
|
|
37
|
+
id: globalThis.crypto.randomUUID(),
|
|
38
|
+
role,
|
|
39
|
+
content,
|
|
40
|
+
createdAt: new Date(),
|
|
41
|
+
});
|
|
42
|
+
/** Read a structured result, falling back to parsing JSON out of the text. */
|
|
43
|
+
const readObject = (result) => {
|
|
44
|
+
if (result.object && typeof result.object === 'object')
|
|
45
|
+
return result.object;
|
|
46
|
+
if (result.text) {
|
|
47
|
+
try {
|
|
48
|
+
return JSON.parse(result.text);
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return null;
|
|
55
|
+
};
|
|
56
|
+
/**
|
|
57
|
+
* The arguments for a call, however the model chose to spell them.
|
|
58
|
+
*
|
|
59
|
+
* `ACTION_SCHEMA` asks for a JSON string because strict structured output
|
|
60
|
+
* cannot carry a schema-less object, but a model reading the endpoint's own
|
|
61
|
+
* schema will sometimes send the object anyway, and a scripted or non-strict
|
|
62
|
+
* provider always will. Both are the same intent, so both are accepted; text
|
|
63
|
+
* that is not JSON at all is not arguments, and calling with it would only
|
|
64
|
+
* produce a 400 attributable to the wrong thing.
|
|
65
|
+
*/
|
|
66
|
+
const readArgs = (args) => {
|
|
67
|
+
if (args && typeof args === 'object')
|
|
68
|
+
return args;
|
|
69
|
+
if (typeof args === 'string' && args.trim()) {
|
|
70
|
+
try {
|
|
71
|
+
const parsed = JSON.parse(args);
|
|
72
|
+
if (parsed && typeof parsed === 'object') {
|
|
73
|
+
return parsed;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
// Not JSON — fall through to no arguments.
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return {};
|
|
81
|
+
};
|
|
82
|
+
/** Keys worth remembering across intents — the things a person writes down. */
|
|
83
|
+
const MEMORABLE = /(id|slug|token|email|name|key|url|code)$/i;
|
|
84
|
+
/**
|
|
85
|
+
* Pull identifiers out of a response so later intents can refer to them.
|
|
86
|
+
*
|
|
87
|
+
* This is what makes a run cohesive without ever handing the user a dataflow
|
|
88
|
+
* map: it discovers that a project has an id by looking at one, the same way a
|
|
89
|
+
* person reads it off the screen.
|
|
90
|
+
*/
|
|
91
|
+
export const rememberIds = (body, into, depth = 0) => {
|
|
92
|
+
if (depth > 4 || body === null || typeof body !== 'object')
|
|
93
|
+
return into;
|
|
94
|
+
if (Array.isArray(body)) {
|
|
95
|
+
for (const item of body.slice(0, 5))
|
|
96
|
+
rememberIds(item, into, depth + 1);
|
|
97
|
+
return into;
|
|
98
|
+
}
|
|
99
|
+
for (const [key, value] of Object.entries(body)) {
|
|
100
|
+
if (typeof value === 'string' && MEMORABLE.test(key) && value.length < 200) {
|
|
101
|
+
into[key] = value;
|
|
102
|
+
}
|
|
103
|
+
else if (typeof value === 'object') {
|
|
104
|
+
rememberIds(value, into, depth + 1);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return into;
|
|
108
|
+
};
|
|
109
|
+
const personaInstructions = (persona, profile, catalogue, agents, fixtures) => [
|
|
110
|
+
`You are a real person using a product through its API. Stay in character; you are the user, not the system.`,
|
|
111
|
+
`Your name is ${persona.name}.`,
|
|
112
|
+
persona.jobTitle ? `Your role: ${persona.jobTitle}.` : '',
|
|
113
|
+
persona.personality ? `Your manner: ${persona.personality}.` : '',
|
|
114
|
+
'',
|
|
115
|
+
profile.instructions,
|
|
116
|
+
'',
|
|
117
|
+
`Each turn you take exactly ONE action:`,
|
|
118
|
+
`- describe: read an endpoint's schema. You MUST describe an endpoint before you may call it.`,
|
|
119
|
+
`- call: invoke an endpoint with arguments that fit its schema. Put them in 'args' as a JSON object written as a string, e.g. "{\\"id\\":\\"abc\\"}".`,
|
|
120
|
+
agents.length
|
|
121
|
+
? `- talkTo: ask one of the product's assistants to do something for you in plain language.`
|
|
122
|
+
: '',
|
|
123
|
+
fixtures.length ? `- upload: send one of your files.` : '',
|
|
124
|
+
`- complete: you got what you came for.`,
|
|
125
|
+
`- stuck: you cannot get there, and you can say why.`,
|
|
126
|
+
'',
|
|
127
|
+
agents.length
|
|
128
|
+
? `Assistants you can talk to:\n${agents
|
|
129
|
+
.map((agent) => `- ${agent.name}${agent.description ? `: ${agent.description}` : ''}`)
|
|
130
|
+
.join('\n')}`
|
|
131
|
+
: '',
|
|
132
|
+
fixtures.length ? `Files you have: ${fixtures.join(', ')}` : '',
|
|
133
|
+
'',
|
|
134
|
+
`The endpoints available to you, as name(inputs) -> outputs:`,
|
|
135
|
+
catalogue,
|
|
136
|
+
]
|
|
137
|
+
.filter(Boolean)
|
|
138
|
+
.join('\n');
|
|
139
|
+
/**
|
|
140
|
+
* Run one virtual user against a live target until its budget runs out.
|
|
141
|
+
*
|
|
142
|
+
* The engine schedules which intent is live and the model decides what to do
|
|
143
|
+
* within it — that split is what lets a run be replayed from its seed while the
|
|
144
|
+
* work inside it stays genuinely open-ended.
|
|
145
|
+
*/
|
|
146
|
+
export const runVirtualUser = async (params) => {
|
|
147
|
+
const { persona, personaId, disposition = 'realistic', tuning, target, llm, model, budget, stop, validateOutput, classify, fixtures = [], agents = [], scopes, allowApprovalRequired = false, maxStepsPerIntent = DEFAULT_MAX_STEPS_PER_INTENT, } = params;
|
|
148
|
+
const profile = dispositionProfile(disposition, tuning);
|
|
149
|
+
const seed = params.seed ?? Math.floor(Math.random() * 2 ** 31);
|
|
150
|
+
const rng = createRng(seed);
|
|
151
|
+
const startedAt = Date.now();
|
|
152
|
+
// An adversarial user is offered the whole surface on purpose — probing what
|
|
153
|
+
// it should not reach is the behaviour under test — while `scopes` stays live
|
|
154
|
+
// below as the oracle that says a success there was a finding.
|
|
155
|
+
const reachable = reachableCatalogue(params.catalogue, {
|
|
156
|
+
readOnly: profile.readOnly,
|
|
157
|
+
allowApprovalRequired,
|
|
158
|
+
scopes: profile.invertedOracle ? undefined : scopes,
|
|
159
|
+
});
|
|
160
|
+
const index = catalogueIndex(reachable);
|
|
161
|
+
const sources = [
|
|
162
|
+
...intentsForPersona(params.intents, personaId),
|
|
163
|
+
...(params.goals ?? []).map((goal, i) => ({
|
|
164
|
+
id: `goal_${i + 1}`,
|
|
165
|
+
title: goal,
|
|
166
|
+
})),
|
|
167
|
+
];
|
|
168
|
+
const stack = new IntentStack(sources, rng, profile);
|
|
169
|
+
const memory = profile.emptyMemory
|
|
170
|
+
? {}
|
|
171
|
+
: { ...(params.memory ?? {}) };
|
|
172
|
+
const described = new Set();
|
|
173
|
+
const findings = [];
|
|
174
|
+
const steps = [];
|
|
175
|
+
const history = new Map();
|
|
176
|
+
const tally = {
|
|
177
|
+
steps: 0,
|
|
178
|
+
calls: 0,
|
|
179
|
+
mutations: 0,
|
|
180
|
+
tokensIn: 0,
|
|
181
|
+
tokensOut: 0,
|
|
182
|
+
model,
|
|
183
|
+
elapsedMs: 0,
|
|
184
|
+
findings: 0,
|
|
185
|
+
};
|
|
186
|
+
const maxSteps = budget?.steps ?? DEFAULT_MAX_STEPS;
|
|
187
|
+
const maxDurationMs = budget?.duration === undefined
|
|
188
|
+
? undefined
|
|
189
|
+
: typeof budget.duration === 'number'
|
|
190
|
+
? budget.duration
|
|
191
|
+
: getDurationInMilliseconds(budget.duration);
|
|
192
|
+
const instructions = personaInstructions(persona, profile, renderCatalogue(reachable), agents, fixtures);
|
|
193
|
+
const addFinding = (finding) => {
|
|
194
|
+
findings.push(finding);
|
|
195
|
+
tally.findings++;
|
|
196
|
+
};
|
|
197
|
+
// Every other exit sets this and breaks; running the loop out means the step
|
|
198
|
+
// budget is what ended it.
|
|
199
|
+
let stoppedBy = 'budget-steps';
|
|
200
|
+
for (let step = 0; step < maxSteps; step++) {
|
|
201
|
+
tally.elapsedMs = Date.now() - startedAt;
|
|
202
|
+
if (maxDurationMs !== undefined && tally.elapsedMs >= maxDurationMs) {
|
|
203
|
+
stoppedBy = 'budget-duration';
|
|
204
|
+
break;
|
|
205
|
+
}
|
|
206
|
+
if (budget?.mutations !== undefined && tally.mutations >= budget.mutations) {
|
|
207
|
+
stoppedBy = 'budget-mutations';
|
|
208
|
+
break;
|
|
209
|
+
}
|
|
210
|
+
if (stop && (await stop({ ...tally }))) {
|
|
211
|
+
stoppedBy = 'stop-hook';
|
|
212
|
+
break;
|
|
213
|
+
}
|
|
214
|
+
const tick = stack.next(step);
|
|
215
|
+
if (!tick) {
|
|
216
|
+
// Nothing left to schedule: either this user was given no intents at all,
|
|
217
|
+
// or it saw every one of them through.
|
|
218
|
+
stoppedBy = stack.records().length === 0 ? 'no-intents' : 'exhausted';
|
|
219
|
+
break;
|
|
220
|
+
}
|
|
221
|
+
// A user who cannot get anywhere gives up rather than looping forever.
|
|
222
|
+
if (tick.intent.steps.length > maxStepsPerIntent) {
|
|
223
|
+
stack.stuck('gave up after going in circles');
|
|
224
|
+
continue;
|
|
225
|
+
}
|
|
226
|
+
const intentId = tick.intent.id;
|
|
227
|
+
if (!history.has(intentId)) {
|
|
228
|
+
const source = tick.intent.source;
|
|
229
|
+
history.set(intentId, [
|
|
230
|
+
msg('user', [
|
|
231
|
+
`What you want to do right now: ${source.title}`,
|
|
232
|
+
source.description ?? '',
|
|
233
|
+
source.steps?.length
|
|
234
|
+
? `Roughly how it goes:\n${source.steps.map((s) => `- ${s}`).join('\n')}`
|
|
235
|
+
: '',
|
|
236
|
+
`Work it out through the API yourself — nobody has told you which endpoints do this.`,
|
|
237
|
+
]
|
|
238
|
+
.filter(Boolean)
|
|
239
|
+
.join('\n')),
|
|
240
|
+
]);
|
|
241
|
+
}
|
|
242
|
+
const messages = history.get(intentId);
|
|
243
|
+
if (tick.move === 'resume' || tick.move === 'suspend') {
|
|
244
|
+
messages.push(msg('user', `You were pulled away and have come back to this. Things may have changed since.`));
|
|
245
|
+
}
|
|
246
|
+
// Whether this user trusts its notes this turn or goes and looks again.
|
|
247
|
+
// `stale` almost always trusts them, a newcomer has none to trust, and an
|
|
248
|
+
// auditor checks nearly everything — which is the whole difference between
|
|
249
|
+
// those runs, expressed as one roll rather than as prose.
|
|
250
|
+
if (Object.keys(memory).length && !rng.chance(profile.reReadRate)) {
|
|
251
|
+
messages.push(msg('user', `Things you have noted down: ${Object.entries(memory)
|
|
252
|
+
.slice(-25)
|
|
253
|
+
.map(([key, value]) => `${key}=${value}`)
|
|
254
|
+
.join(', ')}`));
|
|
255
|
+
}
|
|
256
|
+
else if (Object.keys(memory).length) {
|
|
257
|
+
messages.push(msg('user', `You are not sure your notes are current. Look things up.`));
|
|
258
|
+
}
|
|
259
|
+
const result = await llm({
|
|
260
|
+
model,
|
|
261
|
+
temperature: profile.temperature,
|
|
262
|
+
instructions,
|
|
263
|
+
messages,
|
|
264
|
+
tools: [],
|
|
265
|
+
maxSteps: 1,
|
|
266
|
+
toolChoice: 'none',
|
|
267
|
+
outputSchema: ACTION_SCHEMA,
|
|
268
|
+
});
|
|
269
|
+
tally.steps++;
|
|
270
|
+
tally.tokensIn += result.usage?.inputTokens ?? 0;
|
|
271
|
+
tally.tokensOut += result.usage?.outputTokens ?? 0;
|
|
272
|
+
const raw = readObject(result);
|
|
273
|
+
const record = {
|
|
274
|
+
index: step,
|
|
275
|
+
intentId,
|
|
276
|
+
action: { kind: 'invalid', detail: 'no action produced' },
|
|
277
|
+
tokensIn: result.usage?.inputTokens ?? 0,
|
|
278
|
+
tokensOut: result.usage?.outputTokens ?? 0,
|
|
279
|
+
};
|
|
280
|
+
if (!raw || typeof raw.kind !== 'string') {
|
|
281
|
+
steps.push(record);
|
|
282
|
+
messages.push(msg('user', 'That was not a valid action. Choose one action.'));
|
|
283
|
+
continue;
|
|
284
|
+
}
|
|
285
|
+
const action = { ...raw, kind: raw.kind };
|
|
286
|
+
record.action = action;
|
|
287
|
+
messages.push(msg('assistant', JSON.stringify(raw)));
|
|
288
|
+
/** Tell the user what happened, so the next turn is informed. */
|
|
289
|
+
const reply = (text) => messages.push(msg('user', text));
|
|
290
|
+
if (action.kind === 'complete') {
|
|
291
|
+
stack.complete(action.summary);
|
|
292
|
+
steps.push(record);
|
|
293
|
+
continue;
|
|
294
|
+
}
|
|
295
|
+
if (action.kind === 'stuck') {
|
|
296
|
+
stack.stuck(action.reason);
|
|
297
|
+
steps.push(record);
|
|
298
|
+
continue;
|
|
299
|
+
}
|
|
300
|
+
if (action.kind === 'describe') {
|
|
301
|
+
const entry = catalogueLookup(index, action.rpcName);
|
|
302
|
+
if (!entry) {
|
|
303
|
+
reply(`There is no endpoint called '${action.rpcName}'.`);
|
|
304
|
+
steps.push(record);
|
|
305
|
+
continue;
|
|
306
|
+
}
|
|
307
|
+
described.add(entry.name);
|
|
308
|
+
// Saying the gate is now open matters: reading a schema is free and
|
|
309
|
+
// calling is not, so a model left to infer it will happily spend a whole
|
|
310
|
+
// budget describing one endpoint after another and never touch the app.
|
|
311
|
+
reply(`${JSON.stringify(describeEntry(entry))}\nYou have read this one, so you may now call '${entry.name}'.`);
|
|
312
|
+
steps.push(record);
|
|
313
|
+
continue;
|
|
314
|
+
}
|
|
315
|
+
if (action.kind === 'talkTo') {
|
|
316
|
+
if (!target.talkTo) {
|
|
317
|
+
reply('There is nobody to talk to here.');
|
|
318
|
+
steps.push(record);
|
|
319
|
+
continue;
|
|
320
|
+
}
|
|
321
|
+
const verdict = await target.talkTo(action.agent, action.task);
|
|
322
|
+
record.ok = verdict.passed;
|
|
323
|
+
record.response = verdict.reasoning.slice(0, RESPONSE_EXCERPT);
|
|
324
|
+
reply(`You spoke to ${action.agent}. ${verdict.passed ? 'It sorted it out.' : 'It did not.'} ${verdict.reasoning}`);
|
|
325
|
+
steps.push(record);
|
|
326
|
+
continue;
|
|
327
|
+
}
|
|
328
|
+
if (action.kind === 'upload') {
|
|
329
|
+
if (!target.upload) {
|
|
330
|
+
reply('You have nowhere to send a file.');
|
|
331
|
+
steps.push(record);
|
|
332
|
+
continue;
|
|
333
|
+
}
|
|
334
|
+
const response = await target.upload(action.file);
|
|
335
|
+
record.status = response.status;
|
|
336
|
+
record.ok = response.ok;
|
|
337
|
+
record.response = response.serialized.slice(0, RESPONSE_EXCERPT);
|
|
338
|
+
if (response.status >= 500) {
|
|
339
|
+
record.findingKinds = ['server-error'];
|
|
340
|
+
addFinding({
|
|
341
|
+
kind: 'server-error',
|
|
342
|
+
detail: `upload '${action.file}' returned ${response.status}`,
|
|
343
|
+
status: response.status,
|
|
344
|
+
intentId,
|
|
345
|
+
step,
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
reply(`Upload came back ${response.status}: ${record.response}`);
|
|
349
|
+
steps.push(record);
|
|
350
|
+
continue;
|
|
351
|
+
}
|
|
352
|
+
// Everything below is a call.
|
|
353
|
+
const entry = catalogueLookup(index, action.rpcName);
|
|
354
|
+
if (!entry) {
|
|
355
|
+
reply(`There is no endpoint called '${action.rpcName}'.`);
|
|
356
|
+
steps.push(record);
|
|
357
|
+
continue;
|
|
358
|
+
}
|
|
359
|
+
if (!described.has(entry.name)) {
|
|
360
|
+
// Enforced rather than merely asked for: guessing field names is
|
|
361
|
+
// unproductive fuzz that turns every 400 into a typo. Handing back the
|
|
362
|
+
// schema makes this self-correcting in one turn.
|
|
363
|
+
reply(`You do not know this endpoint's fields yet. Its schema is: ${JSON.stringify(describeEntry(entry))}`);
|
|
364
|
+
described.add(entry.name);
|
|
365
|
+
steps.push(record);
|
|
366
|
+
continue;
|
|
367
|
+
}
|
|
368
|
+
// What may be called is decided once, when the catalogue is narrowed: a
|
|
369
|
+
// read-only disposition is never offered a mutation and an approval-gated
|
|
370
|
+
// endpoint is never offered at all, so `index` is the gate and there is no
|
|
371
|
+
// second one here to drift out of step with it.
|
|
372
|
+
const mutating = !isReadOnly(entry);
|
|
373
|
+
const args = readArgs(action.args);
|
|
374
|
+
const repeats = profile.repeatRate > 0 && rng.chance(profile.repeatRate) ? 2 : 1;
|
|
375
|
+
let lastResponseText = '';
|
|
376
|
+
for (let attempt = 0; attempt < repeats; attempt++) {
|
|
377
|
+
let response;
|
|
378
|
+
try {
|
|
379
|
+
response = await target.call(entry.name, args);
|
|
380
|
+
}
|
|
381
|
+
catch (error) {
|
|
382
|
+
record.findingKinds = [...(record.findingKinds ?? []), 'transport-error'];
|
|
383
|
+
addFinding({
|
|
384
|
+
kind: 'transport-error',
|
|
385
|
+
detail: `${entry.name} threw: ${error.message}`,
|
|
386
|
+
rpcName: entry.name,
|
|
387
|
+
intentId,
|
|
388
|
+
step,
|
|
389
|
+
});
|
|
390
|
+
lastResponseText = `The request failed outright: ${error.message}`;
|
|
391
|
+
break;
|
|
392
|
+
}
|
|
393
|
+
tally.calls++;
|
|
394
|
+
if (mutating)
|
|
395
|
+
tally.mutations++;
|
|
396
|
+
record.status = response.status;
|
|
397
|
+
record.ok = response.ok;
|
|
398
|
+
record.response = response.serialized.slice(0, RESPONSE_EXCERPT);
|
|
399
|
+
lastResponseText = `${response.status}: ${record.response}`;
|
|
400
|
+
const kinds = [];
|
|
401
|
+
if (response.status >= 500) {
|
|
402
|
+
kinds.push('server-error');
|
|
403
|
+
addFinding({
|
|
404
|
+
kind: 'server-error',
|
|
405
|
+
detail: `${entry.name} returned ${response.status}`,
|
|
406
|
+
rpcName: entry.name,
|
|
407
|
+
status: response.status,
|
|
408
|
+
intentId,
|
|
409
|
+
step,
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
if (response.ok && validateOutput) {
|
|
413
|
+
const problem = validateOutput(entry.name, response.body);
|
|
414
|
+
if (problem) {
|
|
415
|
+
kinds.push('schema-violation');
|
|
416
|
+
addFinding({
|
|
417
|
+
kind: 'schema-violation',
|
|
418
|
+
detail: `${entry.name} answered outside its own output schema: ${problem}`,
|
|
419
|
+
rpcName: entry.name,
|
|
420
|
+
status: response.status,
|
|
421
|
+
intentId,
|
|
422
|
+
step,
|
|
423
|
+
});
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
// This persona does not hold the scopes this endpoint declares, and the
|
|
427
|
+
// server served it anyway — the declaration and the enforcement disagree.
|
|
428
|
+
if (response.ok &&
|
|
429
|
+
scopes &&
|
|
430
|
+
entry.scopes?.some((scope) => !scopes.includes(scope))) {
|
|
431
|
+
kinds.push('unexpected-success');
|
|
432
|
+
addFinding({
|
|
433
|
+
kind: 'unexpected-success',
|
|
434
|
+
detail: `${entry.name} succeeded for '${personaId}', who does not hold ${entry.scopes
|
|
435
|
+
.filter((scope) => !scopes.includes(scope))
|
|
436
|
+
.join(', ')}`,
|
|
437
|
+
rpcName: entry.name,
|
|
438
|
+
status: response.status,
|
|
439
|
+
intentId,
|
|
440
|
+
step,
|
|
441
|
+
});
|
|
442
|
+
}
|
|
443
|
+
for (const finding of classify?.({
|
|
444
|
+
rpcName: entry.name,
|
|
445
|
+
args,
|
|
446
|
+
status: response.status,
|
|
447
|
+
ok: response.ok,
|
|
448
|
+
body: response.body,
|
|
449
|
+
intentId,
|
|
450
|
+
step,
|
|
451
|
+
disposition,
|
|
452
|
+
}) ?? []) {
|
|
453
|
+
kinds.push(finding.kind);
|
|
454
|
+
addFinding({ ...finding, step });
|
|
455
|
+
}
|
|
456
|
+
if (kinds.length) {
|
|
457
|
+
record.findingKinds = [...(record.findingKinds ?? []), ...kinds];
|
|
458
|
+
}
|
|
459
|
+
if (response.ok)
|
|
460
|
+
rememberIds(response.body, memory);
|
|
461
|
+
if (attempt + 1 < repeats) {
|
|
462
|
+
lastResponseText += ` (you were not sure it went through, so you sent it again)`;
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
reply(lastResponseText);
|
|
466
|
+
steps.push(record);
|
|
467
|
+
}
|
|
468
|
+
tally.elapsedMs = Date.now() - startedAt;
|
|
469
|
+
return {
|
|
470
|
+
seed,
|
|
471
|
+
tally,
|
|
472
|
+
findings,
|
|
473
|
+
intents: stack.records(),
|
|
474
|
+
steps,
|
|
475
|
+
memory,
|
|
476
|
+
stoppedBy,
|
|
477
|
+
};
|
|
478
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which of the product's agents a persona can actually talk to.
|
|
3
|
+
*
|
|
4
|
+
* An agent is not something a persona declares. It is something a persona
|
|
5
|
+
* *reaches*, under exactly the rule an RPC is reached by: `CoreAIAgent.scopes`
|
|
6
|
+
* is checked against the session, so a persona holding the scopes an agent
|
|
7
|
+
* requires finds it in front of them, and one that does not, does not.
|
|
8
|
+
*
|
|
9
|
+
* That is the whole reason a persona has no `agent:` field. Naming one would
|
|
10
|
+
* add no capability the roles do not already confer, and would narrow the
|
|
11
|
+
* persona to a single brain when the interesting behaviour is the choice
|
|
12
|
+
* between calling the API directly and handing the work to a specialist.
|
|
13
|
+
*/
|
|
14
|
+
/** The part of an agent's meta this needs. */
|
|
15
|
+
export interface AgentReachability {
|
|
16
|
+
name?: string;
|
|
17
|
+
description?: string;
|
|
18
|
+
scopes?: readonly string[];
|
|
19
|
+
auth?: boolean;
|
|
20
|
+
}
|
|
21
|
+
/** One agent, as it is offered to a virtual user. */
|
|
22
|
+
export interface ReachableAgent {
|
|
23
|
+
name: string;
|
|
24
|
+
description?: string;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* The agents to offer, keyed by the name they are declared under.
|
|
28
|
+
*
|
|
29
|
+
* Like {@link reachableCatalogue}, this narrows *what is offered* and never
|
|
30
|
+
* what is enforced: the server decides who may talk to what, and an agent
|
|
31
|
+
* answering a persona this filter would have withheld is a finding rather than
|
|
32
|
+
* a bug here.
|
|
33
|
+
*
|
|
34
|
+
* An agent declaring no scopes is offered to everybody, on the same terms as a
|
|
35
|
+
* function that gates itself with none — undeclared is not the same as denied,
|
|
36
|
+
* and inventing a denial here would hide the endpoints that really are open.
|
|
37
|
+
*/
|
|
38
|
+
export declare const reachableAgents: (agents: Readonly<Record<string, AgentReachability>>, scopes?: readonly string[]) => ReachableAgent[];
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { hasScopes } from '../../scopes.js';
|
|
2
|
+
/**
|
|
3
|
+
* The agents to offer, keyed by the name they are declared under.
|
|
4
|
+
*
|
|
5
|
+
* Like {@link reachableCatalogue}, this narrows *what is offered* and never
|
|
6
|
+
* what is enforced: the server decides who may talk to what, and an agent
|
|
7
|
+
* answering a persona this filter would have withheld is a finding rather than
|
|
8
|
+
* a bug here.
|
|
9
|
+
*
|
|
10
|
+
* An agent declaring no scopes is offered to everybody, on the same terms as a
|
|
11
|
+
* function that gates itself with none — undeclared is not the same as denied,
|
|
12
|
+
* and inventing a denial here would hide the endpoints that really are open.
|
|
13
|
+
*/
|
|
14
|
+
export const reachableAgents = (agents, scopes) => Object.entries(agents)
|
|
15
|
+
.filter(([, agent]) => {
|
|
16
|
+
if (!scopes || !agent.scopes?.length) {
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
return hasScopes(agent.scopes, scopes);
|
|
20
|
+
})
|
|
21
|
+
.map(([id, agent]) => ({
|
|
22
|
+
name: agent.name ?? id,
|
|
23
|
+
...(agent.description ? { description: agent.description } : {}),
|
|
24
|
+
}));
|