@pikku/core 0.12.71 → 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 +848 -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 +40 -30
- 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 -11
- package/dist/handle-error.js +6 -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 +7 -17
- package/dist/schema.js +30 -18
- 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 +1 -100
- package/dist/wirings/http/http-runner.js +12 -166
- package/dist/wirings/http/http.types.d.ts +16 -55
- package/dist/wirings/http/index.d.ts +2 -1
- package/dist/wirings/http/index.js +1 -1
- 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 +6 -36
- package/dist/wirings/http/pikku-fetch-http-request.js +56 -50
- 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 +2 -0
- package/dist/wirings/mcp/mcp-runner.js +6 -16
- 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 -2
- 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 +75 -7
- package/src/function/function-runner.ts +73 -30
- 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/gopass-secrets-removed.test.ts +51 -0
- package/src/handle-error.test.ts +108 -1
- package/src/handle-error.ts +8 -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.test.ts +103 -0
- package/src/schema.ts +35 -18
- 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 +12 -168
- package/src/wirings/http/http.types.ts +17 -62
- package/src/wirings/http/index.ts +5 -1
- package/src/wirings/http/log-http-routes.ts +0 -4
- package/src/wirings/http/pikku-fetch-http-request.test.ts +88 -7
- package/src/wirings/http/pikku-fetch-http-request.ts +74 -50
- 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.test.ts +40 -0
- package/src/wirings/mcp/mcp-runner.ts +9 -15
- 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/services/gopass-secrets.ts +0 -78
- 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,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
|
+
}));
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import type { ApiCatalogueEntry } from './virtual-user.types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Whether a call only reads.
|
|
4
|
+
*
|
|
5
|
+
* An explicit annotation always wins. Without one, the name decides — because
|
|
6
|
+
* an unannotated function means "nobody said", not "safe", and silently
|
|
7
|
+
* treating 395 unannotated endpoints as read-only is how a virtual user
|
|
8
|
+
* deploys something. {@link catalogueClassification} reports how much of the
|
|
9
|
+
* catalogue rests on this guess so the gap is visible rather than assumed.
|
|
10
|
+
*/
|
|
11
|
+
export declare const isReadOnly: (entry: ApiCatalogueEntry) => boolean;
|
|
12
|
+
/** How much of a catalogue's read/write split is annotation and how much is guess. */
|
|
13
|
+
export declare const catalogueClassification: (entries: readonly ApiCatalogueEntry[]) => {
|
|
14
|
+
total: number;
|
|
15
|
+
annotated: number;
|
|
16
|
+
inferred: number;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* The whole reachable API as one compact index, for the instructions.
|
|
20
|
+
*
|
|
21
|
+
* Every endpoint is listed rather than retrieved against. Measured on a 430-RPC
|
|
22
|
+
* project this is ~8k tokens, cached for the life of the run — cheaper than any
|
|
23
|
+
* retrieval step, and it hides nothing. A ranking function would make the
|
|
24
|
+
* virtual user only as adventurous as the ranking, which loses exactly the
|
|
25
|
+
* endpoints worth stumbling into.
|
|
26
|
+
*/
|
|
27
|
+
export declare const renderCatalogue: (entries: readonly ApiCatalogueEntry[]) => string;
|
|
28
|
+
/**
|
|
29
|
+
* The entries a given user may reach: approval-gated calls dropped unless the
|
|
30
|
+
* run opted in, mutations dropped for a read-only disposition, and anything the
|
|
31
|
+
* actor's tier cannot satisfy dropped when the caller supplied one.
|
|
32
|
+
*
|
|
33
|
+
* This narrows *what is offered*, never what is enforced — the server is the
|
|
34
|
+
* only thing that decides who may call what, and a call this filter would have
|
|
35
|
+
* denied still going through is a finding rather than a bug in the filter.
|
|
36
|
+
*/
|
|
37
|
+
export declare const reachableCatalogue: (entries: readonly ApiCatalogueEntry[], { readOnly, allowApprovalRequired, scopes, }?: {
|
|
38
|
+
readOnly?: boolean;
|
|
39
|
+
allowApprovalRequired?: boolean;
|
|
40
|
+
/**
|
|
41
|
+
* The scopes this persona holds, resolved from its roles. Omit to keep
|
|
42
|
+
* every entry.
|
|
43
|
+
*
|
|
44
|
+
* Scopes rather than permission-function names: a function's `scopes` are
|
|
45
|
+
* declared and checkable, whereas a permission is arbitrary code whose
|
|
46
|
+
* verdict nobody can predict without running it. Narrowing by something
|
|
47
|
+
* unpredictable produced a catalogue that was wrong in both directions.
|
|
48
|
+
*/
|
|
49
|
+
scopes?: readonly string[];
|
|
50
|
+
}) => ApiCatalogueEntry[];
|
|
51
|
+
/**
|
|
52
|
+
* Endpoints this persona is *not* entitled to reach.
|
|
53
|
+
*
|
|
54
|
+
* The adversarial oracle. Withheld from the catalogue, so reaching one takes
|
|
55
|
+
* initiative — and a 2xx from one is the finding, because the server is the
|
|
56
|
+
* only thing that decides who may call what.
|
|
57
|
+
*/
|
|
58
|
+
export declare const unreachableCatalogue: (entries: readonly ApiCatalogueEntry[], scopes: readonly string[]) => ApiCatalogueEntry[];
|
|
59
|
+
/** Index a catalogue by name for the engine's lookups. */
|
|
60
|
+
export declare const catalogueIndex: (entries: readonly ApiCatalogueEntry[]) => Map<string, ApiCatalogueEntry>;
|
|
61
|
+
/**
|
|
62
|
+
* Find an entry by the name a model gave, forgiving the signature it just read.
|
|
63
|
+
*
|
|
64
|
+
* The catalogue renders as `name(inputs) -> outputs`, and a model that has the
|
|
65
|
+
* line in front of it will sometimes hand the whole line back — `whoAmI() ->
|
|
66
|
+
* userId,scopes` instead of `whoAmI`. Measured on a live run, 8 of 24 steps
|
|
67
|
+
* went that way and every one of them was answered with "there is no endpoint
|
|
68
|
+
* called that", which is true and useless.
|
|
69
|
+
*
|
|
70
|
+
* Forgiving it is not the same as guessing: an rpc name never contains a
|
|
71
|
+
* bracket, so everything from the first one is the signature the catalogue
|
|
72
|
+
* printed, and taking it off is unambiguous.
|
|
73
|
+
*/
|
|
74
|
+
export declare const catalogueLookup: (index: Map<string, ApiCatalogueEntry>, rpcName: string) => ApiCatalogueEntry | undefined;
|
|
75
|
+
/**
|
|
76
|
+
* What `describe` hands back: the full schemas, so the next call can be made
|
|
77
|
+
* correctly rather than guessed at.
|
|
78
|
+
*
|
|
79
|
+
* Guessing field names is unproductive fuzz — every 400 becomes a typo and
|
|
80
|
+
* nothing is learned from any of them. Reading the schema first is also what a
|
|
81
|
+
* person does, since the form tells them the fields. It makes a subsequent 400
|
|
82
|
+
* mean something: either the schema and the implementation disagree, or the
|
|
83
|
+
* misuse is genuine and worth seeing.
|
|
84
|
+
*/
|
|
85
|
+
export declare const describeEntry: (entry: ApiCatalogueEntry) => {
|
|
86
|
+
name: string;
|
|
87
|
+
description: string | undefined;
|
|
88
|
+
readonly: boolean;
|
|
89
|
+
approvalRequired: boolean;
|
|
90
|
+
input: Record<string, unknown>;
|
|
91
|
+
output: Record<string, unknown> | null;
|
|
92
|
+
};
|