@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
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,1174 @@
|
|
|
1
|
+
## 0.12.77
|
|
2
|
+
|
|
3
|
+
### Patch Changes
|
|
4
|
+
|
|
5
|
+
- 3df4f95: Scaffold virtual user runs as RPCs, backed by a run store.
|
|
6
|
+
|
|
7
|
+
`pikku persona run` could already turn a declared persona loose on a running
|
|
8
|
+
stage, but only from a terminal, and the result only existed in that terminal's
|
|
9
|
+
output. There was no way for CI, a console, or a scheduled job to start a run —
|
|
10
|
+
and nothing kept what a run found, so this week's findings could not be compared
|
|
11
|
+
against last week's.
|
|
12
|
+
|
|
13
|
+
`scaffold.virtualUser` now generates two RPCs and the function behind them:
|
|
14
|
+
- `runVirtualUser({ persona, goals?, memory?, disposition?, budget?, seed? })
|
|
15
|
+
-> { runId }`
|
|
16
|
+
- `getVirtualUserRun({ runId }) -> { status, findings, tally, memory, … }`
|
|
17
|
+
|
|
18
|
+
They are gated on separate scopes — `virtualUser:run` and `virtualUser:read` —
|
|
19
|
+
because an adversarial run's findings are working exploits carrying live ids,
|
|
20
|
+
which makes reading them the more sensitive of the two. Production refuses every
|
|
21
|
+
disposition but `accountable`, checked against the effective one so the
|
|
22
|
+
per-run override cannot smuggle another in.
|
|
23
|
+
|
|
24
|
+
**A run is not a workflow and not a queued job.** It explores, so no two
|
|
25
|
+
attempts take the same steps and there is nothing to replay; and the record
|
|
26
|
+
already carries the progress a queue would only be holding on the way to the
|
|
27
|
+
same place. `runVirtualUser` writes the record, dispatches without awaiting, and
|
|
28
|
+
returns the id. The cost is stated on the type: a restart mid-run strands a
|
|
29
|
+
record at `running`, so a run older than its budget window and still `running`
|
|
30
|
+
is dead rather than working.
|
|
31
|
+
|
|
32
|
+
`@pikku/core` gains `VirtualUserRunStore` (with `virtualUserRunStore` on
|
|
33
|
+
`CoreSingletonServices`), and `@pikku/kysely` ships
|
|
34
|
+
`KyselyVirtualUserRunStore`, which creates its own table on first use like the
|
|
35
|
+
audit sink — the runtime never needs it, so it arrives with the feature that
|
|
36
|
+
fills it rather than in every database.
|
|
37
|
+
|
|
38
|
+
Also in core: `prepareVirtualUserRun`, which derives the catalogue, intents,
|
|
39
|
+
scopes and reachable agents in one place. `pikku persona run` reads the
|
|
40
|
+
inspector state and the generated RPC reads `metaService`, and the two have to
|
|
41
|
+
agree — otherwise the same persona and seed explore a different API depending on
|
|
42
|
+
how the run was started. `personaScopes` moved here from the CLI for the same
|
|
43
|
+
reason and is still re-exported from its old home. `PRODUCTION_DISPOSITION` is
|
|
44
|
+
now exported from `@pikku/core/virtual-user`, which it should always have been.
|
|
45
|
+
|
|
46
|
+
## 0.12.76
|
|
47
|
+
|
|
48
|
+
### Patch Changes
|
|
49
|
+
|
|
50
|
+
- 62ea4cc: The audit trail is now readable — in the generated meta, through an RPC, and as
|
|
51
|
+
a page in the console.
|
|
52
|
+
|
|
53
|
+
`audit: true` reaches `FunctionRuntimeMeta.audit` as its resolved form
|
|
54
|
+
(`{ durability }`), so which functions record anything is answerable without
|
|
55
|
+
running them. It is informational: the runner still resolves audit from the live
|
|
56
|
+
function config, so meta and runtime cannot disagree.
|
|
57
|
+
|
|
58
|
+
`AuditService` grows an optional read side — `query(AuditQuery)` and `facets()`.
|
|
59
|
+
Optional because a sink can legitimately be write-only: a queue producer that
|
|
60
|
+
hands events to another system has nothing to read back, and a reader that finds
|
|
61
|
+
these absent should say the trail is not readable here rather than that it is
|
|
62
|
+
empty. The two are very different answers to give someone auditing a system.
|
|
63
|
+
|
|
64
|
+
`KyselyAuditService` implements both, newest first with offset paging, filtered
|
|
65
|
+
by user, action and time window. Two things it now gets right that are easy to
|
|
66
|
+
get wrong: an empty filter array means "match nothing" rather than "no filter",
|
|
67
|
+
and results are read by physical _and_ camelCase key, because `CamelCasePlugin`
|
|
68
|
+
is on most pikku Kysely instances and renames result keys on the way out — the
|
|
69
|
+
mismatch does not throw, it returns a page of `undefined`. `init()` creates the
|
|
70
|
+
`audit` table for projects that do not migrate it themselves, from a new
|
|
71
|
+
exported `auditSchema` that stays out of `pikkuSchemas` because the runtime does
|
|
72
|
+
not need it.
|
|
73
|
+
|
|
74
|
+
The console addon exposes `console:getAudits` and `console:getAuditFilters`
|
|
75
|
+
behind a new `pikku:audit:read` scope, and forwards the application's `audit`
|
|
76
|
+
service into the addon's own services — without that last part every install
|
|
77
|
+
reported the trail as unreadable, whatever sink it had configured.
|
|
78
|
+
|
|
79
|
+
The console gets an Audit trail page: an infinite list filtered server-side by
|
|
80
|
+
user and action, and a row that opens the whole event, metadata rendered as a
|
|
81
|
+
JSON tree. Refused, unreadable and empty are three different screens, because
|
|
82
|
+
"you may not read this", "nobody can read this" and "nothing happened" are three
|
|
83
|
+
different facts.
|
|
84
|
+
|
|
85
|
+
Events name the person who caused them. The trail records a user id — the only
|
|
86
|
+
thing stable enough to record, since a name can change after the event — so
|
|
87
|
+
`getAudits` resolves those ids against better-auth's user directory at read
|
|
88
|
+
time, and the page shows the name while keeping the recorded id on the event.
|
|
89
|
+
The filter follows: pick a colleague by name, filter by the id. A scenario
|
|
90
|
+
actor is labelled as one, so synthetic traffic is not mistaken for real, and a
|
|
91
|
+
caller who was signed out shows the wire identity pikku resolved for them
|
|
92
|
+
rather than being credited to the system.
|
|
93
|
+
|
|
94
|
+
**Breaking, for anyone already reading `AuditEvent`:** `actor` is now
|
|
95
|
+
`userIdentity`, and its type `AuditActor` is `AuditUserIdentity`; `AuditQuery`
|
|
96
|
+
takes `userIds`/`orgId` in place of `actorUserIds`/`actorOrgId`, and
|
|
97
|
+
`AuditFacets` returns `userIds`. In pikku an _actor_ is a synthetic person a
|
|
98
|
+
scenario drives, flagged on the user row — so naming the causer of an event
|
|
99
|
+
`actor` made the synthetic case unsayable (`actor.actor === true`) and implied
|
|
100
|
+
every recorded action was a test. The overwhelming majority are ordinary
|
|
101
|
+
customers. The `audit` table follows: `actor_user_id` / `actor_org_id`
|
|
102
|
+
are now `user_id` / `org_id`, and a `pikku_user_id` column joins them so the
|
|
103
|
+
wire identity of a caller who never signed in survives the round trip — the
|
|
104
|
+
sink was dropping it, which left the console's Session field permanently
|
|
105
|
+
blank. A project that already migrated the table needs to rename the two
|
|
106
|
+
columns and add the third; `KyselyAuditService.init()` creates the new shape
|
|
107
|
+
for anyone who did not.
|
|
108
|
+
|
|
109
|
+
- 9dddff8: Split a column's at-rest form out of its classification.
|
|
110
|
+
|
|
111
|
+
`security: 'encrypted'` sat beside `'secret'` as though the two were
|
|
112
|
+
alternatives, which made the field unanswerable: a token hash and a live bearer
|
|
113
|
+
token are both secret, one must never be encrypted — the digest _is_ the lookup
|
|
114
|
+
key — and the other must always be. A column now carries a second, independent
|
|
115
|
+
`form: 'plain' | 'hashed' | 'wrapped' | 'sealed'` saying how the bytes are held.
|
|
116
|
+
|
|
117
|
+
Declaring a form other than `plain` makes the column's INSERT/UPDATE type
|
|
118
|
+
nominal — `WrappedValue`, `SealedValue`, `HashedValue` — so a plain string no
|
|
119
|
+
longer compiles there and the only way to write the column is with something an
|
|
120
|
+
encrypt, seal or hash call produced. `envelopeEncrypt`, `envelopeRewrap` and
|
|
121
|
+
`wrapDEK` now return the brand, and a new `hashToken` produces `HashedValue`, so
|
|
122
|
+
the round trip needs no casts; `column-form.ts` exports deliberately-named
|
|
123
|
+
`unsafeAs*` assertions for backfills, fixtures and values sealed elsewhere.
|
|
124
|
+
Reads are unaffected — the brands widen to `string` and compose with the
|
|
125
|
+
classification brand as `Secret<WrappedValue>`.
|
|
126
|
+
|
|
127
|
+
`wrapped` and `sealed` stay distinct because a sealed value is one the
|
|
128
|
+
application cannot read back; storing one where the other belongs is a row
|
|
129
|
+
nobody can open.
|
|
130
|
+
|
|
131
|
+
A `secret` column that has not declared a form now warns (PKU483), and a form on
|
|
132
|
+
a non-text column warns and is dropped (PKU484). Both are warnings, so existing
|
|
133
|
+
projects keep migrating — `pikku db --fail-on-warn` opts into the ratchet, and
|
|
134
|
+
an explicit `form: 'plain'` is the acknowledgement that silences it. The legacy
|
|
135
|
+
`security: 'encrypted'` keeps working and now expands to the pair it always
|
|
136
|
+
meant, `secret` + `wrapped`.
|
|
137
|
+
|
|
138
|
+
- 78b29f0: `SecretService` now returns a `SecretValue<T>` rather than the bare value, so a
|
|
139
|
+
vault secret cannot reach a sink by accident.
|
|
140
|
+
|
|
141
|
+
`SecretValue` is nominally typed, which means it is not assignable to `string`
|
|
142
|
+
(or to any other concretely-typed field). Every sink with a real type — a
|
|
143
|
+
database column, an email body, a session payload — rejects it with no lint
|
|
144
|
+
rule involved. The sinks typed `any`, `unknown`, or a free generic — the logger,
|
|
145
|
+
queue payloads, webhook and email inputs, and a function's own output — are
|
|
146
|
+
guarded with `Safe<T>`, which collapses a `SecretValue` found anywhere inside
|
|
147
|
+
`T`, however deeply nested, to `never`.
|
|
148
|
+
|
|
149
|
+
Unwrap deliberately at the point the secret reaches the wire:
|
|
150
|
+
|
|
151
|
+
```ts
|
|
152
|
+
const secret = await secrets.getSecret('BETTER_AUTH_SECRET')
|
|
153
|
+
betterAuth({ secret: secret.reveal() })
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
Two behaviours cover what types cannot see. Structured serialization redacts —
|
|
157
|
+
`JSON.stringify` and node's inspect both yield `[secret]`, so an audit or log
|
|
158
|
+
write stays honest without crashing the request. String coercion throws
|
|
159
|
+
`SecretCoercionError`, because a template literal is always a leak.
|
|
160
|
+
|
|
161
|
+
`AuditLog.write` is guarded the same way as the logger, since an audit event
|
|
162
|
+
carries `input` and `metadata` as `unknown` and nominality alone cannot stop a
|
|
163
|
+
secret landing in one.
|
|
164
|
+
|
|
165
|
+
`.reveal()` is the deliberate escape hatch, and what it hands back is an
|
|
166
|
+
ordinary string as far as every sink signature is concerned. **PKU953** closes
|
|
167
|
+
that gap: under `pikku all --security` the inspector reports a revealed secret
|
|
168
|
+
that flows into a logger, an audit, a queue, an email or a webhook — `console` included.
|
|
169
|
+
|
|
170
|
+
This also fixed a real one: `remote-addon-auth.ts` called `String(token)` on an
|
|
171
|
+
`unknown` and wrote the result straight into an `Authorization` header.
|
|
172
|
+
|
|
173
|
+
## 0.12.75
|
|
174
|
+
|
|
175
|
+
### Patch Changes
|
|
176
|
+
|
|
177
|
+
- 32277d5: Make a voice conversation with an agent something a chat surface can turn on, rather than
|
|
178
|
+
something each consumer reassembles.
|
|
179
|
+
|
|
180
|
+
The server half already worked — `voiceInput` transcribed, `voiceOutput` synthesized a
|
|
181
|
+
sentence at a time, and the AG-UI mapper forwarded the audio. What was missing was the
|
|
182
|
+
turn's own words. The client sends audio, so only the server ever knows what was said,
|
|
183
|
+
and nothing carried that back: a spoken turn rendered as an empty user bubble followed by
|
|
184
|
+
an answer to a question nobody could see, and thread history recorded the base64 audio
|
|
185
|
+
blob instead of the transcript — megabytes of unreadable data in place of the only
|
|
186
|
+
readable record of the turn.
|
|
187
|
+
|
|
188
|
+
`voiceInput` now records what it heard, the stream emits it as a `transcript` event ahead
|
|
189
|
+
of the run (the reply starts within a few hundred milliseconds, and a question that
|
|
190
|
+
appears after its answer reads as the wrong question), and it reaches the browser as
|
|
191
|
+
`pikku:transcript`. Both run paths persist the transcribed message rather than the one
|
|
192
|
+
that arrived on the wire. `audio-delta` also carries the sentence it says, which is what
|
|
193
|
+
a barge-in needs to report the part the user actually heard — a reply cut off after "I'll
|
|
194
|
+
delete the staging database and" is answered very differently depending on whether the
|
|
195
|
+
model knows the sentence never landed.
|
|
196
|
+
|
|
197
|
+
`@pikku/voice-agents` gains the two things a voice UI needs and could not get: a live
|
|
198
|
+
input level, attached to the source rather than to a detector so it keeps reading on the
|
|
199
|
+
Silero path, and the microphone list — re-readable on demand, because device labels are
|
|
200
|
+
empty until permission is granted and nothing fires when it is. `VoiceSession` also
|
|
201
|
+
learned manual turn boundaries, so push-to-talk is a mode rather than a detector fought
|
|
202
|
+
to a standstill: holding the key through a three-second pause is someone thinking, and
|
|
203
|
+
any endpointer worth having would cut them off.
|
|
204
|
+
|
|
205
|
+
`<PikkuAgentChat voice />` puts a microphone beside the send button, promotes it to
|
|
206
|
+
primary when nothing is typed, and opens an indicator with a live level bar, a device
|
|
207
|
+
picker and a hold-to-record toggle. It plays the agent's speech, and cancels the run on
|
|
208
|
+
barge-in — talking over the agent should stop the bill, not just the sound.
|
|
209
|
+
|
|
210
|
+
Opt-in, because the component cannot check the two things it depends on: the agent has to
|
|
211
|
+
be wired with `voiceInput` for the audio to be understood and `voiceOutput` for anything
|
|
212
|
+
to come back.
|
|
213
|
+
|
|
214
|
+
- ea8aabf: Serve `LocalContent` uploads and signed reads under Bun.
|
|
215
|
+
|
|
216
|
+
`LocalContent` hands the browser a `PUT <uploadUrlPrefix>/<key>` upload URL and a signed
|
|
217
|
+
`GET <assetUrlPrefix>/<key>` read URL, but it is a `ContentService` and cannot answer
|
|
218
|
+
either — something in the serving path has to. Only `@pikku/node-http-server` did. The
|
|
219
|
+
same project served under Bun handed out upload URLs that 404ed, with nothing naming the
|
|
220
|
+
cause: the config was accepted, the service was constructed, and the URLs looked right.
|
|
221
|
+
|
|
222
|
+
`@pikku/core` now exports `createLocalContentRequestHandler` from
|
|
223
|
+
`@pikku/core/services/local-content-request-handler` — the server half of `LocalContent`,
|
|
224
|
+
expressed in Web `Request`/`Response` so every runtime shares one implementation of the
|
|
225
|
+
signature check rather than each re-deriving it. It returns `null` for anything that is
|
|
226
|
+
not a content request, which is the caller's signal to carry on with its normal routing.
|
|
227
|
+
|
|
228
|
+
`PikkuBunServer` accepts `config.content` and a `contentSigningJWT` option, mirroring
|
|
229
|
+
`PikkuNodeHTTPServer`, and answers both prefixes ahead of static mounts and routing.
|
|
230
|
+
`BunServerRunner` was dropping `contentSigningJWT` on the floor, which silently disabled
|
|
231
|
+
signed asset reads for every Bun project even once the prefixes were served — the config
|
|
232
|
+
arrived, the service that verifies its signatures did not.
|
|
233
|
+
|
|
234
|
+
Signed reads are refused unless every claim matches, the path included: without that, a
|
|
235
|
+
signature minted for one asset would read any other.
|
|
236
|
+
|
|
237
|
+
- 33e96ab: Make a CLI served over a channel typecheck in a real project.
|
|
238
|
+
|
|
239
|
+
Both of these are unreachable for a hand-written `wireChannel`, whose routes are usually
|
|
240
|
+
bare identifiers, and unavoidable for a CLI one, whose routes are command ids.
|
|
241
|
+
|
|
242
|
+
`ChannelsMap` emitted route and message keys unquoted. A command id is a kebab or dotted
|
|
243
|
+
name far more often than not — `app-smoke`, `registry.search`, `package.upgrade-pikku` —
|
|
244
|
+
and each one ends the property early, so the generated map is not parseable TypeScript at
|
|
245
|
+
all. One project's map came out with 107 syntax errors from a single CLI channel. Keys are
|
|
246
|
+
now quoted when they are not bare identifiers, and left alone when they are, so existing
|
|
247
|
+
generated output is unchanged.
|
|
248
|
+
|
|
249
|
+
`executeRawCLIViaChannel` typed its renderers `Record<string, CorePikkuCLIRender<any>>`,
|
|
250
|
+
whose services parameter defaults to `CoreServices`. The renderers a generated client
|
|
251
|
+
passes are the app's own, typed against its `SingletonServices`, and a function taking
|
|
252
|
+
those is not assignable to one taking `CoreServices` — so the generated client failed to
|
|
253
|
+
compile for any app that adds a service, which is every app.
|
|
254
|
+
|
|
255
|
+
Rather than widen the type, it now says what is actually true on that side of the socket:
|
|
256
|
+
a renderer running on the client gets a logger and nothing else, because there is no
|
|
257
|
+
service container there to resolve anything from. `CorePikkuCLIClientRender` and
|
|
258
|
+
`ClientCLIRenderServices` are new exports of `@pikku/core/cli/channel`. They are not
|
|
259
|
+
expressible as `CorePikkuCLIRender`, whose `Services` parameter is constrained to
|
|
260
|
+
`CoreSingletonServices` and so demands a `config`, `variables` and `secrets` the client
|
|
261
|
+
cannot invent. The one cast from the app's renderer type to that shape is localised to the
|
|
262
|
+
generated client, where it is sound: generation refuses to emit the file at all if a
|
|
263
|
+
renderer reaches for a service other than `logger`.
|
|
264
|
+
|
|
265
|
+
- fd72e58: Drop `scenario.step` — a scenario step is now always a `given`, `when` or
|
|
266
|
+
`then`.
|
|
267
|
+
|
|
268
|
+
`step` rendered no keyword, which made it the phase to reach for whenever a
|
|
269
|
+
step did not obviously fit one of the three. That is exactly the step a reader
|
|
270
|
+
cannot check: a scenario is read by people deciding whether it describes the
|
|
271
|
+
behaviour they wanted, and a row that says what it does without saying whether
|
|
272
|
+
it is setup, action or claim tells them nothing to agree or disagree with. It
|
|
273
|
+
was also the escape hatch from the assertion lint — a scenario with no `then`
|
|
274
|
+
could be made to stop complaining by demoting its steps rather than by
|
|
275
|
+
asserting anything.
|
|
276
|
+
|
|
277
|
+
Replace `scenario.step(...)` with whichever of `given`, `when` or `then` the
|
|
278
|
+
step actually is. `then` is not a rename: it makes the step's bindings
|
|
279
|
+
witnesses rather than alternatives, so every declared surface runs and they
|
|
280
|
+
must agree.
|
|
281
|
+
|
|
282
|
+
- fd72e58: Make personas a first-class surface rather than a detail of the test runner.
|
|
283
|
+
|
|
284
|
+
A persona is now read in three places — the knowledge base resolves `persona:`
|
|
285
|
+
URIs against it, scenarios cast it as an actor, and a virtual user runs as it —
|
|
286
|
+
so it gets its own page at `/personas` under a new **People** section in the
|
|
287
|
+
rail, alongside Users. The card is a profile: avatar, name, job title, computed
|
|
288
|
+
address, the system roles they hold, and how many scenarios cast them. Opening
|
|
289
|
+
one expands each role to the scopes it confers, which is the half of the picture
|
|
290
|
+
that explains a 403.
|
|
291
|
+
|
|
292
|
+
`definePersonas` takes an optional `avatarUrl` — any URL a browser can load.
|
|
293
|
+
Nothing is derived from the address: a persona's address is synthetic, so a
|
|
294
|
+
derived identicon would be the same shrug for everyone. Omitted, the console
|
|
295
|
+
keeps drawing the deterministic colour-and-icon avatar from the persona's id.
|
|
296
|
+
|
|
297
|
+
- fd72e58: Read the actors that are not people on the personas page.
|
|
298
|
+
|
|
299
|
+
The platform — the app acting on itself, what `pikkuPlatformScenarioStep`
|
|
300
|
+
declares — now has a row of its own, alongside one per addon whose system a
|
|
301
|
+
step makes act. They sit behind a People / System / All filter that opens on
|
|
302
|
+
the people: a subject holds no roles and signs in as nobody, so leading with it
|
|
303
|
+
would put the rows nothing is authorized through above the ones that are.
|
|
304
|
+
|
|
305
|
+
The platform row is built in rather than derived. A project that has never
|
|
306
|
+
written a platform step still has a platform, and a card that appeared the
|
|
307
|
+
moment somebody declared their first step would read as a feature they had
|
|
308
|
+
switched on.
|
|
309
|
+
|
|
310
|
+
Also: PKU680 now counts `expectService`, `expectError` and `expectEventually`
|
|
311
|
+
as assertions. They are inline steps and carry no phase, so a scenario whose
|
|
312
|
+
only witness was a recorded service call was being told it never asserts.
|
|
313
|
+
|
|
314
|
+
- 894b2f8: `defineScope` and `defineSystemRole` accumulate across call sites again. Only `definePersonas` is one-per-codebase.
|
|
315
|
+
|
|
316
|
+
The previous release made all three single-declaration constructs, which no project scaffolding user-admin could satisfy: the CLI generates a `defineScope` of its own in `user-admin.gen.ts` carrying the whole `admin` tree, and `@pikku/addon-console` spells the same tree out again, so a second hand-written declaration failed the build with PKU583 — and the losing file's scopes were dropped from the metadata rather than merged.
|
|
317
|
+
|
|
318
|
+
Exempting generated files would have reinstated exactly the ambiguity the rule removes, only for the files nobody can read the rule from. The real fix is for `admin` to be a default scope nobody declares, at which point the rule can come back for scopes and roles.
|
|
319
|
+
|
|
320
|
+
`definePersonas` is unaffected: nothing generates one, so its single call site stands.
|
|
321
|
+
|
|
322
|
+
- dd19aa7: Drop `scopes` from sessionless functions, rename `selfAuthenticated`, and make both
|
|
323
|
+
escape hatches opt-in.
|
|
324
|
+
|
|
325
|
+
**`scopes` are gone from `pikkuSessionlessFunc`.** They are AND-ed and `verifyScopes`
|
|
326
|
+
fails closed on a session that does not exist, so every scope listed on a sessionless
|
|
327
|
+
function rejected the anonymous caller it exists to serve. `CorePikkuSessionlessFunctionConfig`
|
|
328
|
+
now states this once in core, and the generated `pikkuSessionlessFunc` / `pikkuVoidFunc`
|
|
329
|
+
configs derive from it — so the field is absent rather than subtracted.
|
|
330
|
+
|
|
331
|
+
`@pikku/addon-console`'s `installAddon` and `installOpenapiAddon` are now `pikkuFunc`.
|
|
332
|
+
Both set `auth: true` and `scopes: ['admin']`, and a test exercises that gate, so the
|
|
333
|
+
scopes were load-bearing — they only compiled as sessionless because the config accepted
|
|
334
|
+
a field it could not honour. No behaviour change: both already required a session.
|
|
335
|
+
|
|
336
|
+
**`selfAuthenticated` is now `permissionsInBody`.** It never described authentication:
|
|
337
|
+
what it records is that the permission check lives in the function body rather than in a
|
|
338
|
+
declared `permissions` entry.
|
|
339
|
+
|
|
340
|
+
**Both escape hatches must be opted into**, via a new `allow` block in
|
|
341
|
+
`pikku.config.json`:
|
|
342
|
+
|
|
343
|
+
```json
|
|
344
|
+
"allow": { "permissionsInBody": true, "complexWorkflows": true }
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
Unset means unavailable, and using the feature is a build error naming the flag that
|
|
348
|
+
would permit it — PKU576 for `permissionsInBody`, PKU643 for `pikkuWorkflowComplexFunc`.
|
|
349
|
+
Both trade something the tooling can inspect for something only a reader can verify: a
|
|
350
|
+
permission check buried in a body, or workflow steps that cannot be serialized into the
|
|
351
|
+
graph, replayed, or migrated. Both are occasionally right, and both are the path of least
|
|
352
|
+
resistance whenever the declarative form is merely inconvenient. Whoever owns the project
|
|
353
|
+
makes that call once, in writing, instead of every author making it silently at the call
|
|
354
|
+
site.
|
|
355
|
+
|
|
356
|
+
**PKU574's message no longer contradicts any of this.** Every function it reports is
|
|
357
|
+
sessionless — that is how the population is selected, not a finding — yet it opened by
|
|
358
|
+
reporting that they "require neither a session", then advised adding scopes. It now names
|
|
359
|
+
them as sessionless and recommends only gates an anonymous caller can meet:
|
|
360
|
+
`permissions`, `auth: true`, `wireAddon({ auth: true })`, or dropping `expose: true`.
|
|
361
|
+
`permissionsInBody` is deliberately absent from that list: a diagnostic should not
|
|
362
|
+
advertise its own escape hatch.
|
|
363
|
+
|
|
364
|
+
- 50ec500: Make `defineScope`, `defineSystemRole` and `definePersonas` single-declaration constructs
|
|
365
|
+
— exactly one call site per codebase, the rule `pikkuBetterAuth` has always had.
|
|
366
|
+
|
|
367
|
+
Each of the three already takes a keyed object, so one call declares as many entries as
|
|
368
|
+
you like. Spreading the calls across files bought nothing and cost the thing that matters:
|
|
369
|
+
there was no answer to "where do I add a persona?", so downstream tooling and agents had
|
|
370
|
+
nowhere unambiguous to read from or append to. The only duplicate handling that existed
|
|
371
|
+
caught a narrow case — the same id declared twice with different content — and said
|
|
372
|
+
nothing about the same id declared twice in two files.
|
|
373
|
+
|
|
374
|
+
A second call now fails the build with `PKU583` (`defineScope`), `PKU584`
|
|
375
|
+
(`defineSystemRole`) or `PKU585` (`definePersonas`), naming both source files and saying
|
|
376
|
+
to declare them all in one call. A second call in the _same_ file is refused too: "the
|
|
377
|
+
file" is not an answer either when the file holds two calls.
|
|
378
|
+
|
|
379
|
+
## 0.12.74
|
|
380
|
+
|
|
381
|
+
### Patch Changes
|
|
382
|
+
|
|
383
|
+
- 6a307f0: Fix four latent correctness bugs in the function, RPC and error runtimes, and
|
|
384
|
+
remove dead code from the workflow service surface.
|
|
385
|
+
|
|
386
|
+
`WorkflowService.getNodesWithoutSteps` is gone. It was declared on the abstract
|
|
387
|
+
service and implemented by all five storage backends, and nothing ever called
|
|
388
|
+
it — hence the non-core packages in this changeset, which only lose that method.
|
|
389
|
+
|
|
390
|
+
**An RPC could execute twice.** `RPCService.invoke`, its addon path, and
|
|
391
|
+
`rpcWithWire` each wrapped the _execution_ of a resolved function in a `try`
|
|
392
|
+
whose `catch` treated `RPCNotFoundError` as "not found locally" and re-dispatched
|
|
393
|
+
the call through `deploymentService`. A nested `rpc.invoke` to a missing name,
|
|
394
|
+
raised from inside an already-running function, therefore re-ran that function on
|
|
395
|
+
a remote instance after its local side effects had already committed. Resolution
|
|
396
|
+
is now separated from execution on all three paths, so only a genuinely
|
|
397
|
+
unresolvable name reaches the fallback. What escapes to callers is unchanged.
|
|
398
|
+
|
|
399
|
+
**`addonNamespace` leaked between sibling calls.** The function runner's
|
|
400
|
+
middleware path restored `rpc`, `functionId`, `audit` and `addonNamespace` after
|
|
401
|
+
an invocation; the non-middleware path restored the first three but not the
|
|
402
|
+
fourth. A call into an addon function with no middleware left the addon's
|
|
403
|
+
namespace on the wire, so subsequent sibling calls resolved the wrong
|
|
404
|
+
per-instance singletons and `credentialOverrides`.
|
|
405
|
+
|
|
406
|
+
**Errors registered on a subclass never resolved.** `misc.errors` was typed
|
|
407
|
+
`Map<PikkuError, ErrorDetails>` — instances — while `addError` stores
|
|
408
|
+
constructors, a mismatch hidden by its `error: any` parameter. The instance
|
|
409
|
+
lookup in `getErrorResponse` was consequently dead, and lookup fell through to a
|
|
410
|
+
scan comparing `constructor.name`, so a subclass of a registered error got no
|
|
411
|
+
status mapping at all. The map is now typed `Map<PikkuErrorConstructor, …>` and
|
|
412
|
+
lookup walks the prototype chain first. Name matching is retained, deliberately,
|
|
413
|
+
as the fallback that keeps error mapping working when two copies of
|
|
414
|
+
`@pikku/core` are installed.
|
|
415
|
+
|
|
416
|
+
**`createWeakUID` collided across instances.** The prefix was
|
|
417
|
+
`Date.now().toString(36)` evaluated at module load, so any two instances loading
|
|
418
|
+
the module in the same millisecond emitted identical `channelId` and `requestId`
|
|
419
|
+
sequences — reproducibly, not just in principle. It is now seeded lazily from
|
|
420
|
+
`crypto.randomUUID()`.
|
|
421
|
+
|
|
422
|
+
Also: `pikkuState` keys its global map with `Symbol.for` rather than `Symbol`, so
|
|
423
|
+
two copies of the package share registrations instead of silently getting
|
|
424
|
+
disjoint state; and the local channel upgrade path no longer keys its middleware
|
|
425
|
+
cache on the raw request path, which grew the cache without bound while the
|
|
426
|
+
cached value never varied by path.
|
|
427
|
+
|
|
428
|
+
- afef587: Close eleven security weaknesses found in a review of `@pikku/core`. Most are
|
|
429
|
+
breaking, and two invalidate data or credentials already in the wild — read the
|
|
430
|
+
migration notes before upgrading.
|
|
431
|
+
|
|
432
|
+
**Breaking: AI agent thread ownership now fails closed.** Reading, listing,
|
|
433
|
+
resuming or approving an existing thread or run requires a resolved session
|
|
434
|
+
principal (`userId`, or `orgId` for `sessionScope: 'org'`), regardless of the
|
|
435
|
+
agent's `auth` setting. Previously a request without a session had no ownership
|
|
436
|
+
model at all: the caller-supplied `resourceId` was accepted as the ownership
|
|
437
|
+
key, so any caller could read or resume another party's thread by naming its
|
|
438
|
+
`resourceId`. Worse, `threadOwnerConstraint` returned `undefined` for a
|
|
439
|
+
sessionless caller, and `undefined` means _no filter_ rather than _no rows_ —
|
|
440
|
+
so `getAgentThreads` returned every thread in the deployment. It now returns
|
|
441
|
+
`string[]`, empty for a sessionless caller, which every storage backend already
|
|
442
|
+
treats as matching nothing. Sessionless agents still run one-shot conversations,
|
|
443
|
+
each with a fresh unguessable owner; what they lose is cross-request continuity.
|
|
444
|
+
Wire a session to restore it.
|
|
445
|
+
|
|
446
|
+
**Breaking: stored secrets and credentials must be re-entered.** `deriveKey` ran
|
|
447
|
+
a single unsalted round of SHA-256 over the passphrase and used the digest
|
|
448
|
+
directly as the AES-GCM key — roughly one hash per brute-force guess, with one
|
|
449
|
+
rainbow table working against every deployment. It is now PBKDF2-HMAC-SHA256 at
|
|
450
|
+
600,000 iterations over a random salt. There is no compatibility path: every
|
|
451
|
+
value held by the kysely, mongodb and redis secret services and the kysely
|
|
452
|
+
credential service becomes undecryptable. They fail loud, naming the key and
|
|
453
|
+
`key_version`, so the app hard-fails on first secret read until each is re-set.
|
|
454
|
+
|
|
455
|
+
The KEK salt is scoped to the key version and stored alongside it, rather than
|
|
456
|
+
per secret, so a bulk read costs one derivation instead of N — `getSecrets` over
|
|
457
|
+
50 secrets went from ~2.3s to ~48ms, and rotation from ~4.6s to ~94ms. This adds
|
|
458
|
+
a salt table (kysely), hash field (redis) or collection (mongodb), created
|
|
459
|
+
automatically on first use.
|
|
460
|
+
|
|
461
|
+
**Breaking: `PIKKU_REMOTE_SECRET` must be at least 32 characters.** The
|
|
462
|
+
remote-RPC session envelope moved from PBKDF2 to HKDF, which expands
|
|
463
|
+
high-entropy key material rather than stretching a low-entropy passphrase. That
|
|
464
|
+
took a remote hop from ~269ms to ~0.4ms — PBKDF2 was running twice per request —
|
|
465
|
+
but HKDF supplies no brute-force resistance, so the secret must carry the
|
|
466
|
+
entropy itself. A shorter secret now throws `WeakKeyMaterialError` at both ends.
|
|
467
|
+
Generate one with `openssl rand -base64 32` and roll it out to every service in
|
|
468
|
+
the mesh together: existing bearer tokens are format-incompatible, so a partial
|
|
469
|
+
rollout produces 401s until every instance is updated. The Cloudflare, Lambda
|
|
470
|
+
and Azure deployment services each hand-rolled a copy of `buildRemoteHeaders`
|
|
471
|
+
and now call the shared one, which is what keeps the two sides in step.
|
|
472
|
+
|
|
473
|
+
**Breaking: previously signed content URLs stop verifying.** `LocalContent`
|
|
474
|
+
signed only `{signedAt, expiresAt, notBefore}`, so a signature proved when a URL
|
|
475
|
+
was issued but never what it was issued for — any valid token was a skeleton
|
|
476
|
+
key, and swapping the pathname from a public thumbnail to a private document
|
|
477
|
+
still verified. The signature now binds the request path. Separately, the
|
|
478
|
+
verifier returned "valid" when no JWT service was wired, which is how
|
|
479
|
+
`pikku serve` ran: a forged `?signedAt=0&expiresAt=99999999999999` was accepted.
|
|
480
|
+
It now rejects with 403, `LocalContent` requires a `JWTService`, and
|
|
481
|
+
`pikku serve`/`pikku dev` mint an ephemeral per-process signing key so local
|
|
482
|
+
development works without shipping a fail-open path. In-flight signed URLs must
|
|
483
|
+
be re-issued.
|
|
484
|
+
|
|
485
|
+
**Request body size limits now apply to every adapter.** The `maxBodySize` cap
|
|
486
|
+
existed only in `PikkuFetchHTTPRequest`. The real hole was uWebSockets, which
|
|
487
|
+
drove `res.onData` itself and concatenated every chunk with no bound and nothing
|
|
488
|
+
downstream able to intervene; it now drops chunks past the limit and replies 413
|
|
489
|
+
before routing. Fastify delegates to its native `bodyLimit` (set only when
|
|
490
|
+
`maxBodySize` is configured, so fastify's stricter 1 MB default is never
|
|
491
|
+
loosened), and `PikkuExpressServer` feeds the limit into its body parsers. Two
|
|
492
|
+
paths can only reject rather than prevent, and are documented as such:
|
|
493
|
+
`express-middleware` mounted on your own app receives an already-parsed body, so
|
|
494
|
+
that deployment must bound its own parser; Next server actions bottom out at
|
|
495
|
+
`experimental.serverActions.bodySizeLimit`.
|
|
496
|
+
|
|
497
|
+
**Breaking: the console addon's privileged functions are gated by default.**
|
|
498
|
+
`wireAddon` gains a `scopes` option that applies to every function in the
|
|
499
|
+
addon's namespace, and the console scaffold now generates
|
|
500
|
+
`wireAddon({ name: 'console', package: '@pikku/addon-console', scopes: ['admin'] })`.
|
|
501
|
+
Previously the console's entire privileged surface — around 54 functions
|
|
502
|
+
including `credentialGet`, which returns a resolved OAuth token for an arbitrary
|
|
503
|
+
`userId`, `updateFunctionBody`, and `installAddon`, which shells out to a
|
|
504
|
+
package install — was protected only by an optional host-registered
|
|
505
|
+
`addGlobalPermission`. `resolveGlobalPermissions` returns `[]` when none is
|
|
506
|
+
registered and permission checking then no-ops, so an app that never registered
|
|
507
|
+
one served those functions to anyone, and with the template's default
|
|
508
|
+
`scaffold.rpc: "no-auth"` that meant unauthenticated. All of them now return 403
|
|
509
|
+
`MissingScopeError` without an `admin`, `admin:*` or `*` scope. **Regenerating
|
|
510
|
+
is required** — an app holding an old `console.gen.ts` stays open.
|
|
511
|
+
`installAddon` and `installOpenapiAddon` additionally declare their own
|
|
512
|
+
`auth: true, scopes: ['admin']`, and `getAgentThreads` now scopes its listing to
|
|
513
|
+
the session's own threads unless the caller holds admin.
|
|
514
|
+
|
|
515
|
+
Addon scopes are enforced in `runPikkuFunc` rather than at the RPC boundary,
|
|
516
|
+
because a wiring can reference an addon function directly — the inspector
|
|
517
|
+
records the addon's `packageName` on HTTP, channel, schedule, queue, CLI,
|
|
518
|
+
trigger, gateway and MCP wirings — and those paths never call `resolveNamespace`.
|
|
519
|
+
Enforcing at the RPC seam would have covered only the `namespace:function` form
|
|
520
|
+
while reading as complete.
|
|
521
|
+
|
|
522
|
+
**Breaking: `wireAddon`'s `auth` and `tags` now apply on direct wirings too.**
|
|
523
|
+
Both were read only by `resolveAddonFunction`, so they had exactly the hole
|
|
524
|
+
scopes had: `wireAddon({ name: 'console', package: '@pikku/addon-console', auth:
|
|
525
|
+
true, tags: ['admin'] })` gated `rpc('console:credentialGet')` and gated nothing
|
|
526
|
+
at all on an HTTP route wired straight to `credentialGet`. A consumer who
|
|
527
|
+
reached for the documented way to lock an addon down got a control that was
|
|
528
|
+
silently inert on every wiring except one. Both now resolve in `runPikkuFunc`.
|
|
529
|
+
`auth` merges as an OR — `auth: false` from an addon is ignored on a direct
|
|
530
|
+
wiring, because an addon may require a session the wiring did not but must never
|
|
531
|
+
waive one it did. Addon tags resolve to concrete middleware against the **root**
|
|
532
|
+
tag groups before the call rather than being folded into the function's
|
|
533
|
+
inherited middleware: `addTagMiddleware('admin', …)` is written by the consuming
|
|
534
|
+
app and registers under the root package, whereas `combineMiddleware` would look
|
|
535
|
+
the tag up under the addon's own `packageName`, where it does not exist.
|
|
536
|
+
|
|
537
|
+
One consequence worth naming: an addon that wires `auth: true` and also runs its
|
|
538
|
+
own sessionless internal work — a scheduled task or queue worker inside the
|
|
539
|
+
addon calling a sibling function — is now gated where it previously was not,
|
|
540
|
+
because a bare `rpc('fn')` from inside the addon reaches `runPikkuFunc` with the
|
|
541
|
+
addon's `packageName` like any other call. This reverses a decision that
|
|
542
|
+
deliberately scoped the gate to the namespaced boundary; that reasoning held
|
|
543
|
+
only while the boundary was real, and a direct wiring can enter an addon without
|
|
544
|
+
crossing it, so "already inside" is not something the runtime can infer. Such an
|
|
545
|
+
addon should carry authorization on the function via
|
|
546
|
+
`pikkuFunc({ permissions })`, which has always been enforced on every path. A
|
|
547
|
+
follow-up will add execution provenance so an intra-addon call can be
|
|
548
|
+
distinguished from an external one and skip the addon-level check; that needs a
|
|
549
|
+
marker no caller outside the process can set, which is its own design problem
|
|
550
|
+
and does not belong in a security fix.
|
|
551
|
+
|
|
552
|
+
**Codegen now warns when an exposed function has no gate (PKU574).** The
|
|
553
|
+
generated `POST /rpc/:rpcName` dispatcher forwards to `rpc.exposed`, which
|
|
554
|
+
refuses anything without `expose: true` — but nothing checked whether the
|
|
555
|
+
target was gated, because a dispatcher cannot know what it dispatches to. The
|
|
556
|
+
console shipped ~54 privileged functions through that gap and the toolchain was
|
|
557
|
+
silent. The inspector now reports every function that is exposed, sessionless,
|
|
558
|
+
and carries no `auth`, `scopes` or `permissions` of its own and none from a
|
|
559
|
+
governing `wireAddon`. It is a `warn`, not a critical: `expose: true` on an
|
|
560
|
+
ungated sessionless function is correct for a genuinely public endpoint, so it
|
|
561
|
+
blocks a build only under `--fail-on-warn`.
|
|
562
|
+
|
|
563
|
+
Two pieces of metadata were missing for this to be answerable statically, and
|
|
564
|
+
both are now recorded. A `pikkuSessionlessFunc`'s own `auth: true` was read at
|
|
565
|
+
runtime but never written to function meta, so a self-gated function was
|
|
566
|
+
indistinguishable from an ungated one — `sessionless` carries the baseline, and
|
|
567
|
+
`auth` now carries the tightening. And `wireAddon`'s `scopes`, `auth` and `tags`
|
|
568
|
+
were not parsed at all: the inspector recorded the addon's `rpcEndpoint` and its
|
|
569
|
+
secret, variable and credential overrides, and dropped every one of its gates.
|
|
570
|
+
An addon whose gates are not statically knowable is treated as gated, because a
|
|
571
|
+
false positive on a correctly-secured addon costs more than the one case it
|
|
572
|
+
would catch.
|
|
573
|
+
|
|
574
|
+
**Breaking: an application's global permissions now apply inside addons.**
|
|
575
|
+
`resolveGlobalPermissions` read only the bucket matching the function's own
|
|
576
|
+
package, but the generated `addGlobalPermission` wrapper takes no package
|
|
577
|
+
argument and always registers under the root. An app-wide rule like "every
|
|
578
|
+
request needs a signed-in user" therefore stopped at the addon boundary, and the
|
|
579
|
+
bucket an addon's functions did read was one no host could write to — which is
|
|
580
|
+
why the console addon's recommended `addGlobalPermission([isAdmin],
|
|
581
|
+
'@pikku/addon-console')` was never a gate anybody could actually install. A
|
|
582
|
+
function now resolves the root bucket and its own package's, root first.
|
|
583
|
+
Unioning is safe in a way nothing else here would be: globals AND, so adding the
|
|
584
|
+
root ones can only tighten. Package buckets stay one-way — a package's globals
|
|
585
|
+
never reach root functions, or an installed addon could gate the whole
|
|
586
|
+
application. Apps with both a root global and addon-provided functions will see
|
|
587
|
+
those functions gated where they previously were not.
|
|
588
|
+
|
|
589
|
+
**Codegen now records whether each HTTP route requires a session, and warns
|
|
590
|
+
about inert addon tags (PKU575).** Four separate things can demand a session —
|
|
591
|
+
the function's `sessionless`, its own `auth`, the route's (or its group's)
|
|
592
|
+
`auth`, and the addon it belongs to — and answering "which routes are open?"
|
|
593
|
+
meant joining all four by hand and knowing which wins. Each route's meta now
|
|
594
|
+
carries the resolved `requiresSession` alongside the route's own `auth`. Scopes
|
|
595
|
+
count as requiring one, since they are matched against the session's and fail
|
|
596
|
+
closed. Anything not statically knowable resolves to `true`, matching PKU574:
|
|
597
|
+
a route that looks stricter than it is costs less than one that looks open and
|
|
598
|
+
isn't. Separately, `wireAddon({ tags: ['admin'] })` reads like a gate and is
|
|
599
|
+
applied like one right up until no `addTagMiddleware('admin', …)` exists, at
|
|
600
|
+
which point it resolves to an empty list and gates nothing; that now warns.
|
|
601
|
+
Only addon tags are reported — a tag on a function is as likely to be
|
|
602
|
+
organizational, and warning about those would bury the case that matters.
|
|
603
|
+
|
|
604
|
+
**Object-shorthand permissions were missing from meta.** The inspector visited
|
|
605
|
+
`ts.PropertyAssignment` but not `ts.ShorthandPropertyAssignment`, so
|
|
606
|
+
`permissions: { canAdminOrg }` — enforced identically to the longhand form at
|
|
607
|
+
runtime, since `verifyPermissions` has a non-array branch — was recorded as _no
|
|
608
|
+
permissions at all_. That is the most dangerous direction for meta to be wrong
|
|
609
|
+
in: an audit reading it sees an open door where one is shut. It cost this review
|
|
610
|
+
a false IDOR report across ~35 billing and org functions before the source
|
|
611
|
+
contradicted the metadata.
|
|
612
|
+
|
|
613
|
+
**Functions that authorize in their own body can say so.** A webhook receiver
|
|
614
|
+
verifying a signature, or a handler redeeming a signed invite, is genuinely
|
|
615
|
+
closed while carrying no session, scope or permission — indistinguishable in
|
|
616
|
+
meta from one nobody remembered to gate, and so warned about forever by PKU574.
|
|
617
|
+
`selfAuthenticated: true` on the function config records the claim and silences
|
|
618
|
+
the warning for that function. It is declarative only: nothing at runtime reads
|
|
619
|
+
it and it grants nothing. Detection was rejected deliberately — inferring it
|
|
620
|
+
from the body means a function that _looks_ like it checks something silences
|
|
621
|
+
the warning while checking nothing, and a warning that is usually wrong stops
|
|
622
|
+
being read.
|
|
623
|
+
|
|
624
|
+
**Breaking: `scaffold.<feature>` is now `boolean | { auth?, path? }`, and `true`
|
|
625
|
+
means authenticated.** The old `'auth' | 'no-auth' | false` read like a
|
|
626
|
+
starter-file preference while being a live authorization decision, set three
|
|
627
|
+
directories from the functions it governed — the shape the console incident
|
|
628
|
+
took. A surface now becomes public only by writing `{ auth: false }`, so
|
|
629
|
+
omitting a field can never open anything: the failure mode of a forgotten flag
|
|
630
|
+
is a locked door. `{ path }` additionally overrides where the file is
|
|
631
|
+
generated, which previously could only be set for all features at once via
|
|
632
|
+
`pikkuDir`.
|
|
633
|
+
|
|
634
|
+
The legacy strings are **refused, not coerced**. `resolveScaffoldFeature` throws
|
|
635
|
+
naming the key and its replacement (`"rpc": "no-auth"` → `"rpc": { "auth":
|
|
636
|
+
false }`), and it does so at config load, not downstream. An earlier design used
|
|
637
|
+
a bare `string` for the output path, under which `"no-auth"` would have parsed
|
|
638
|
+
as a file named `no-auth` and every unmigrated config would have silently
|
|
639
|
+
produced nonsense; the object form makes any string invalid, so the failure is
|
|
640
|
+
loud.
|
|
641
|
+
|
|
642
|
+
The collapse is deliberately not uniform in effect. For `rpc` the flag was a
|
|
643
|
+
blanket "no anonymous RPC in this app" set on a dispatcher that cannot know what
|
|
644
|
+
it dispatches to; for `userAdmin` it was redundant, since the generated
|
|
645
|
+
functions are already `pikkuFunc` with `scopes: ['admin:users:list']`. But for
|
|
646
|
+
`agent`, `workflow`, `events` and `scenarios` it is the only gate — those
|
|
647
|
+
generate real endpoints the app never authors — so `true` keeps them
|
|
648
|
+
authenticated rather than opening them as a side effect of a config cleanup.
|
|
649
|
+
`webhook` and `remoteRpc` have no auth dimension at all (`serialize-remote-rpc.ts`
|
|
650
|
+
hardcodes `auth: false`), so an `{ auth }` on them is ignored. The three
|
|
651
|
+
configs in this repo are migrated preserving their current behaviour exactly.
|
|
652
|
+
|
|
653
|
+
**Queue job identities are signed.** A job carried the producer's `pikkuUserId`
|
|
654
|
+
as a plain string and the worker resolved a session from it with no
|
|
655
|
+
verification, so write access to the queue backend was act-as-any-user. The
|
|
656
|
+
identity is now `pq1.<claim>.<hmac>`, HMAC-SHA256 over the claim and the
|
|
657
|
+
canonicalized job payload, keyed by HKDF expansion of a new
|
|
658
|
+
`PIKKU_QUEUE_IDENTITY_SECRET`. Producers opt in by wrapping their queue service
|
|
659
|
+
with `SignedQueueService`. This fails safe rather than closed: with no secret
|
|
660
|
+
configured the identity is dropped and jobs still process, warning once per
|
|
661
|
+
process, so no existing deployment breaks on upgrade — it simply loses queue
|
|
662
|
+
identity until the secret is set. The payload rather than the job id is bound
|
|
663
|
+
because SQS, Cloudflare Queues, Azure and the in-memory service all mint ids
|
|
664
|
+
after `add` returns.
|
|
665
|
+
|
|
666
|
+
**Workflow inline state is read from the run record.** `isInline` consulted a
|
|
667
|
+
process-local `Map`, while `WorkflowRun.inline` is durable. Any instance that
|
|
668
|
+
did not start a run disagreed with the record, so one instance could dispatch a
|
|
669
|
+
queued job for a workflow another was already executing in-process. It is now
|
|
670
|
+
async and resolves through the durable identity, cached only when a context
|
|
671
|
+
already exists so a passive reader allocates nothing. The same `Map` also leaked:
|
|
672
|
+
`nextStepKey` fabricated replay state on every step, and `releaseContext`
|
|
673
|
+
refused to free anything carrying it, so runs whose steps executed outside a
|
|
674
|
+
`beginReplay` bracket — the step-worker queue path — stranded their context and
|
|
675
|
+
step state for the process lifetime. Contexts are now released by an explicit
|
|
676
|
+
execution counter. Step ordinals reset per execution rather than accumulating
|
|
677
|
+
across step-worker invocations in one process, which makes step naming
|
|
678
|
+
independent of how work was distributed.
|
|
679
|
+
|
|
680
|
+
**Secret reads fail loud in every store.** `MongoDBSecretService.getSecrets`
|
|
681
|
+
skipped rows that failed to decrypt, and the redis equivalent dropped every
|
|
682
|
+
rejection via `Promise.allSettled`, including the "No KEK available for
|
|
683
|
+
key_version N" configuration error. Both now throw, naming the key and its key
|
|
684
|
+
version, matching the kysely behaviour. This matters most alongside the KEK
|
|
685
|
+
change above: without it, an upgrade surfaces as a partial secrets map and an
|
|
686
|
+
opaque downstream failure instead of an error naming the secret to re-enter.
|
|
687
|
+
|
|
688
|
+
**A second middleware registration for a pattern no longer erases the first.**
|
|
689
|
+
`addHTTPMiddleware`, `addTagMiddleware` and `addChannelMiddleware` groups are
|
|
690
|
+
keyed by pattern or tag and held one source file each, so a second file's call
|
|
691
|
+
overwrote the first's. Codegen emits its imports from what is stored, so the
|
|
692
|
+
losing file was never imported and its middleware never registered — the
|
|
693
|
+
runtime composes repeated registrations for a pattern happily, and only codegen
|
|
694
|
+
dropped one. Adding an unrelated `addHTTPMiddleware('*', …)` to an app was
|
|
695
|
+
enough to silently unregister the generated better-auth session bridge, which
|
|
696
|
+
fails open and gives no sign until a request arrives without a session. A group
|
|
697
|
+
now carries every registration made for it, and all of them are imported.
|
|
698
|
+
|
|
699
|
+
- 8075f6a: Confine `SecretService` to the places an app is wired.
|
|
700
|
+
|
|
701
|
+
`secrets` is now omitted from the services every function, AI agent, workflow,
|
|
702
|
+
permission and wire receives, and the function runner replaces it with a
|
|
703
|
+
throwing accessor so a cast cannot reach past the type. It stays available in
|
|
704
|
+
`pikkuServices`, `pikkuWireServices`, addon service factories and middleware —
|
|
705
|
+
read a secret there, give it to a service, and have the function ask that
|
|
706
|
+
service.
|
|
707
|
+
|
|
708
|
+
Alongside it:
|
|
709
|
+
- `wireSecret` gains `allowedHosts`, refusing a secret attached to a host it was
|
|
710
|
+
not declared for. Permissive by default; strict via
|
|
711
|
+
`config.secrets.requireAllowedHosts`.
|
|
712
|
+
- `pikku-graph`'s `httpRequest` resolves and attaches its credential inside a new
|
|
713
|
+
`httpRequester` service instead of holding the plaintext in the function.
|
|
714
|
+
- New inspector diagnostics: `PKU950` (a `SecretService` exposed under another
|
|
715
|
+
service name), `PKU951` (a secret read that no `wireSecret` declares) and
|
|
716
|
+
`PKU952` (a secret read with a non-literal key).
|
|
717
|
+
|
|
718
|
+
## 0.12.73
|
|
719
|
+
|
|
720
|
+
### Patch Changes
|
|
721
|
+
|
|
722
|
+
- c984df6: Give an agent's tools back the descriptions their authors wrote
|
|
723
|
+
|
|
724
|
+
A tool's description is what the model is told the tool does, and the main
|
|
725
|
+
thing it chooses between tools on. It was not reaching the model. `description`
|
|
726
|
+
is classed as a verbose field, so it is stripped from the metadata bundled into
|
|
727
|
+
the generated bootstrap — the copy `pikkuState('function', 'meta')` is built
|
|
728
|
+
from. `buildToolDefs` read the description from there, found it always
|
|
729
|
+
undefined, and fell through to the tool's own name. Every agent has been
|
|
730
|
+
choosing between bare identifiers. The same fallback was offering an addon's
|
|
731
|
+
MCP tools under their names, for the same reason.
|
|
732
|
+
|
|
733
|
+
Tool definitions now resolve descriptions through `metaService`, which reads
|
|
734
|
+
the verbose metadata and falls back to the minimal copy, so the authored text
|
|
735
|
+
is recovered wherever the generated `.pikku` directory is readable. Where it is
|
|
736
|
+
not — no `metaService`, or a deployment shipping only the stripped copy — a
|
|
737
|
+
tool falls back to its name, which is what it did before. Addon metadata is
|
|
738
|
+
likewise loaded verbose-first. `title` is no longer part of the chain: a title
|
|
739
|
+
labels a tool in a UI, it does not tell a model when to reach for it.
|
|
740
|
+
|
|
741
|
+
An addon has to ship the verbose file for any of this to reach it. `tsc` only
|
|
742
|
+
emits the JSON it sees imported and nothing imports the verbose meta, so the
|
|
743
|
+
bundled addons now copy it into `dist` explicitly.
|
|
744
|
+
|
|
745
|
+
`ref()` is resolved at build time. It used to be pushed through codegen as an
|
|
746
|
+
opaque string, so `ref('todos:doesNotExist')` generated cleanly and failed only
|
|
747
|
+
when the agent ran. The inspector now resolves each reference against the
|
|
748
|
+
project's functions, or — using the namespace-to-package mapping `wireAddon`
|
|
749
|
+
already provides — against the addon's own metadata, and reports an unwired
|
|
750
|
+
namespace (`PKU152`) or a missing function (`PKU153`) at codegen. An addon that
|
|
751
|
+
has not been built yet contributed no metadata and is skipped rather than
|
|
752
|
+
reported missing.
|
|
753
|
+
|
|
754
|
+
New `pikku --strict-meta` additionally fails the build on any agent tool with
|
|
755
|
+
no description (`PKU154`), including tools reached through an addon. It is off
|
|
756
|
+
by default, so nothing that builds today stops building; turn it on to hold a
|
|
757
|
+
project to the metadata its agents actually run on.
|
|
758
|
+
|
|
759
|
+
- 63ff32b: Run a CLI's commands on the server, over the connection the client opened
|
|
760
|
+
|
|
761
|
+
A CLI that talks to a service has to ship the service's command tree, so the
|
|
762
|
+
two versions drift: the binary someone installed months ago still believes in
|
|
763
|
+
flags and commands the server has since changed. This makes the command tree
|
|
764
|
+
the server's, and leaves the client holding only a socket.
|
|
765
|
+
|
|
766
|
+
`wireCLI` gains `auth`, and a program wired with a channel entrypoint now
|
|
767
|
+
generates a `__raw` route: the client forwards argv untouched, the server
|
|
768
|
+
parses it, runs the command, and streams the output back as it happens. The
|
|
769
|
+
terminating frame carries the exit code, so a failed remote command still exits
|
|
770
|
+
non-zero locally. Renderers stay on the client and are matched by the command
|
|
771
|
+
id the server reports; an unrecognised command falls back to JSON rather than
|
|
772
|
+
failing.
|
|
773
|
+
|
|
774
|
+
Every channel gains `channel.remote(...)`: calling a function on the peer at
|
|
775
|
+
the other end of the connection and waiting for its answer. A channel is
|
|
776
|
+
otherwise fire-and-forget in both directions, so this is what reaches a peer
|
|
777
|
+
that has no address of its own — a CLI on a laptop, a browser tab, a sandbox
|
|
778
|
+
behind NAT. It is on `channel` rather than `rpc` because it is bound to one
|
|
779
|
+
connection: which peer answers is the socket the call goes out on, not
|
|
780
|
+
something the RPC map could resolve. Any `wireChannel` gets it — a client
|
|
781
|
+
registers what it is willing to answer to, and a name outside that list is
|
|
782
|
+
refused.
|
|
783
|
+
|
|
784
|
+
Requests are correlated by id, time out, and fail fast when the socket closes
|
|
785
|
+
rather than waiting out the timeout. Replies are taken off the socket ahead of
|
|
786
|
+
routing, so a channel needs no route for them and an answer can never be
|
|
787
|
+
mistaken for a new message; the transport is created on first use and released
|
|
788
|
+
when the channel closes, which is also what fails anything the departing peer
|
|
789
|
+
still owed an answer to. Channels that only flow one way — SSE, an agent's
|
|
790
|
+
output stream, a locally-run CLI — refuse the call outright instead of waiting
|
|
791
|
+
for an answer that was never going to come.
|
|
792
|
+
|
|
793
|
+
What a peer answers with is its word, so it is checked before a caller sees it
|
|
794
|
+
— against the schema codegen already generated from the function's declared
|
|
795
|
+
return type, the same one an agent tool or an HTTP response is checked against.
|
|
796
|
+
A capability is declared with `pikkuRemoteChannelFunc`, which takes the usual
|
|
797
|
+
`title` / `description` / `input` / `output` but no `func` — this side owns the
|
|
798
|
+
contract, the peer owns the body. It registers under its name like any other
|
|
799
|
+
function, so `channel.remote` is typed off the same generated map as
|
|
800
|
+
`rpc.remote` and no caller has to cast, and a local call throws rather than
|
|
801
|
+
missing: reaching it locally means a command asked the server for something
|
|
802
|
+
only a client knows. A client on an older build fails the call it answered
|
|
803
|
+
rather than the caller failing later somewhere with no reason to expect a bad
|
|
804
|
+
shape; a name with no declared contract is left alone. Both frame guards
|
|
805
|
+
validate the whole envelope rather than the action tag alone, and a failure
|
|
806
|
+
payload with a non-string name or message falls back rather than being attached
|
|
807
|
+
to an `Error`.
|
|
808
|
+
|
|
809
|
+
The arguments going the other way are checked too, against the schema for the
|
|
810
|
+
capability's declared input, before anything is registered or sent. That is not
|
|
811
|
+
a boundary — the peer runs the code and has to check what it was handed, and a
|
|
812
|
+
caller that meant harm would send arguments that pass. It catches drift, where a
|
|
813
|
+
server built against a newer capability signature calls a client that predates
|
|
814
|
+
it, and fails it here rather than inside someone else's process.
|
|
815
|
+
|
|
816
|
+
A channel-driven CLI command uses this to ask its caller for machine-local
|
|
817
|
+
facts mid-run — a git sha, a working tree, a local file. The CLI wire's own
|
|
818
|
+
channel is synthetic (it exists so a command can stream progress without
|
|
819
|
+
knowing where that goes), so it delegates `remote` to the connection the
|
|
820
|
+
command actually arrived on.
|
|
821
|
+
|
|
822
|
+
Because that runs code on someone's machine at a remote caller's request, the
|
|
823
|
+
capability map says what _can_ run and approval says whether a particular call
|
|
824
|
+
_should_. A capability may be declared `{ execute, needsApproval }`, sharing
|
|
825
|
+
`ApprovalPolicy` — `needsApproval` and `approvalDescriptionFn` — with
|
|
826
|
+
`AIAgentToolDef`, which has carried both since before channels could call back:
|
|
827
|
+
both are an allowlist of named callables invoked by something other than the
|
|
828
|
+
code that wrote them. The runtime around them is deliberately not shared, since
|
|
829
|
+
an agent suspends its run and resumes it later while a reverse call is a live
|
|
830
|
+
await with a person at the other end.
|
|
831
|
+
|
|
832
|
+
A capability written as a bare function is unclassified, and unclassified means
|
|
833
|
+
approval is required — the annotation nobody got round to writing is the one
|
|
834
|
+
most likely to matter, so it fails closed. Declare
|
|
835
|
+
`{ execute, needsApproval: false }` for a capability that may run unattended.
|
|
836
|
+
Nothing infers this: core cannot tell a read-only capability from a destructive
|
|
837
|
+
one, so `needsApproval: false` is the author asserting it, and the assertion is
|
|
838
|
+
the only thing standing between a remote caller and the machine.
|
|
839
|
+
|
|
840
|
+
The default is the opposite of `AIAgentToolDef`'s, where absence means "do not
|
|
841
|
+
ask" — a tool is written by the same people who run the server it executes on,
|
|
842
|
+
and a capability is not.
|
|
843
|
+
|
|
844
|
+
`executeRawCLIViaChannel` reads `--auto-approve` and
|
|
845
|
+
`--dangerously-auto-approve` out of argv (or `PIKKU_AUTO_APPROVE` /
|
|
846
|
+
`PIKKU_DANGEROUSLY_AUTO_APPROVE`) and strips them before argv reaches the
|
|
847
|
+
server — what may run on this machine is this machine's decision, and a flag
|
|
848
|
+
the server can see is one the server could act on. `--auto-approve` permits the
|
|
849
|
+
classified-safe set and refuses the rest; `--dangerously-auto-approve` permits
|
|
850
|
+
everything and says so once on stderr. Interactively the user is asked per
|
|
851
|
+
call, with `y` / `n` / `a`, where `a` is remembered for that one capability for
|
|
852
|
+
the rest of the run and never written to disk — widening it to the session
|
|
853
|
+
would quietly turn an interactive run into `--dangerously-auto-approve`. A run
|
|
854
|
+
with no terminal and no flag refuses rather than assuming yes, because CI is
|
|
855
|
+
exactly where an unattended `git push` would otherwise happen. The tiers are
|
|
856
|
+
meaningful here in a way they would not be for an agent: the caller is a
|
|
857
|
+
deterministic program whose source can be read, so "these calls are always
|
|
858
|
+
fine" is a claim someone can actually justify.
|
|
859
|
+
|
|
860
|
+
A peer that is asking a human sends a pending frame first, which stops the
|
|
861
|
+
caller's timeout. Without it any approval slower than the timeout would fail
|
|
862
|
+
the call and then discard the decision when it finally arrived. The call is
|
|
863
|
+
still failed the moment the socket drops — what actually happens when a peer
|
|
864
|
+
dies mid-prompt — and a peer that sends the frame dishonestly can do nothing
|
|
865
|
+
but keep its own call waiting. A refusal is sent as an answer, so a denied call
|
|
866
|
+
fails its command immediately rather than hanging.
|
|
867
|
+
|
|
868
|
+
Fixes found on the way, each of which broke this path:
|
|
869
|
+
- A websocket upgrade wrote middleware headers (CORS, on every request)
|
|
870
|
+
straight onto the socket, so the first bytes a client saw were headers rather
|
|
871
|
+
than `ws`'s `101` status line and the handshake failed to parse. Header
|
|
872
|
+
writes are now buffered and flushed behind a status line only when the
|
|
873
|
+
upgrade is actually being rejected.
|
|
874
|
+
- An upgrade socket had no error listener while the channel opened, so a client
|
|
875
|
+
that gave up mid-handshake took the whole server process down with an
|
|
876
|
+
unhandled `ECONNRESET`.
|
|
877
|
+
- `onConnect` and `onDisconnect` never saw the session established during the
|
|
878
|
+
upgrade, so a channel could not tell who had just connected.
|
|
879
|
+
- Setting the routing key on a channel result mutated the value in place, which
|
|
880
|
+
throws for a primitive under ESM strict mode.
|
|
881
|
+
|
|
882
|
+
- ba6cc08: fix: stop leaking internal error detail and bound the request body size
|
|
883
|
+
|
|
884
|
+
HTTP error responses no longer forward an error's `payload` or its raw `message` for
|
|
885
|
+
registered 5xx errors — those responses carry the registered error message instead, so an
|
|
886
|
+
internal error that happens to hold a `payload` cannot leak it to the client. Errors
|
|
887
|
+
registered with a 4xx status keep their message and payload, and `exposeErrors` still
|
|
888
|
+
surfaces the full detail outside production.
|
|
889
|
+
|
|
890
|
+
`PikkuFetchHTTPRequest` now caps how much of a request body it buffers, rejecting the
|
|
891
|
+
declared `content-length` up front and measuring the stream as it arrives so a lying or
|
|
892
|
+
absent header cannot exhaust memory. Exceeding the limit throws `PayloadTooLargeError`
|
|
893
|
+
(413). The ceiling defaults to 10MB and is configurable via the new `maxBodySize` option on
|
|
894
|
+
the constructor and on `RunHTTPWiringOptions`.
|
|
895
|
+
|
|
896
|
+
- d007191: `cors()` takes an `exposeHeaders` option.
|
|
897
|
+
|
|
898
|
+
Without `Access-Control-Expose-Headers` a cross-origin caller can read only the
|
|
899
|
+
CORS-safelisted response headers, so any header the client is meant to act on was
|
|
900
|
+
invisible to it. The cross-site session relay in `@pikku/better-auth` is the case that
|
|
901
|
+
surfaced it: the client cannot read `x-pikku-cross-site-set-cookie` off a cross-origin
|
|
902
|
+
response without being told it may.
|
|
903
|
+
|
|
904
|
+
Defaults to none, so nothing new is exposed unless it is named.
|
|
905
|
+
|
|
906
|
+
- a7b26c5: rename the inspected declarations to `define*`: `wireScope` → `defineScope`, `wireSecret` → `defineSecret`, `wireVariable` → `defineVariable`, `wireCredential` → `defineCredential`
|
|
907
|
+
|
|
908
|
+
`wire*` meant two unrelated things. A transport wiring attaches a function to
|
|
909
|
+
something that can invoke it — `wireHTTP`, `wireChannel`, `wireScheduler`,
|
|
910
|
+
`wireQueueWorker` and the rest — and the thing it wires runs. These four wire
|
|
911
|
+
nothing: they are no-ops that exist only so the call typechecks, they are
|
|
912
|
+
tree-shaken out of the build, and their whole job is to be found by the
|
|
913
|
+
inspector's AST pass and turned into a type union. One word for both left the
|
|
914
|
+
declaration reading like a registration with a runtime.
|
|
915
|
+
|
|
916
|
+
So the vocabulary splits: **`wire*` is a transport, `define*` is an inspected
|
|
917
|
+
declaration.**
|
|
918
|
+
|
|
919
|
+
```ts
|
|
920
|
+
import { defineScope } from '@pikku/core/scope'
|
|
921
|
+
import { defineSecret } from '@pikku/core/secret'
|
|
922
|
+
import { defineVariable } from '@pikku/core/variable'
|
|
923
|
+
import { defineCredential } from '@pikku/core/credential'
|
|
924
|
+
|
|
925
|
+
defineScope({ admin: { scopes: { invoices: { scopes: { create: {} } } } } })
|
|
926
|
+
```
|
|
927
|
+
|
|
928
|
+
**Breaking:** no alias is kept. Rename the four call sites; the module subpaths
|
|
929
|
+
(`@pikku/core/scope`, `/secret`, `/variable`) are unchanged.
|
|
930
|
+
|
|
931
|
+
The inspector matches these by identifier text, so a stale `wire*` call is not a
|
|
932
|
+
type error — it is silently not extracted, and the generated union comes back
|
|
933
|
+
empty. That fails as "this scope isn't declared" on code that was fine a moment
|
|
934
|
+
ago, nowhere near the declaration. Grep for the old names rather than trusting a
|
|
935
|
+
clean build.
|
|
936
|
+
|
|
937
|
+
An addon published with `.pikku` output generated before this release re-exports
|
|
938
|
+
`wireSecret` from `@pikku/core/secret` and will not typecheck against this core
|
|
939
|
+
until it is rebuilt and republished.
|
|
940
|
+
|
|
941
|
+
- 457cb25: Add `definePersonas()`: the people a project's scenarios and virtual users run
|
|
942
|
+
as, declared in code.
|
|
943
|
+
|
|
944
|
+
There used to be three names for two-and-a-bit things — an _actor_ in
|
|
945
|
+
`scenarios.actors`, a _persona_ in `scenarios.personas`, and a _virtual user_
|
|
946
|
+
declared separately against an actor. In practice almost every actor was its own
|
|
947
|
+
kind, so the second set carried no information and the third was a third place
|
|
948
|
+
for a name to drift. There is now one declaration:
|
|
949
|
+
|
|
950
|
+
```ts
|
|
951
|
+
definePersonas({
|
|
952
|
+
shopper: {
|
|
953
|
+
name: 'Sam Shopper',
|
|
954
|
+
jobTitle: 'Shopper',
|
|
955
|
+
personality: 'Buys in a hurry and leaves tabs open',
|
|
956
|
+
roles: ['customer'],
|
|
957
|
+
disposition: 'careless',
|
|
958
|
+
goals: ['Buy something without reading anything'],
|
|
959
|
+
account: {},
|
|
960
|
+
},
|
|
961
|
+
})
|
|
962
|
+
```
|
|
963
|
+
|
|
964
|
+
A persona is a person: what they are like, what they want, the roles they hold,
|
|
965
|
+
and **one** account they sign in with — `account: {}` plus `linkedAccounts` for
|
|
966
|
+
the rare case of more, modelled on how better-auth does linking. A persona with a
|
|
967
|
+
`disposition` is a virtual user; `runnable: false` marks someone who only ever
|
|
968
|
+
exists to be acted upon — banned, shared with, reset — and is never handed a
|
|
969
|
+
session.
|
|
970
|
+
|
|
971
|
+
**A persona names roles, never scopes.** Scopes come from `defineSystemRole()`
|
|
972
|
+
expansion, so the build fails if a persona names a role nobody declared, and
|
|
973
|
+
fails again if a role confers a scope no `defineScope` declares. Running one only
|
|
974
|
+
ever has to check that its roles are still valid.
|
|
975
|
+
|
|
976
|
+
**Addresses are computed, never declared.** `personaEmail(id, domain, runId)`
|
|
977
|
+
derives `<id>[+runId]@<domain>` from `scenarios.emailDomain`, so a seed, a
|
|
978
|
+
scenario run and a virtual-user run cannot disagree about who they are signing in
|
|
979
|
+
as. `scenarios.actors` and `scenarios.personas` are gone from
|
|
980
|
+
`pikku.config.json` — only `emailDomain` remains.
|
|
981
|
+
|
|
982
|
+
`actor` survives in exactly one place: the name of a **slot in a scenario step**,
|
|
983
|
+
which is the role a persona is cast in for that step. `pikkuVirtualUser()`,
|
|
984
|
+
`kind`, `grants` and the `actor` field are removed; the `actors` service is now
|
|
985
|
+
`personas`, and the CLI's `virtual-user` commands are now `pikku persona list` /
|
|
986
|
+
`pikku persona run`. `budget` and `allowApprovalRequired` moved to run flags —
|
|
987
|
+
how much you will spend today is not a fact about a person.
|
|
988
|
+
|
|
989
|
+
`@pikku/cucumber` drops its `Actor` class and `ActorDispatchContext`: a
|
|
990
|
+
hand-rolled cookie jar that a persona's own typed session replaces outright.
|
|
991
|
+
|
|
992
|
+
- f7567ad: Add `defineSystemRole()`: roles that ship with the product, declared in code.
|
|
993
|
+
|
|
994
|
+
A system role is to a console-composed role what an AWS managed policy is to a
|
|
995
|
+
customer-managed one — the console may show and grant it, but not rename,
|
|
996
|
+
re-scope or delete it. The CLI extracts declarations by AST and generates a
|
|
997
|
+
`SystemRoleName` union, so naming a role that does not exist fails the build,
|
|
998
|
+
and a role granting a scope no `defineScope` declares fails it too.
|
|
999
|
+
|
|
1000
|
+
Removal is additive on the same terms as `defineScope`: deleting a declaration
|
|
1001
|
+
leaves an inert row rather than revoking everyone's grant mid-deploy.
|
|
1002
|
+
|
|
1003
|
+
`ScopeService` gains `syncSystemRoles`, `findStaleSystemRoles` and
|
|
1004
|
+
`pruneSystemRoles`; `Role` gains `system` and `declared`. Implementations
|
|
1005
|
+
enforce immutability through the shared `assertRoleIsMutable` /
|
|
1006
|
+
`assertRoleNameAvailable` guards rather than each inventing the rule.
|
|
1007
|
+
|
|
1008
|
+
- ba6cc08: Security hardening: removed the gopass secret service and stopped MCP internal errors leaking stack traces.
|
|
1009
|
+
|
|
1010
|
+
**Breaking:** `GopassSecretService` and the `@pikku/core/services/gopass-secrets` subpath export are gone. The service shelled out to the `gopass` binary and its key validation accepted `../`, so a caller-supplied key could traverse out of the configured prefix namespace and read secrets outside it. Rather than harden a shell-out that few projects used, the service is removed. Anyone importing it should implement `SecretService` against their own secret backend. Pre-0.13 breaking changes still ship as a patch.
|
|
1011
|
+
|
|
1012
|
+
MCP internal errors (JSON-RPC `-32603`) previously always attached `data: { message, stack }`, handing any MCP client an internal stack trace. That payload is now gated on `exposeErrors`, which defaults to `!isProduction()` — the same convention `handleHTTPError` already uses. In production a client receives a bare `Internal error` with no `message` and no `stack`; `RunMCPEndpointParams` accepts an explicit `exposeErrors` to override the default.
|
|
1013
|
+
|
|
1014
|
+
- a2e21e5: Keep the persona runtime off the production barrels
|
|
1015
|
+
|
|
1016
|
+
`@pikku/core/services` exported `HttpPersona`, `createHttpPersonas` and
|
|
1017
|
+
`readScenarioHttpResponse` as values, and `@pikku/core/workflow` exported
|
|
1018
|
+
`readScenarioHttpResponse` and `postScenarioJson`. Both are barrels a production
|
|
1019
|
+
server imports, and `http-personas` reaches the actor-flow conversation runner
|
|
1020
|
+
and through it the agent runner — so signing-in-as-a-persona machinery sat in the
|
|
1021
|
+
module graph of every app that imported services.
|
|
1022
|
+
|
|
1023
|
+
Tree-shaking only removes that if you bundle. An unbundled Node or Lambda deploy
|
|
1024
|
+
loads whatever the graph names, which is the case this matters in.
|
|
1025
|
+
|
|
1026
|
+
The values now come from `@pikku/core/persona`, which is where the rest of the
|
|
1027
|
+
persona API already lives. **Types stay exactly where they were** — `import type`
|
|
1028
|
+
erases, so it costs a bundle nothing, and moving them would put core in a cycle
|
|
1029
|
+
with the code that describes its own function types.
|
|
1030
|
+
|
|
1031
|
+
`serialize-personas` generates the new import, so a regenerated
|
|
1032
|
+
`pikku-personas.gen.ts` picks it up with no edit. Anything importing these four
|
|
1033
|
+
values from `@pikku/core/services` or `@pikku/core/workflow` changes the
|
|
1034
|
+
specifier to `@pikku/core/persona`; the names and signatures are unchanged.
|
|
1035
|
+
|
|
1036
|
+
A test walks each barrel's value-import graph and fails if scenario runtime
|
|
1037
|
+
reappears, so this cannot regress quietly.
|
|
1038
|
+
|
|
1039
|
+
- 457cb25: Let a persona do a real job in production, and say where it may act.
|
|
1040
|
+
|
|
1041
|
+
A persona was only ever a test subject: something you pointed at a stage to find
|
|
1042
|
+
out what the product does wrong. But the same declaration — a name, a job, the
|
|
1043
|
+
roles it holds and what it is trying to get done — describes a teammate doing
|
|
1044
|
+
the work for real, and nothing about the engine cared which one it was.
|
|
1045
|
+
|
|
1046
|
+
Four changes make that difference explicit and enforced.
|
|
1047
|
+
|
|
1048
|
+
**`environments` moves to the top level of `pikku.config.json`**, out from under
|
|
1049
|
+
`scenarios`. It was never a scenario's anything: `persona run` targets one, and
|
|
1050
|
+
now so does `persona sync`. An environment may be flagged `production: true` —
|
|
1051
|
+
a flag rather than a reserved name, because projects call it `prod`, `live` or
|
|
1052
|
+
`eu-prod`, and more than one environment can be production.
|
|
1053
|
+
|
|
1054
|
+
**A persona may name its `environments`.** Omitting them means every configured
|
|
1055
|
+
environment _except_ the production ones, so nothing reaches production by being
|
|
1056
|
+
forgotten. Naming a production environment requires `disposition: 'accountable'`.
|
|
1057
|
+
The rule is checked twice, on purpose: the inspector refuses to generate a
|
|
1058
|
+
declaration that breaks it, and sign-in re-checks against the environment
|
|
1059
|
+
actually resolved — the build check trusts the file, and the run check does not
|
|
1060
|
+
trust which artifact got deployed. An unresolved environment fails closed.
|
|
1061
|
+
|
|
1062
|
+
**`disposition: 'accountable'`** is that production disposition. It sits opposite
|
|
1063
|
+
`adversarial` on the intent axis rather than the care axis: what it changes stays
|
|
1064
|
+
changed, every call is recorded against its name, and it stops to ask rather than
|
|
1065
|
+
acting and reporting afterwards. Alongside it, **agents now appear in a persona's
|
|
1066
|
+
computed catalogue**, gated by the same scopes as the RPCs — an agent is reached
|
|
1067
|
+
rather than declared, so a persona finds the specialists its roles unlock and
|
|
1068
|
+
chooses between calling the API itself and handing the work over. That also fixes
|
|
1069
|
+
a latent gap: `talkTo` was wired at the target but never advertised in the
|
|
1070
|
+
instructions, so it was never used.
|
|
1071
|
+
|
|
1072
|
+
**`pikku persona sync <environment>`** provisions them: it creates each account
|
|
1073
|
+
and applies the roles it declares, additively, and never revokes. Seeding is test
|
|
1074
|
+
data and `db seed` does not run in production; a teammate doing a real job still
|
|
1075
|
+
needs an account and its grants. It needs both halves of an environment — its API
|
|
1076
|
+
to sign the person in, its database to write the grants — and `--dry-run` reports
|
|
1077
|
+
who would be provisioned, with what, and why anyone was skipped.
|
|
1078
|
+
|
|
1079
|
+
In the console, a virtual user now says where it may act — the environments it
|
|
1080
|
+
named, or the rule when it named none — and its dossier carries the `sync`
|
|
1081
|
+
command alongside the `run` one, because the account is not a by-product of a
|
|
1082
|
+
run. `accountable` reads as a disposition like the rest.
|
|
1083
|
+
|
|
1084
|
+
- 86a50b9: scenario: replace `browser: true` + `func` with per-surface bindings on `pikkuScenarioStep`
|
|
1085
|
+
|
|
1086
|
+
A step now declares one implementation per surface it can be driven through:
|
|
1087
|
+
|
|
1088
|
+
```ts
|
|
1089
|
+
export const buysTheItem = pikkuScenarioStep<{ sku: string }, { orderId: string }>({
|
|
1090
|
+
name: 'buysTheItem',
|
|
1091
|
+
description: 'buys the item',
|
|
1092
|
+
browser: async (services, data, { browser }) => { ... },
|
|
1093
|
+
default: async (services, data, { rpc }) => { ... },
|
|
1094
|
+
})
|
|
1095
|
+
```
|
|
1096
|
+
|
|
1097
|
+
`pikku scenario run --run browser|cli|default` picks which surface the run drives,
|
|
1098
|
+
and the two phases resolve bindings differently:
|
|
1099
|
+
- **Actions** (`given` / `when` / `step`) run exactly one binding — the run
|
|
1100
|
+
surface if it has one, otherwise `default`. A step with neither now fails with
|
|
1101
|
+
`ScenarioNoSurfaceBinding` instead of silently running server-side.
|
|
1102
|
+
- **Assertions** (`then`) are witnesses, not alternatives: every declared binding
|
|
1103
|
+
runs and they must agree. Two surfaces reporting different things fails the run
|
|
1104
|
+
with `ScenarioWitnessDisagreement` rather than reporting a pass. An assertion
|
|
1105
|
+
with no witness the run can execute at all fails with `ScenarioNoWitness` —
|
|
1106
|
+
without it the step returns `undefined` and renders as a tick, reporting a pass
|
|
1107
|
+
for something nobody checked.
|
|
1108
|
+
|
|
1109
|
+
A scenario written as a step ladder that never calls `then` is now a **PKU680**
|
|
1110
|
+
critical. It proves only that nothing threw, so an assertion-free ladder of
|
|
1111
|
+
browser-bound actions would score perfect coverage while checking nothing.
|
|
1112
|
+
|
|
1113
|
+
The report gains a surface-coverage line — `n/m steps ran on browser`, counted
|
|
1114
|
+
over every step, so an action that fell back to the server lowers the ratio
|
|
1115
|
+
rather than needing a footnote. That also makes surfaces comparable over one
|
|
1116
|
+
denominator: a scenario is `4/4` on a default run and `3/4` on a browser one.
|
|
1117
|
+
Assertions that fell back are named separately and gate `--strict`, since a
|
|
1118
|
+
sentence claiming the actor saw something nobody looked at is a different problem
|
|
1119
|
+
from an action taking a shortcut.
|
|
1120
|
+
|
|
1121
|
+
**Breaking:** `browser: true` and the third `B extends boolean` type argument are
|
|
1122
|
+
gone. Rename `func` to `default` (or to `browser` where the step drove a browser)
|
|
1123
|
+
and drop the type argument.
|
|
1124
|
+
|
|
1125
|
+
- 0e0f6eb: Add virtual users: LLM-driven synthetic users that work a real stage in
|
|
1126
|
+
character.
|
|
1127
|
+
|
|
1128
|
+
A scenario proves a path somebody thought of. A virtual user works the same
|
|
1129
|
+
ground without the script — it signs in as a declared persona over the app's own
|
|
1130
|
+
auth, is handed the scenarios' BDD prose and the schema of every endpoint it may
|
|
1131
|
+
reach, and decides for itself what to do. It asserts nothing; a run produces
|
|
1132
|
+
findings, and their absence only ever means "not this time, not with this seed".
|
|
1133
|
+
|
|
1134
|
+
There is nothing extra to declare. A persona with a `disposition` is a virtual
|
|
1135
|
+
user, and running it is what makes it one — see the `definePersonas` changeset
|
|
1136
|
+
for the declaration itself. Listing, describing or running one never loads the
|
|
1137
|
+
app: the inspector reads the literal declaration, the CLI writes
|
|
1138
|
+
`scopes/pikku-personas-meta.gen.json`, and `MetaService.getPersonasMeta()`
|
|
1139
|
+
serves it.
|
|
1140
|
+
|
|
1141
|
+
**Dispositions are engine dials, not prose.** Each carries its own intent weights
|
|
1142
|
+
(continue / suspend / resume / abandon), temperature, re-read and repeat rates,
|
|
1143
|
+
and switches: `careless` puts things down and picks them up in the wrong order,
|
|
1144
|
+
`newcomer` starts with no memory, `auditor` is never offered a mutation,
|
|
1145
|
+
`adversarial` is shown the catalogue its roles do not cover — being offered a
|
|
1146
|
+
call it should not be able to make is the test — while those roles stay live as
|
|
1147
|
+
the oracle, so a success outside them is authorization drift rather than a pass.
|
|
1148
|
+
|
|
1149
|
+
**Nothing is retrieved against.** The whole reachable catalogue goes into the
|
|
1150
|
+
instructions (~8k tokens on a 430-RPC project, cached for the run), because a
|
|
1151
|
+
ranking function would make the user only as adventurous as the ranking and lose
|
|
1152
|
+
exactly the endpoints worth stumbling into. Schema first: an endpoint must be
|
|
1153
|
+
described before it may be called.
|
|
1154
|
+
|
|
1155
|
+
**No money in core.** The engine counts steps, calls, mutations and tokens; what
|
|
1156
|
+
they cost is the app's to decide through `stop(tally)`.
|
|
1157
|
+
|
|
1158
|
+
CLI: `pikku persona list` and `pikku persona run <environment> [name]`, with
|
|
1159
|
+
flags overriding a declaration for reproduction (`--seed`, `--steps`,
|
|
1160
|
+
`--disposition`). Spending is a run flag too — `--steps`, `--mutations` and
|
|
1161
|
+
`--duration` bound a run, because how much you will spend today is not a fact
|
|
1162
|
+
about a person. Console: a Virtual Users screen beside Scenarios, built out of
|
|
1163
|
+
core's own derivation functions so it shows a run's actual inputs rather than a
|
|
1164
|
+
second implementation of them.
|
|
1165
|
+
|
|
1166
|
+
`dev-ai-runner` now ships its own `@pikku/ai-vercel` and
|
|
1167
|
+
`@ai-sdk/openai-compatible` instead of requiring them from the project. Behind a
|
|
1168
|
+
proxy one openai-compatible provider answers for every prefix, so there was never
|
|
1169
|
+
a per-vendor package worth making somebody install; the project's copies still
|
|
1170
|
+
win when it has them, and both load from the same place or neither does.
|
|
1171
|
+
|
|
1
1172
|
## 0.12.72
|
|
2
1173
|
|
|
3
1174
|
### Patch Changes
|