@pikku/core 0.12.71 → 0.12.74
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +848 -0
- package/dist/crypto-utils.d.ts +30 -5
- package/dist/crypto-utils.js +146 -41
- package/dist/dev/hot-reload.js +11 -30
- package/dist/dev/module-runner.d.ts +3 -7
- package/dist/dev/module-runner.js +4 -10
- package/dist/dev/reload-meta.d.ts +8 -20
- package/dist/dev/reload-meta.js +9 -29
- package/dist/errors/error-handler.d.ts +5 -30
- package/dist/errors/error-handler.js +16 -32
- package/dist/errors/errors.d.ts +32 -151
- package/dist/errors/errors.js +55 -157
- package/dist/function/abort-scope.d.ts +47 -0
- package/dist/function/abort-scope.js +63 -0
- package/dist/function/function-runner.js +40 -30
- package/dist/function/functions.types.d.ts +44 -136
- package/dist/function/functions.types.js +0 -58
- package/dist/function/list.types.d.ts +12 -62
- package/dist/function/list.types.js +4 -25
- package/dist/handle-error.d.ts +0 -11
- package/dist/handle-error.js +6 -18
- package/dist/index.d.ts +5 -2
- package/dist/index.js +4 -1
- package/dist/middleware/auth-apikey.d.ts +3 -18
- package/dist/middleware/auth-apikey.js +0 -17
- package/dist/middleware/auth-bearer.d.ts +6 -41
- package/dist/middleware/auth-bearer.js +3 -40
- package/dist/middleware/auth-cookie.d.ts +5 -27
- package/dist/middleware/auth-cookie.js +2 -26
- package/dist/middleware/cors.d.ts +7 -34
- package/dist/middleware/cors.js +7 -34
- package/dist/middleware/remote-auth.d.ts +3 -1
- package/dist/middleware/remote-auth.js +3 -2
- package/dist/middleware/telemetry.d.ts +8 -33
- package/dist/middleware/telemetry.js +2 -31
- package/dist/middleware-runner.d.ts +4 -55
- package/dist/middleware-runner.js +5 -74
- package/dist/permissions.d.ts +3 -44
- package/dist/permissions.js +19 -71
- package/dist/pikku-request.d.ts +0 -6
- package/dist/pikku-request.js +0 -6
- package/dist/pikku-state.d.ts +0 -26
- package/dist/pikku-state.js +2 -30
- package/dist/remote.d.ts +3 -5
- package/dist/remote.js +8 -7
- package/dist/schema.d.ts +7 -17
- package/dist/schema.js +30 -18
- package/dist/scopes.d.ts +4 -23
- package/dist/scopes.js +7 -48
- package/dist/services/ai-agent-runner-service.d.ts +20 -0
- package/dist/services/ai-embedding-service.d.ts +2 -25
- package/dist/services/audit-service.js +1 -2
- package/dist/services/content-service.d.ts +1 -46
- package/dist/services/credential-service.d.ts +3 -40
- package/dist/services/deployment-service.d.ts +3 -9
- package/dist/services/gateway-service.d.ts +0 -15
- package/dist/services/http-personas.d.ts +80 -0
- package/dist/services/http-personas.js +233 -0
- package/dist/services/in-memory-queue-service.d.ts +0 -14
- package/dist/services/in-memory-queue-service.js +1 -15
- package/dist/services/in-memory-trigger-service.d.ts +0 -18
- package/dist/services/in-memory-trigger-service.js +1 -18
- package/dist/services/in-memory-workflow-service.d.ts +0 -16
- package/dist/services/in-memory-workflow-service.js +4 -33
- package/dist/services/index.d.ts +5 -6
- package/dist/services/index.js +2 -5
- package/dist/services/istanbul-coverage-service.d.ts +1 -5
- package/dist/services/istanbul-coverage-service.js +2 -8
- package/dist/services/jwt-service.d.ts +1 -16
- package/dist/services/local-content.d.ts +13 -2
- package/dist/services/local-content.js +40 -13
- package/dist/services/local-gateway-service.d.ts +0 -16
- package/dist/services/local-gateway-service.js +2 -17
- package/dist/services/local-secrets.d.ts +0 -4
- package/dist/services/local-secrets.js +0 -4
- package/dist/services/logger-console.d.ts +3 -7
- package/dist/services/logger-console.js +3 -7
- package/dist/services/logger.d.ts +2 -37
- package/dist/services/meta-service.d.ts +23 -26
- package/dist/services/meta-service.js +22 -36
- package/dist/services/personas-service.d.ts +134 -0
- package/dist/services/personas-service.js +40 -0
- package/dist/services/pikku-user-id.js +0 -4
- package/dist/services/queue-webhook-service.d.ts +2 -36
- package/dist/services/queue-webhook-service.js +9 -41
- package/dist/services/scheduler-service.d.ts +1 -50
- package/dist/services/scheduler-service.js +0 -10
- package/dist/services/schema-service.d.ts +1 -24
- package/dist/services/scope-service.d.ts +49 -34
- package/dist/services/scoped-secret-service.d.ts +0 -4
- package/dist/services/scoped-secret-service.js +0 -4
- package/dist/services/secret-host-binding.d.ts +8 -0
- package/dist/services/secret-host-binding.js +36 -0
- package/dist/services/secret-service.d.ts +5 -33
- package/dist/services/secretless.d.ts +6 -0
- package/dist/services/secretless.js +21 -0
- package/dist/services/stub-tracker.d.ts +7 -18
- package/dist/services/stub-tracker.js +8 -18
- package/dist/services/system-role-guard.d.ts +33 -0
- package/dist/services/system-role-guard.js +38 -0
- package/dist/services/trigger-service.d.ts +0 -12
- package/dist/services/typed-secret-service.d.ts +0 -7
- package/dist/services/typed-secret-service.js +1 -7
- package/dist/services/v8-coverage-service.d.ts +2 -3
- package/dist/services/v8-coverage-service.js +1 -2
- package/dist/services/variables-service.d.ts +1 -8
- package/dist/services/webhook-service.d.ts +19 -63
- package/dist/services/webhook-service.js +6 -20
- package/dist/services/workflow-service.d.ts +3 -15
- package/dist/testing/service-tests.js +0 -17
- package/dist/time-utils.d.ts +0 -16
- package/dist/time-utils.js +1 -19
- package/dist/types/core.types.d.ts +99 -219
- package/dist/types/core.types.js +0 -42
- package/dist/types/state.types.d.ts +4 -9
- package/dist/utils/hmac.d.ts +4 -10
- package/dist/utils/hmac.js +4 -10
- package/dist/utils/safe-fetch.d.ts +7 -35
- package/dist/utils/safe-fetch.js +13 -53
- package/dist/utils.d.ts +1 -6
- package/dist/utils.js +6 -15
- package/dist/wirings/actor-flow/actor-flow.types.d.ts +1 -34
- package/dist/wirings/actor-flow/index.d.ts +0 -9
- package/dist/wirings/actor-flow/run-conversation.d.ts +5 -5
- package/dist/wirings/actor-flow/run-conversation.js +14 -7
- package/dist/wirings/ai-agent/ai-agent-agui.d.ts +0 -5
- package/dist/wirings/ai-agent/ai-agent-agui.js +34 -12
- package/dist/wirings/ai-agent/ai-agent-helpers.d.ts +7 -0
- package/dist/wirings/ai-agent/ai-agent-helpers.js +7 -0
- package/dist/wirings/ai-agent/ai-agent-interrupt.d.ts +153 -0
- package/dist/wirings/ai-agent/ai-agent-interrupt.js +256 -0
- package/dist/wirings/ai-agent/ai-agent-memory.js +0 -2
- package/dist/wirings/ai-agent/ai-agent-model-config.d.ts +0 -9
- package/dist/wirings/ai-agent/ai-agent-model-config.js +1 -9
- package/dist/wirings/ai-agent/ai-agent-prepare.d.ts +10 -99
- package/dist/wirings/ai-agent/ai-agent-prepare.js +58 -131
- package/dist/wirings/ai-agent/ai-agent-registry.d.ts +2 -1
- package/dist/wirings/ai-agent/ai-agent-registry.js +5 -1
- package/dist/wirings/ai-agent/ai-agent-runner.js +49 -20
- package/dist/wirings/ai-agent/ai-agent-stream.d.ts +25 -2
- package/dist/wirings/ai-agent/ai-agent-stream.js +153 -63
- package/dist/wirings/ai-agent/ai-agent.types.d.ts +84 -4
- package/dist/wirings/ai-agent/index.d.ts +6 -4
- package/dist/wirings/ai-agent/index.js +5 -4
- package/dist/wirings/ai-agent/voice-input.d.ts +39 -1
- package/dist/wirings/ai-agent/voice-input.js +46 -3
- package/dist/wirings/ai-agent/voice-output.d.ts +54 -1
- package/dist/wirings/ai-agent/voice-output.js +153 -50
- package/dist/wirings/channel/channel-common.d.ts +7 -20
- package/dist/wirings/channel/channel-common.js +7 -21
- package/dist/wirings/channel/channel-handler.js +25 -6
- package/dist/wirings/channel/channel-host-rpc.d.ts +25 -0
- package/dist/wirings/channel/channel-host-rpc.js +38 -0
- package/dist/wirings/channel/channel-middleware-runner.d.ts +0 -12
- package/dist/wirings/channel/channel-middleware-runner.js +0 -12
- package/dist/wirings/channel/channel-rpc-registry.d.ts +31 -0
- package/dist/wirings/channel/channel-rpc-registry.js +89 -0
- package/dist/wirings/channel/channel-rpc-responder.d.ts +15 -0
- package/dist/wirings/channel/channel-rpc-responder.js +71 -0
- package/dist/wirings/channel/channel-rpc-service.d.ts +40 -0
- package/dist/wirings/channel/channel-rpc-service.js +106 -0
- package/dist/wirings/channel/channel-rpc-validators.d.ts +14 -0
- package/dist/wirings/channel/channel-rpc-validators.js +30 -0
- package/dist/wirings/channel/channel-rpc.d.ts +5 -0
- package/dist/wirings/channel/channel-rpc.js +5 -0
- package/dist/wirings/channel/channel-rpc.types.d.ts +90 -0
- package/dist/wirings/channel/channel-rpc.types.js +50 -0
- package/dist/wirings/channel/channel-runner.d.ts +0 -4
- package/dist/wirings/channel/channel-runner.js +0 -14
- package/dist/wirings/channel/channel-store.d.ts +0 -10
- package/dist/wirings/channel/channel.types.d.ts +12 -1
- package/dist/wirings/channel/define-channel-routes.d.ts +0 -20
- package/dist/wirings/channel/define-channel-routes.js +0 -20
- package/dist/wirings/channel/eventhub-service.d.ts +0 -18
- package/dist/wirings/channel/index.d.ts +4 -1
- package/dist/wirings/channel/index.js +2 -0
- package/dist/wirings/channel/local/local-channel-runner.js +3 -1
- package/dist/wirings/channel/local/local-eventhub-service.d.ts +0 -33
- package/dist/wirings/channel/local/local-eventhub-service.js +2 -36
- package/dist/wirings/channel/log-channels.d.ts +0 -4
- package/dist/wirings/channel/log-channels.js +0 -4
- package/dist/wirings/channel/pikku-abstract-channel-handler.js +6 -0
- package/dist/wirings/channel/serverless/serverless-channel-runner.js +2 -5
- package/dist/wirings/cli/channel/cli-approval.d.ts +41 -0
- package/dist/wirings/cli/channel/cli-approval.js +81 -0
- package/dist/wirings/cli/channel/cli-channel-runner.d.ts +0 -4
- package/dist/wirings/cli/channel/cli-channel-runner.js +3 -25
- package/dist/wirings/cli/channel/cli-raw-channel-runner.d.ts +47 -9
- package/dist/wirings/cli/channel/cli-raw-channel-runner.js +24 -16
- package/dist/wirings/cli/channel/cli-raw-client-runner.d.ts +20 -0
- package/dist/wirings/cli/channel/cli-raw-client-runner.js +121 -0
- package/dist/wirings/cli/channel/index.d.ts +4 -0
- package/dist/wirings/cli/channel/index.js +2 -0
- package/dist/wirings/cli/cli-runner.d.ts +20 -20
- package/dist/wirings/cli/cli-runner.js +28 -89
- package/dist/wirings/cli/cli.types.d.ts +14 -3
- package/dist/wirings/cli/command-parser.d.ts +1 -10
- package/dist/wirings/cli/command-parser.js +10 -87
- package/dist/wirings/cli/define-cli-commands.d.ts +1 -17
- package/dist/wirings/cli/define-cli-commands.js +1 -17
- package/dist/wirings/credential/credential.types.d.ts +0 -12
- package/dist/wirings/credential/define-credential.d.ts +48 -0
- package/dist/wirings/credential/define-credential.js +47 -0
- package/dist/wirings/credential/index.d.ts +1 -1
- package/dist/wirings/credential/index.js +1 -1
- package/dist/wirings/credential/validate-credential-definitions.d.ts +2 -4
- package/dist/wirings/gateway/gateway-runner.d.ts +1 -20
- package/dist/wirings/gateway/gateway-runner.js +8 -105
- package/dist/wirings/gateway/gateway.types.d.ts +7 -80
- package/dist/wirings/http/http-routes.d.ts +0 -63
- package/dist/wirings/http/http-routes.js +0 -63
- package/dist/wirings/http/http-runner.d.ts +1 -100
- package/dist/wirings/http/http-runner.js +12 -166
- package/dist/wirings/http/http.types.d.ts +16 -55
- package/dist/wirings/http/index.d.ts +2 -1
- package/dist/wirings/http/index.js +1 -1
- package/dist/wirings/http/log-http-routes.d.ts +0 -4
- package/dist/wirings/http/log-http-routes.js +0 -4
- package/dist/wirings/http/pikku-fetch-http-request.d.ts +6 -36
- package/dist/wirings/http/pikku-fetch-http-request.js +56 -50
- package/dist/wirings/http/pikku-fetch-http-response.js +0 -3
- package/dist/wirings/http/routers/path-to-regex.js +2 -13
- package/dist/wirings/http/web-request.d.ts +0 -8
- package/dist/wirings/http/web-request.js +25 -17
- package/dist/wirings/mcp/mcp-runner.d.ts +2 -0
- package/dist/wirings/mcp/mcp-runner.js +6 -16
- package/dist/wirings/mcp/mcp.types.d.ts +2 -35
- package/dist/wirings/oauth2/oauth2.types.d.ts +0 -28
- package/dist/wirings/oauth2/oauth2.types.js +0 -3
- package/dist/wirings/persona/define-personas.d.ts +28 -0
- package/dist/wirings/persona/define-personas.js +27 -0
- package/dist/wirings/persona/index.d.ts +21 -0
- package/dist/wirings/persona/index.js +17 -0
- package/dist/wirings/persona/persona-email.d.ts +37 -0
- package/dist/wirings/persona/persona-email.js +69 -0
- package/dist/wirings/persona/persona-environments.d.ts +45 -0
- package/dist/wirings/persona/persona-environments.js +81 -0
- package/dist/wirings/persona/persona-mailbox.d.ts +101 -0
- package/dist/wirings/persona/persona-mailbox.js +53 -0
- package/dist/wirings/persona/persona.types.d.ts +125 -0
- package/dist/wirings/persona/persona.types.js +1 -0
- package/dist/wirings/persona/validate-personas.d.ts +53 -0
- package/dist/wirings/persona/validate-personas.js +94 -0
- package/dist/wirings/queue/index.d.ts +3 -0
- package/dist/wirings/queue/index.js +2 -3
- package/dist/wirings/queue/queue-identity.d.ts +28 -0
- package/dist/wirings/queue/queue-identity.js +102 -0
- package/dist/wirings/queue/queue-runner.d.ts +0 -19
- package/dist/wirings/queue/queue-runner.js +9 -30
- package/dist/wirings/queue/queue.types.d.ts +18 -89
- package/dist/wirings/queue/register-queue-helper.d.ts +0 -12
- package/dist/wirings/queue/register-queue-helper.js +0 -11
- package/dist/wirings/queue/signed-queue-service.d.ts +16 -0
- package/dist/wirings/queue/signed-queue-service.js +42 -0
- package/dist/wirings/queue/validate-worker-config.d.ts +2 -23
- package/dist/wirings/queue/validate-worker-config.js +0 -14
- package/dist/wirings/role/define-system-role.d.ts +32 -0
- package/dist/wirings/role/define-system-role.js +31 -0
- package/dist/wirings/role/index.d.ts +3 -0
- package/dist/wirings/role/index.js +2 -0
- package/dist/wirings/role/role.types.d.ts +43 -0
- package/dist/wirings/role/role.types.js +1 -0
- package/dist/wirings/role/validate-role-definitions.d.ts +21 -0
- package/dist/wirings/role/validate-role-definitions.js +71 -0
- package/dist/wirings/rpc/addon-runner.d.ts +0 -19
- package/dist/wirings/rpc/addon-runner.js +0 -51
- package/dist/wirings/rpc/remote-addon-auth.d.ts +1 -12
- package/dist/wirings/rpc/remote-addon-auth.js +1 -9
- package/dist/wirings/rpc/rpc-runner.d.ts +11 -18
- package/dist/wirings/rpc/rpc-runner.js +88 -105
- package/dist/wirings/rpc/rpc-types.d.ts +7 -6
- package/dist/wirings/rpc/wire-addon.d.ts +25 -0
- package/dist/wirings/rpc/wire-addon.js +62 -0
- package/dist/wirings/rpc/wire-remote-addon.d.ts +3 -28
- package/dist/wirings/rpc/wire-remote-addon.js +0 -8
- package/dist/wirings/scheduler/log-schedulers.d.ts +0 -4
- package/dist/wirings/scheduler/log-schedulers.js +0 -4
- package/dist/wirings/scheduler/scheduler-runner.d.ts +0 -1
- package/dist/wirings/scheduler/scheduler-runner.js +0 -1
- package/dist/wirings/scheduler/scheduler.types.d.ts +1 -14
- package/dist/wirings/scope/define-scope.d.ts +32 -0
- package/dist/wirings/scope/define-scope.js +31 -0
- package/dist/wirings/scope/index.d.ts +1 -1
- package/dist/wirings/scope/index.js +1 -1
- package/dist/wirings/scope/scope.types.d.ts +7 -9
- package/dist/wirings/scope/validate-scope-definitions.d.ts +2 -21
- package/dist/wirings/scope/validate-scope-definitions.js +3 -21
- package/dist/wirings/secret/index.d.ts +1 -1
- package/dist/wirings/secret/index.js +1 -1
- package/dist/wirings/secret/secret.types.d.ts +19 -15
- package/dist/wirings/secret/secret.types.js +1 -1
- package/dist/wirings/secret/validate-secret-definitions.d.ts +2 -4
- package/dist/wirings/trigger/trigger-runner.d.ts +0 -27
- package/dist/wirings/trigger/trigger-runner.js +1 -24
- package/dist/wirings/trigger/trigger.types.d.ts +1 -82
- package/dist/wirings/trigger/trigger.types.js +0 -34
- package/dist/wirings/variable/index.d.ts +1 -1
- package/dist/wirings/variable/index.js +1 -1
- package/dist/wirings/variable/validate-variable-definitions.d.ts +2 -4
- package/dist/wirings/variable/variable.types.d.ts +1 -13
- package/dist/wirings/variable/variable.types.js +1 -1
- package/dist/wirings/virtual-user/index.d.ts +27 -0
- package/dist/wirings/virtual-user/index.js +8 -0
- package/dist/wirings/virtual-user/run-virtual-user.d.ts +92 -0
- package/dist/wirings/virtual-user/run-virtual-user.js +478 -0
- package/dist/wirings/virtual-user/virtual-user-agents.d.ts +38 -0
- package/dist/wirings/virtual-user/virtual-user-agents.js +24 -0
- package/dist/wirings/virtual-user/virtual-user-catalogue.d.ts +92 -0
- package/dist/wirings/virtual-user/virtual-user-catalogue.js +134 -0
- package/dist/wirings/virtual-user/virtual-user-derive.d.ts +26 -0
- package/dist/wirings/virtual-user/virtual-user-derive.js +137 -0
- package/dist/wirings/virtual-user/virtual-user-dispositions.d.ts +79 -0
- package/dist/wirings/virtual-user/virtual-user-dispositions.js +128 -0
- package/dist/wirings/virtual-user/virtual-user-intents.d.ts +78 -0
- package/dist/wirings/virtual-user/virtual-user-intents.js +142 -0
- package/dist/wirings/virtual-user/virtual-user-rng.d.ts +24 -0
- package/dist/wirings/virtual-user/virtual-user-rng.js +44 -0
- package/dist/wirings/virtual-user/virtual-user-target.d.ts +21 -0
- package/dist/wirings/virtual-user/virtual-user-target.js +34 -0
- package/dist/wirings/virtual-user/virtual-user.types.d.ts +199 -0
- package/dist/wirings/virtual-user/virtual-user.types.js +8 -0
- package/dist/wirings/workflow/dsl/workflow-dsl.types.d.ts +5 -5
- package/dist/wirings/workflow/dsl/workflow-runner.d.ts +0 -4
- package/dist/wirings/workflow/dsl/workflow-runner.js +0 -4
- package/dist/wirings/workflow/feature.d.ts +0 -19
- package/dist/wirings/workflow/feature.js +0 -19
- package/dist/wirings/workflow/graph/graph-node.d.ts +0 -98
- package/dist/wirings/workflow/graph/graph-node.js +0 -34
- package/dist/wirings/workflow/graph/graph-runner.js +6 -41
- package/dist/wirings/workflow/graph/wire-workflow-graph.d.ts +0 -4
- package/dist/wirings/workflow/graph/workflow-graph.types.d.ts +0 -58
- package/dist/wirings/workflow/graph/workflow-graph.types.js +0 -6
- package/dist/wirings/workflow/index.d.ts +5 -7
- package/dist/wirings/workflow/index.js +3 -17
- package/dist/wirings/workflow/pikku-scenario-service.d.ts +87 -5
- package/dist/wirings/workflow/pikku-scenario-service.js +204 -42
- package/dist/wirings/workflow/pikku-workflow-service.d.ts +7 -459
- package/dist/wirings/workflow/pikku-workflow-service.js +58 -551
- package/dist/wirings/workflow/run-timeline.d.ts +0 -47
- package/dist/wirings/workflow/run-timeline.js +0 -22
- package/dist/wirings/workflow/scenario-cookie-jar.d.ts +0 -23
- package/dist/wirings/workflow/scenario-cookie-jar.js +0 -16
- package/dist/wirings/workflow/scenario-poll.d.ts +0 -15
- package/dist/wirings/workflow/scenario-poll.js +0 -12
- package/dist/wirings/workflow/scenario-prose.d.ts +0 -28
- package/dist/wirings/workflow/scenario-prose.js +0 -18
- package/dist/wirings/workflow/scenario-step-guards.d.ts +0 -13
- package/dist/wirings/workflow/scenario-step-guards.js +1 -14
- package/dist/wirings/workflow/scenario-step.types.d.ts +72 -6
- package/dist/wirings/workflow/scenario-step.types.js +5 -1
- package/dist/wirings/workflow/scenario-surface.d.ts +16 -0
- package/dist/wirings/workflow/scenario-surface.js +56 -0
- package/dist/wirings/workflow/workflow-invocation-id.d.ts +0 -18
- package/dist/wirings/workflow/workflow-invocation-id.js +2 -22
- package/dist/wirings/workflow/workflow-queue-workers.d.ts +0 -20
- package/dist/wirings/workflow/workflow-queue-workers.js +0 -19
- package/dist/wirings/workflow/workflow.types.d.ts +0 -197
- package/knowledge/decisions/index.md +19 -0
- package/knowledge/decisions/internals/a-secret-that-fails-to-decrypt-fails-the-whole-read.md +49 -0
- package/knowledge/decisions/internals/actor-flow-conversations-seed-a-hidden-kickoff-message.md +23 -0
- package/knowledge/decisions/internals/actor-flow-drives-the-target-through-a-transport-seam.md +24 -0
- package/knowledge/decisions/internals/actor-flow-verdicts-are-llm-self-evaluations.md +25 -0
- package/knowledge/decisions/internals/addon-package-roots-resolve-by-walking-node-module-search-paths.md +26 -0
- package/knowledge/decisions/internals/addon-singleton-services-are-cached-per-namespace-not-per-package.md +33 -0
- package/knowledge/decisions/internals/addon-workflow-names-are-prefixed-with-the-consumer-namespace.md +28 -0
- package/knowledge/decisions/internals/ai-agent-agui-bridge-obeys-the-client-ordering-contract.md +29 -0
- package/knowledge/decisions/internals/ai-agent-audio-chunks-carry-the-format-the-provider-returned.md +20 -0
- package/knowledge/decisions/internals/ai-agent-credential-suspensions-hide-the-tool-result.md +26 -0
- package/knowledge/decisions/internals/ai-agent-delegate-and-supervise-hide-different-text.md +26 -0
- package/knowledge/decisions/internals/ai-agent-llm-tool-arguments-have-nulls-stripped.md +23 -0
- package/knowledge/decisions/internals/ai-agent-model-config-stays-a-single-resolution-seam.md +25 -0
- package/knowledge/decisions/internals/ai-agent-onerror-hooks-cannot-change-the-failure.md +22 -0
- package/knowledge/decisions/internals/ai-agent-runner-methods-must-keep-their-receiver.md +22 -0
- package/knowledge/decisions/internals/ai-agent-stream-persistence-is-best-effort.md +27 -0
- package/knowledge/decisions/internals/ai-agent-sub-agents-inherit-the-parent-context-block.md +26 -0
- package/knowledge/decisions/internals/ai-agent-tool-execute-failures-are-logged-unconditionally.md +25 -0
- package/knowledge/decisions/internals/ai-agent-voice-input-transcribes-audio-parts-in-place.md +22 -0
- package/knowledge/decisions/internals/ai-agent-working-memory-is-persisted-only-when-valid.md +25 -0
- package/knowledge/decisions/internals/channel-message-handlers-accept-three-config-shapes.md +30 -0
- package/knowledge/decisions/internals/channel-middleware-caches-only-statically-resolved-middleware.md +31 -0
- package/knowledge/decisions/internals/channel-state-is-per-socket-session-state-is-per-user.md +29 -0
- package/knowledge/decisions/internals/channel-user-id-is-persisted-after-onconnect-middleware-runs.md +28 -0
- package/knowledge/decisions/internals/cli-option-names-are-camelcase-in-state-and-kebab-on-the-command-line.md +27 -0
- package/knowledge/decisions/internals/cli-parse-errors-are-routed-by-message-prefix.md +28 -0
- package/knowledge/decisions/internals/cli-stdout-is-reserved-for-machine-readable-output.md +34 -0
- package/knowledge/decisions/internals/cli-unknown-long-options-warn-instead-of-failing.md +29 -0
- package/knowledge/decisions/internals/core-data-classification-brand-is-an-optional-property.md +34 -0
- package/knowledge/decisions/internals/core-function-runner-restores-the-wire-fields-it-overwrites.md +44 -0
- package/knowledge/decisions/internals/core-hot-reload-merges-generated-meta-never-replaces-it.md +39 -0
- package/knowledge/decisions/internals/core-hot-reload-owns-its-module-registry.md +42 -0
- package/knowledge/decisions/internals/core-middleware-order-is-scope-then-priority.md +39 -0
- package/knowledge/decisions/internals/core-schema-defaults-apply-on-every-transport.md +43 -0
- package/knowledge/decisions/internals/core-scopes-are-an-and-gate-separate-from-permissions.md +38 -0
- package/knowledge/decisions/internals/core-state-is-a-global-map-written-only-at-registration-time.md +44 -0
- package/knowledge/decisions/internals/email-meta-is-read-uncached-because-codegen-rewrites-it-mid-session.md +27 -0
- package/knowledge/decisions/internals/gateway-adapters-resolve-lazily-and-are-promise-cached.md +32 -0
- package/knowledge/decisions/internals/gateway-webhook-challenges-echo-bytes-not-json.md +28 -0
- package/knowledge/decisions/internals/gateway-wiring-is-a-meta-wiring-over-http-and-channels.md +31 -0
- package/knowledge/decisions/internals/generated-src-paths-in-pikku-meta-are-absolute.md +26 -0
- package/knowledge/decisions/internals/http-request-bodies-are-read-once-and-shared.md +32 -0
- package/knowledge/decisions/internals/http-route-groups-cascade-config-in-a-fixed-order.md +28 -0
- package/knowledge/decisions/internals/http-router-matches-normalized-paths-but-returns-registered-ones.md +30 -0
- package/knowledge/decisions/internals/http-runner-logs-through-a-trace-scoped-logger-functions-do-not.md +26 -0
- package/knowledge/decisions/internals/http-set-cookie-headers-are-appended-never-joined.md +28 -0
- package/knowledge/decisions/internals/http-sse-streams-flush-headers-only-after-middleware.md +32 -0
- package/knowledge/decisions/internals/http-wiring-without-metadata-is-skipped-not-fatal.md +26 -0
- package/knowledge/decisions/internals/in-a-scenario-a-4xx-is-data-not-an-exception.md +25 -0
- package/knowledge/decisions/internals/in-memory-workflow-history-aliases-the-live-step-object.md +26 -0
- package/knowledge/decisions/internals/index.md +113 -0
- package/knowledge/decisions/internals/istanbul-statement-counts-attach-to-the-start-line-only.md +25 -0
- package/knowledge/decisions/internals/local-trigger-and-gateway-services-assume-a-single-process.md +26 -0
- package/knowledge/decisions/internals/node-only-builtins-are-imported-dynamically.md +24 -0
- package/knowledge/decisions/internals/queue-group-concurrency-keeps-one-shared-queue-fair.md +28 -0
- package/knowledge/decisions/internals/queue-jobs-always-carry-an-explicit-attempts-count.md +27 -0
- package/knowledge/decisions/internals/remote-addons-dispatch-over-http-instead-of-local-meta.md +31 -0
- package/knowledge/decisions/internals/rpc-names-resolve-through-package-scope-before-root.md +32 -0
- package/knowledge/decisions/internals/scenario-agent-calls-sign-in-on-401-only.md +27 -0
- package/knowledge/decisions/internals/scenario-meta-lives-apart-from-app-meta-but-merges-when-read-off-disk.md +26 -0
- package/knowledge/decisions/internals/scenario-steps-return-drained-response-records.md +27 -0
- package/knowledge/decisions/internals/scope-roots-may-be-co-declared-by-an-addon-and-its-host-app.md +30 -0
- package/knowledge/decisions/internals/serverless-channel-disconnect-must-tolerate-a-missing-channel.md +28 -0
- package/knowledge/decisions/internals/the-dev-queue-copies-prod-timing-and-serialization-semantics.md +30 -0
- package/knowledge/decisions/internals/the-embedding-model-is-pinned-per-service-and-doc-query-embedding-is-split.md +29 -0
- package/knowledge/decisions/internals/the-in-memory-workflow-service-is-inline-only-and-single-process.md +27 -0
- package/knowledge/decisions/internals/the-kek-salt-is-scoped-to-the-key-version.md +40 -0
- package/knowledge/decisions/internals/the-schema-service-is-never-stubbed.md +26 -0
- package/knowledge/decisions/internals/trigger-declaration-is-split-from-trigger-source.md +33 -0
- package/knowledge/decisions/internals/typed-secret-service-caches-for-the-process-lifetime.md +26 -0
- package/knowledge/decisions/internals/webhook-delivery-history-records-every-attempt-best-effort.md +26 -0
- package/knowledge/decisions/internals/webhook-service-collaborators-are-constructor-args-not-locator-lookups.md +25 -0
- package/knowledge/decisions/internals/whether-a-run-is-inline-is-read-from-the-run-record.md +58 -0
- package/knowledge/decisions/internals/workflow-approval-expiry-is-decided-from-a-recorded-deadline.md +34 -0
- package/knowledge/decisions/internals/workflow-core-never-imports-a-browser-driver.md +42 -0
- package/knowledge/decisions/internals/workflow-dsl-meta-separates-runtime-expressions-from-literals.md +38 -0
- package/knowledge/decisions/internals/workflow-features-resolve-scenarios-by-object-identity.md +29 -0
- package/knowledge/decisions/internals/workflow-graph-inline-and-queued-runs-share-one-planner.md +42 -0
- package/knowledge/decisions/internals/workflow-graph-node-notes-are-excluded-from-the-graph-hash.md +25 -0
- package/knowledge/decisions/internals/workflow-inline-runs-report-their-run-id-before-they-can-fail.md +29 -0
- package/knowledge/decisions/internals/workflow-invocation-id-is-the-dedupe-key-not-step-id.md +43 -0
- package/knowledge/decisions/internals/workflow-queued-step-dispatch-requires-an-explicit-opt-in.md +29 -0
- package/knowledge/decisions/internals/workflow-queues-are-per-workflow-by-default.md +42 -0
- package/knowledge/decisions/internals/workflow-repeated-step-names-get-an-ordinal-suffix.md +33 -0
- package/knowledge/decisions/internals/workflow-replay-reads-its-steps-once-and-caches-only-the-immutable-half.md +32 -0
- package/knowledge/decisions/internals/workflow-retries-are-owned-by-the-workflow-not-the-queue.md +31 -0
- package/knowledge/decisions/internals/workflow-run-capabilities-are-extensions-not-subclasses.md +39 -0
- package/knowledge/decisions/internals/workflow-run-mirror-is-never-a-source-of-truth.md +29 -0
- package/knowledge/decisions/internals/workflow-run-polling-backs-off-to-the-callers-ceiling.md +33 -0
- package/knowledge/decisions/internals/workflow-run-timeline-is-a-pure-fold-over-durable-history.md +37 -0
- package/knowledge/decisions/internals/workflow-scenario-assertions-never-retry-and-record-one-step.md +50 -0
- package/knowledge/decisions/internals/workflow-scenario-hooks-are-a-scenario-only-affordance.md +43 -0
- package/knowledge/decisions/internals/workflow-scenario-prose-is-rendered-from-typed-calls-not-parsed-from-english.md +32 -0
- package/knowledge/decisions/internals/workflow-scenario-quarantine-reason-lives-in-code.md +18 -0
- package/knowledge/decisions/internals/workflow-scenario-step-targets-are-string-literals-for-the-inspector.md +34 -0
- package/knowledge/decisions/internals/workflow-step-compensation-runs-as-its-own-durable-step.md +26 -0
- package/knowledge/decisions/internals/workflow-step-dispatch-failure-is-transient-not-a-run-failure.md +33 -0
- package/knowledge/decisions/internals/workflow-step-lock-is-held-only-to-claim-the-step.md +27 -0
- package/knowledge/decisions/internals/workflow-step-rpc-name-is-provenance-only.md +34 -0
- package/knowledge/decisions/internals/workflow-suspend-and-approval-reasons-are-durable-step-identities.md +38 -0
- package/knowledge/decisions/internals/workflow-suspended-runs-keep-their-in-process-context.md +30 -0
- package/knowledge/decisions/security/a-dropped-audit-write-is-always-logged.md +26 -0
- package/knowledge/decisions/security/actor-flow-missing-approval-decisions-default-to-denied.md +22 -0
- package/knowledge/decisions/security/actor-sign-in-is-proven-by-set-cookie-not-a-non-empty-jar.md +27 -0
- package/knowledge/decisions/security/actor-sign-in-only-works-for-actor-flagged-users.md +27 -0
- package/knowledge/decisions/security/addon-auth-and-tags-only-tighten.md +43 -0
- package/knowledge/decisions/security/addon-config-gates-apply-only-at-the-namespaced-rpc-boundary.md +52 -0
- package/knowledge/decisions/security/addon-scopes-are-resolved-where-the-function-runs.md +46 -0
- package/knowledge/decisions/security/ai-agent-approval-forwarding-requires-a-symbol-brand.md +27 -0
- package/knowledge/decisions/security/ai-agent-credential-requests-are-symbol-branded.md +37 -0
- package/knowledge/decisions/security/ai-agent-gate-requires-a-session-only-when-auth-is-true.md +31 -0
- package/knowledge/decisions/security/ai-agent-ownership-failures-never-echo-the-resource.md +23 -0
- package/knowledge/decisions/security/ai-agent-resume-re-runs-the-authorization-gate.md +22 -0
- package/knowledge/decisions/security/ai-agent-sessionless-deployments-have-no-thread-ownership.md +39 -0
- package/knowledge/decisions/security/ai-agent-thread-ownership-composes-the-session-principal.md +30 -0
- package/knowledge/decisions/security/ai-agent-tool-filtering-reads-the-live-function-config.md +24 -0
- package/knowledge/decisions/security/an-empty-owners-constraint-matches-nothing.md +30 -0
- package/knowledge/decisions/security/an-exposed-ungated-function-is-a-codegen-warning.md +49 -0
- package/knowledge/decisions/security/console-addon-privileged-functions-gate-themselves.md +76 -0
- package/knowledge/decisions/security/core-safe-fetch-blocks-ssrf-by-host-literal-not-dns.md +39 -0
- package/knowledge/decisions/security/core-secrets-use-a-per-secret-dek-wrapped-by-a-kek.md +37 -0
- package/knowledge/decisions/security/gateway-handlers-run-through-the-function-runner-gate.md +31 -0
- package/knowledge/decisions/security/gateway-middleware-sessions-must-be-bridged-onto-the-wire.md +30 -0
- package/knowledge/decisions/security/global-permissions-and-function-permissions-are-independent-gates.md +40 -0
- package/knowledge/decisions/security/http-error-detail-is-withheld-from-clients-in-production.md +33 -0
- package/knowledge/decisions/security/http-request-bodies-are-bounded-before-they-are-buffered.md +46 -0
- package/knowledge/decisions/security/index.md +55 -0
- package/knowledge/decisions/security/mcp-internal-error-details-are-double-gated-on-production.md +27 -0
- package/knowledge/decisions/security/passphrases-are-stretched-key-material-is-expanded.md +40 -0
- package/knowledge/decisions/security/permission-auth-filtering-requires-live-permission-functions.md +31 -0
- package/knowledge/decisions/security/pikku-carries-actor-scopes-as-data-and-the-app-grants-them.md +26 -0
- package/knowledge/decisions/security/queue-job-identities-are-signed-at-enqueue.md +69 -0
- package/knowledge/decisions/security/queue-jobs-carry-the-producers-pikku-user-id.md +39 -0
- package/knowledge/decisions/security/remote-addon-tokens-are-client-credentials-not-mesh-trust.md +34 -0
- package/knowledge/decisions/security/scaffold-features-are-authenticated-unless-opted-out.md +49 -0
- package/knowledge/decisions/security/scenario-step-functions-are-never-externally-invocable.md +30 -0
- package/knowledge/decisions/security/scope-resolution-happens-at-the-session-boundary-and-sync-never-deletes.md +28 -0
- package/knowledge/decisions/security/self-authentication-is-declared-not-detected.md +34 -0
- package/knowledge/decisions/security/signed-content-urls-bind-the-request-path.md +37 -0
- package/knowledge/decisions/security/webhook-bodies-are-signed-before-they-are-enqueued.md +25 -0
- package/knowledge/decisions/security/workflow-actor-steps-always-use-the-real-transport.md +34 -0
- package/knowledge/decisions/security/workflow-approval-payloads-are-validated-on-replay-inside-the-workflow.md +40 -0
- package/knowledge/decisions/security/workflow-queued-steps-rehydrate-their-session-from-the-run-wire.md +32 -0
- package/knowledge/decisions/security/workflow-scenario-sessions-are-isolated-per-actor-and-per-scenario.md +32 -0
- package/knowledge/decisions/security/workflow-scenario-steps-are-never-network-invocable.md +31 -0
- package/knowledge/index.md +24 -0
- package/knowledge/questions/index.md +15 -0
- package/package.json +4 -2
- package/run-tests.sh +0 -0
- package/src/crypto-utils.test.ts +460 -19
- package/src/crypto-utils.ts +283 -55
- package/src/data-classification.ts +1 -7
- package/src/dev/hot-reload.test.ts +0 -4
- package/src/dev/hot-reload.ts +11 -30
- package/src/dev/module-runner.ts +7 -32
- package/src/dev/reload-meta.ts +9 -29
- package/src/errors/error-handler.ts +20 -35
- package/src/errors/error.test.ts +30 -1
- package/src/errors/errors.ts +73 -157
- package/src/function/abort-scope.test.ts +97 -0
- package/src/function/abort-scope.ts +80 -0
- package/src/function/function-runner.test.ts +75 -7
- package/src/function/function-runner.ts +73 -30
- package/src/function/functions.types.ts +56 -136
- package/src/function/list.types.test.ts +3 -25
- package/src/function/list.types.ts +12 -62
- package/src/gopass-secrets-removed.test.ts +51 -0
- package/src/handle-error.test.ts +108 -1
- package/src/handle-error.ts +8 -18
- package/src/index.ts +60 -1
- package/src/middleware/auth-apikey.test.ts +0 -1
- package/src/middleware/auth-apikey.ts +0 -17
- package/src/middleware/auth-bearer.test.ts +0 -3
- package/src/middleware/auth-bearer.ts +3 -40
- package/src/middleware/auth-cookie.test.ts +0 -6
- package/src/middleware/auth-cookie.ts +2 -26
- package/src/middleware/cors.test.ts +34 -0
- package/src/middleware/cors.ts +12 -33
- package/src/middleware/remote-auth.test.ts +24 -9
- package/src/middleware/remote-auth.ts +10 -2
- package/src/middleware/telemetry.ts +2 -31
- package/src/middleware-runner.test.ts +0 -2
- package/src/middleware-runner.ts +5 -74
- package/src/permissions.test.ts +30 -0
- package/src/permissions.ts +24 -74
- package/src/pikku-request.ts +0 -6
- package/src/pikku-state.ts +2 -30
- package/src/production-barrels-stay-lean.test.ts +110 -0
- package/src/remote.test.ts +172 -0
- package/src/remote.ts +17 -7
- package/src/schema.test.ts +103 -0
- package/src/schema.ts +35 -18
- package/src/scopes.ts +7 -48
- package/src/services/ai-agent-runner-service.ts +20 -0
- package/src/services/ai-embedding-service.ts +3 -25
- package/src/services/audit-service.ts +1 -2
- package/src/services/content-service.ts +1 -46
- package/src/services/credential-service.ts +3 -40
- package/src/services/credential-wire-service.test.ts +0 -2
- package/src/services/deployment-service.ts +3 -9
- package/src/services/gateway-service.ts +0 -15
- package/src/services/{http-scenario-actors-converse.test.ts → http-personas-converse.test.ts} +38 -9
- package/src/services/{http-scenario-actors.test.ts → http-personas.test.ts} +39 -22
- package/src/services/{http-scenario-actors.ts → http-personas.ts} +85 -45
- package/src/services/in-memory-queue-service.ts +1 -15
- package/src/services/in-memory-trigger-service.ts +1 -18
- package/src/services/in-memory-workflow-service.test.ts +0 -13
- package/src/services/in-memory-workflow-service.ts +4 -38
- package/src/services/index.ts +20 -15
- package/src/services/istanbul-coverage-service.ts +2 -8
- package/src/services/jwt-service.ts +1 -16
- package/src/services/local-content.test.ts +159 -27
- package/src/services/local-content.ts +55 -23
- package/src/services/local-gateway-service.ts +2 -17
- package/src/services/local-secrets.ts +0 -4
- package/src/services/logger-console.test.ts +0 -1
- package/src/services/logger-console.ts +3 -7
- package/src/services/logger.ts +2 -37
- package/src/services/meta-service.test.ts +1 -5
- package/src/services/meta-service.ts +41 -61
- package/src/services/{scenario-actors-service.ts → personas-service.ts} +48 -43
- package/src/services/pikku-user-id.ts +0 -4
- package/src/services/queue-webhook-service.ts +9 -41
- package/src/services/scheduler-service.ts +1 -50
- package/src/services/schema-service.ts +1 -24
- package/src/services/scope-service.ts +50 -34
- package/src/services/scoped-secret-service.ts +0 -4
- package/src/services/secret-host-binding.test.ts +138 -0
- package/src/services/secret-host-binding.ts +51 -0
- package/src/services/secret-service.ts +5 -33
- package/src/services/secretless.test.ts +54 -0
- package/src/services/secretless.ts +29 -0
- package/src/services/stub-tracker.ts +8 -18
- package/src/services/system-role-guard.test.ts +93 -0
- package/src/services/system-role-guard.ts +71 -0
- package/src/services/trigger-service.ts +0 -12
- package/src/services/typed-secret-service.ts +1 -7
- package/src/services/v8-coverage-service.ts +3 -6
- package/src/services/variables-service.ts +1 -8
- package/src/services/webhook-service.ts +19 -63
- package/src/services/workflow-service.ts +3 -20
- package/src/testing/service-tests.ts +0 -26
- package/src/time-utils.ts +1 -19
- package/src/types/core.types.ts +116 -229
- package/src/types/state.types.ts +7 -9
- package/src/utils/hmac.ts +4 -10
- package/src/utils/safe-fetch.ts +13 -54
- package/src/utils.test.ts +11 -2
- package/src/utils.ts +6 -15
- package/src/wirings/actor-flow/actor-flow.types.ts +1 -34
- package/src/wirings/actor-flow/index.ts +0 -9
- package/src/wirings/actor-flow/run-conversation.test.ts +11 -6
- package/src/wirings/actor-flow/run-conversation.ts +19 -12
- package/src/wirings/ai-agent/ai-agent-agui.test.ts +75 -10
- package/src/wirings/ai-agent/ai-agent-agui.ts +36 -17
- package/src/wirings/ai-agent/ai-agent-helpers.ts +20 -0
- package/src/wirings/ai-agent/ai-agent-interrupt.test.ts +842 -0
- package/src/wirings/ai-agent/ai-agent-interrupt.ts +399 -0
- package/src/wirings/ai-agent/ai-agent-memory.ts +0 -2
- package/src/wirings/ai-agent/ai-agent-model-config.ts +1 -9
- package/src/wirings/ai-agent/ai-agent-prepare.test.ts +202 -31
- package/src/wirings/ai-agent/ai-agent-prepare.ts +82 -138
- package/src/wirings/ai-agent/ai-agent-registry.test.ts +191 -6
- package/src/wirings/ai-agent/ai-agent-registry.ts +18 -1
- package/src/wirings/ai-agent/ai-agent-resume-authorization.test.ts +0 -2
- package/src/wirings/ai-agent/ai-agent-runner.test.ts +11 -9
- package/src/wirings/ai-agent/ai-agent-runner.ts +67 -33
- package/src/wirings/ai-agent/ai-agent-stream.test.ts +205 -103
- package/src/wirings/ai-agent/ai-agent-stream.ts +192 -75
- package/src/wirings/ai-agent/ai-agent-thread-ownership.test.ts +301 -0
- package/src/wirings/ai-agent/ai-agent.types.ts +85 -4
- package/src/wirings/ai-agent/index.ts +35 -3
- package/src/wirings/ai-agent/voice-input.test.ts +72 -7
- package/src/wirings/ai-agent/voice-input.ts +48 -3
- package/src/wirings/ai-agent/voice-output.test.ts +422 -0
- package/src/wirings/ai-agent/voice-output.ts +216 -56
- package/src/wirings/channel/channel-common.ts +15 -20
- package/src/wirings/channel/channel-handler.test.ts +50 -0
- package/src/wirings/channel/channel-handler.ts +32 -11
- package/src/wirings/channel/channel-host-rpc.test.ts +150 -0
- package/src/wirings/channel/channel-host-rpc.ts +69 -0
- package/src/wirings/channel/channel-middleware-runner.test.ts +0 -1
- package/src/wirings/channel/channel-middleware-runner.ts +0 -12
- package/src/wirings/channel/channel-rpc-registry.ts +116 -0
- package/src/wirings/channel/channel-rpc-responder.ts +117 -0
- package/src/wirings/channel/channel-rpc-service.ts +146 -0
- package/src/wirings/channel/channel-rpc-validators.ts +65 -0
- package/src/wirings/channel/channel-rpc.test.ts +820 -0
- package/src/wirings/channel/channel-rpc.ts +5 -0
- package/src/wirings/channel/channel-rpc.types.ts +150 -0
- package/src/wirings/channel/channel-runner.ts +0 -14
- package/src/wirings/channel/channel-store.ts +0 -10
- package/src/wirings/channel/channel.types.ts +19 -8
- package/src/wirings/channel/define-channel-routes.ts +0 -20
- package/src/wirings/channel/eventhub-service.ts +0 -18
- package/src/wirings/channel/index.ts +35 -0
- package/src/wirings/channel/local/local-channel-handler.ts +3 -1
- package/src/wirings/channel/local/local-channel-runner.test.ts +0 -10
- package/src/wirings/channel/local/local-channel-runner.ts +3 -1
- package/src/wirings/channel/local/local-eventhub-service.test.ts +0 -13
- package/src/wirings/channel/local/local-eventhub-service.ts +2 -37
- package/src/wirings/channel/log-channels.ts +0 -4
- package/src/wirings/channel/pikku-abstract-channel-handler.test.ts +83 -2
- package/src/wirings/channel/pikku-abstract-channel-handler.ts +7 -0
- package/src/wirings/channel/serverless/serverless-channel-runner.ts +2 -5
- package/src/wirings/cli/channel/cli-approval.test.ts +177 -0
- package/src/wirings/cli/channel/cli-approval.ts +135 -0
- package/src/wirings/cli/channel/cli-channel-runner.ts +4 -26
- package/src/wirings/cli/channel/cli-raw-channel-runner.test.ts +169 -0
- package/src/wirings/cli/channel/cli-raw-channel-runner.ts +59 -16
- package/src/wirings/cli/channel/cli-raw-client-runner.test.ts +480 -0
- package/src/wirings/cli/channel/cli-raw-client-runner.ts +155 -0
- package/src/wirings/cli/channel/index.ts +9 -0
- package/src/wirings/cli/cli-runner.test.ts +0 -1
- package/src/wirings/cli/cli-runner.ts +46 -88
- package/src/wirings/cli/cli.types.ts +14 -3
- package/src/wirings/cli/command-parser.test.ts +0 -4
- package/src/wirings/cli/command-parser.ts +11 -91
- package/src/wirings/cli/define-cli-commands.ts +1 -17
- package/src/wirings/credential/credential.types.ts +0 -12
- package/src/wirings/credential/{wire-credential.ts → define-credential.ts} +7 -7
- package/src/wirings/credential/index.ts +1 -1
- package/src/wirings/credential/validate-credential-definitions.ts +2 -4
- package/src/wirings/gateway/gateway-runner.test.ts +1 -21
- package/src/wirings/gateway/gateway-runner.ts +8 -110
- package/src/wirings/gateway/gateway.types.ts +7 -80
- package/src/wirings/http/http-routes.test.ts +0 -3
- package/src/wirings/http/http-routes.ts +0 -86
- package/src/wirings/http/http-runner.test.ts +0 -1
- package/src/wirings/http/http-runner.ts +12 -168
- package/src/wirings/http/http.types.ts +17 -62
- package/src/wirings/http/index.ts +5 -1
- package/src/wirings/http/log-http-routes.ts +0 -4
- package/src/wirings/http/pikku-fetch-http-request.test.ts +88 -7
- package/src/wirings/http/pikku-fetch-http-request.ts +74 -50
- package/src/wirings/http/pikku-fetch-http-response.test.ts +1 -1
- package/src/wirings/http/pikku-fetch-http-response.ts +0 -3
- package/src/wirings/http/routers/path-to-regex.test.ts +4 -17
- package/src/wirings/http/routers/path-to-regex.ts +2 -13
- package/src/wirings/http/web-request.test.ts +33 -2
- package/src/wirings/http/web-request.ts +30 -17
- package/src/wirings/mcp/mcp-endpoint-registry.test.ts +0 -1
- package/src/wirings/mcp/mcp-runner.test.ts +40 -0
- package/src/wirings/mcp/mcp-runner.ts +9 -15
- package/src/wirings/mcp/mcp.types.ts +7 -42
- package/src/wirings/oauth2/oauth2.types.ts +0 -30
- package/src/wirings/persona/define-personas.ts +29 -0
- package/src/wirings/persona/index.ts +62 -0
- package/src/wirings/persona/persona-email.ts +87 -0
- package/src/wirings/persona/persona-environments.test.ts +183 -0
- package/src/wirings/persona/persona-environments.ts +138 -0
- package/src/wirings/persona/persona-mailbox.ts +156 -0
- package/src/wirings/persona/persona.test.ts +220 -0
- package/src/wirings/persona/persona.types.ts +131 -0
- package/src/wirings/persona/validate-personas.ts +133 -0
- package/src/wirings/queue/index.ts +13 -3
- package/src/wirings/queue/queue-identity.test.ts +453 -0
- package/src/wirings/queue/queue-identity.ts +173 -0
- package/src/wirings/queue/queue-runner.ts +12 -31
- package/src/wirings/queue/queue.types.ts +19 -89
- package/src/wirings/queue/register-queue-helper.ts +0 -14
- package/src/wirings/queue/signed-queue-service.ts +59 -0
- package/src/wirings/queue/validate-worker-config.ts +2 -28
- package/src/wirings/role/define-system-role.ts +33 -0
- package/src/wirings/role/index.ts +13 -0
- package/src/wirings/role/role.test.ts +104 -0
- package/src/wirings/role/role.types.ts +47 -0
- package/src/wirings/role/validate-role-definitions.ts +93 -0
- package/src/wirings/rpc/addon-auth-tags.test.ts +223 -0
- package/src/wirings/rpc/addon-runner.ts +0 -56
- package/src/wirings/rpc/addon-scopes.test.ts +225 -0
- package/src/wirings/rpc/remote-addon-auth.ts +1 -13
- package/src/wirings/rpc/rpc-runner.test.ts +186 -2
- package/src/wirings/rpc/rpc-runner.ts +145 -127
- package/src/wirings/rpc/rpc-types.ts +11 -6
- package/src/wirings/rpc/wire-addon.test.ts +43 -1
- package/src/wirings/rpc/wire-addon.ts +99 -0
- package/src/wirings/rpc/wire-remote-addon.ts +9 -29
- package/src/wirings/scheduler/log-schedulers.ts +0 -4
- package/src/wirings/scheduler/scheduler-runner.test.ts +1 -8
- package/src/wirings/scheduler/scheduler-runner.ts +0 -2
- package/src/wirings/scheduler/scheduler.types.ts +1 -14
- package/src/wirings/scope/{wire-scope.ts → define-scope.ts} +5 -6
- package/src/wirings/scope/index.ts +1 -1
- package/src/wirings/scope/scope.test.ts +1 -2
- package/src/wirings/scope/scope.types.ts +7 -9
- package/src/wirings/scope/validate-scope-definitions.ts +3 -21
- package/src/wirings/secret/index.ts +1 -1
- package/src/wirings/secret/secret.types.ts +19 -15
- package/src/wirings/secret/validate-secret-definitions.ts +2 -4
- package/src/wirings/trigger/trigger-runner.ts +1 -27
- package/src/wirings/trigger/trigger.types.ts +1 -82
- package/src/wirings/variable/index.ts +1 -1
- package/src/wirings/variable/validate-variable-definitions.ts +2 -4
- package/src/wirings/variable/variable.types.ts +1 -13
- package/src/wirings/virtual-user/index.ts +76 -0
- package/src/wirings/virtual-user/run-virtual-user.test.ts +765 -0
- package/src/wirings/virtual-user/run-virtual-user.ts +671 -0
- package/src/wirings/virtual-user/virtual-user-agents.test.ts +65 -0
- package/src/wirings/virtual-user/virtual-user-agents.ts +57 -0
- package/src/wirings/virtual-user/virtual-user-catalogue.test.ts +215 -0
- package/src/wirings/virtual-user/virtual-user-catalogue.ts +184 -0
- package/src/wirings/virtual-user/virtual-user-derive.test.ts +398 -0
- package/src/wirings/virtual-user/virtual-user-derive.ts +173 -0
- package/src/wirings/virtual-user/virtual-user-dispositions.test.ts +63 -0
- package/src/wirings/virtual-user/virtual-user-dispositions.ts +213 -0
- package/src/wirings/virtual-user/virtual-user-intents.test.ts +208 -0
- package/src/wirings/virtual-user/virtual-user-intents.ts +185 -0
- package/src/wirings/virtual-user/virtual-user-rng.test.ts +72 -0
- package/src/wirings/virtual-user/virtual-user-rng.ts +50 -0
- package/src/wirings/virtual-user/virtual-user-target.ts +47 -0
- package/src/wirings/virtual-user/virtual-user.types.ts +219 -0
- package/src/wirings/workflow/dsl/workflow-dsl.types.ts +5 -4
- package/src/wirings/workflow/dsl/workflow-runner.ts +0 -4
- package/src/wirings/workflow/feature.ts +0 -19
- package/src/wirings/workflow/graph/graph-node.ts +0 -136
- package/src/wirings/workflow/graph/graph-runner.test.ts +20 -19
- package/src/wirings/workflow/graph/graph-runner.ts +6 -41
- package/src/wirings/workflow/graph/wire-workflow-graph.ts +0 -4
- package/src/wirings/workflow/graph/workflow-graph.types.ts +0 -58
- package/src/wirings/workflow/index.ts +10 -44
- package/src/wirings/workflow/pikku-scenario-service.ts +235 -52
- package/src/wirings/workflow/pikku-workflow-service.test.ts +0 -39
- package/src/wirings/workflow/pikku-workflow-service.ts +77 -674
- package/src/wirings/workflow/run-timeline.test.ts +7 -19
- package/src/wirings/workflow/run-timeline.ts +0 -56
- package/src/wirings/workflow/scenario-cookie-jar.test.ts +0 -1
- package/src/wirings/workflow/scenario-cookie-jar.ts +0 -25
- package/src/wirings/workflow/scenario-expectations.test.ts +2 -7
- package/src/wirings/workflow/scenario-hooks.test.ts +2 -7
- package/src/wirings/workflow/scenario-poll.test.ts +0 -2
- package/src/wirings/workflow/scenario-poll.ts +0 -15
- package/src/wirings/workflow/scenario-prose.ts +0 -28
- package/src/wirings/workflow/scenario-service.test.ts +2 -9
- package/src/wirings/workflow/scenario-step-guards.ts +1 -14
- package/src/wirings/workflow/scenario-step.test.ts +159 -14
- package/src/wirings/workflow/scenario-step.types.ts +82 -6
- package/src/wirings/workflow/scenario-surface.test.ts +145 -0
- package/src/wirings/workflow/scenario-surface.ts +71 -0
- package/src/wirings/workflow/workflow-dispatch-durability.test.ts +14 -15
- package/src/wirings/workflow/workflow-dispatch-payload.test.ts +0 -4
- package/src/wirings/workflow/workflow-inline-authority.test.ts +169 -0
- package/src/wirings/workflow/workflow-invocation-id.test.ts +0 -2
- package/src/wirings/workflow/workflow-invocation-id.ts +2 -22
- package/src/wirings/workflow/workflow-mirror.test.ts +0 -7
- package/src/wirings/workflow/workflow-on-error.test.ts +0 -9
- package/src/wirings/workflow/workflow-queue-workers.ts +0 -21
- package/src/wirings/workflow/workflow-replay-snapshot.test.ts +8 -7
- package/src/wirings/workflow/workflow-retry-policy.test.ts +0 -5
- package/src/wirings/workflow/workflow-run-context.test.ts +5 -10
- package/src/wirings/workflow/workflow-run-polling.test.ts +0 -5
- package/src/wirings/workflow/workflow-step-ordinal.test.ts +19 -4
- package/src/wirings/workflow/workflow-step-session.test.ts +0 -7
- package/src/wirings/workflow/workflow.types.ts +0 -203
- package/tsconfig.tsbuildinfo +1 -1
- package/src/middleware/timeout.ts +0 -22
- package/src/pikku-response.ts +0 -5
- package/src/services/gopass-secrets.ts +0 -78
- package/src/wirings/mcp/mcp-endpoint-registry.test.d.ts +0 -1
- package/src/wirings/workflow/dsl/index.ts +0 -36
- package/src/wirings/workflow/graph/index.ts +0 -15
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: decision
|
|
3
|
+
title: The embedding model is pinned per service and doc/query embedding is split
|
|
4
|
+
description: AIEmbeddingService fixes its model at construction so index and query share a vector space, and separates embedDocuments from embedQuery for asymmetric models
|
|
5
|
+
tags: services
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# The embedding model is pinned per service and doc/query embedding is split
|
|
9
|
+
|
|
10
|
+
`AIEmbeddingService` (`packages/core/src/services/ai-embedding-service.ts`) is a
|
|
11
|
+
deliberately narrow interface with two properties that look like restrictions.
|
|
12
|
+
The model is `readonly` and fixed at construction rather than passed per call,
|
|
13
|
+
and documents and queries go through separate methods.
|
|
14
|
+
|
|
15
|
+
Both are about comparability. Vector stores (Qdrant, Pinecone, pgvector) embed at
|
|
16
|
+
index time and again at query time; if those two moments can name different
|
|
17
|
+
models they end up in different vector spaces, and similarity search does not
|
|
18
|
+
error — it silently returns nonsense. Pinning the model to the service makes the
|
|
19
|
+
drift unrepresentable. The split methods exist because several embedding models
|
|
20
|
+
are asymmetric and must know which side they are embedding to produce comparable
|
|
21
|
+
vectors: Cohere's `input_type`, E5's `query:` / `passage:` prefixes, BGE's query
|
|
22
|
+
instruction. Symmetric providers such as OpenAI simply point both methods at the
|
|
23
|
+
same call.
|
|
24
|
+
|
|
25
|
+
**What this rules out:** adding a per-call `model` parameter, collapsing
|
|
26
|
+
`embedDocuments` and `embedQuery` into one `embed`, and pointing a vector store
|
|
27
|
+
at `AIAgentRunnerService.embed` / `embedMany` instead — those take a per-call
|
|
28
|
+
model on purpose and drag in the whole agent-runner tool loop, and they give back
|
|
29
|
+
neither guarantee.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: decision
|
|
3
|
+
title: The in-memory workflow service is inline-only and single-process
|
|
4
|
+
description: InMemoryWorkflowService wires no queues and implements withRunLock/withStepLock as pass-throughs, because inline execution has no second holder to exclude
|
|
5
|
+
tags: services
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# The in-memory workflow service is inline-only and single-process
|
|
9
|
+
|
|
10
|
+
`InMemoryWorkflowService` (`packages/core/src/services/in-memory-workflow-service.ts`)
|
|
11
|
+
calls `super({ ...options, wireQueues: false })` and implements `withRunLock` and
|
|
12
|
+
`withStepLock` as bare `return fn()`. Both look like unfinished work and neither
|
|
13
|
+
is.
|
|
14
|
+
|
|
15
|
+
Every step runs inline in the process that started the run: there are no queue
|
|
16
|
+
workers, so no other worker can be mid-step on the same run, and all state lives
|
|
17
|
+
in this instance's `Map`s, so no other process can see it to contend for it. A
|
|
18
|
+
lock would be excluding a competitor that cannot exist. It is offered for CLI
|
|
19
|
+
tools that want step orchestration, for tests, and for single-process apps that
|
|
20
|
+
do not need persistence — and the run state is unbounded and lost on restart,
|
|
21
|
+
which is the price of that.
|
|
22
|
+
|
|
23
|
+
**What this rules out:** treating the no-op locks as a bug and adding real
|
|
24
|
+
locking here, and running this service anywhere a second process or a queue
|
|
25
|
+
worker could touch the same run. A deployment that needs either wants a
|
|
26
|
+
persistent `WorkflowService` implementation instead; there is nothing to fix in
|
|
27
|
+
this one.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: decision
|
|
3
|
+
title: The KEK salt is scoped to the key version, not the secret
|
|
4
|
+
description: One stored salt per key version means N secrets cost one derivation, which is the point of envelope encryption
|
|
5
|
+
tags: crypto, storage
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# The KEK salt is scoped to the key version, not the secret
|
|
9
|
+
|
|
10
|
+
Each secret and credential service stores one random salt per `keyVersion`,
|
|
11
|
+
generated on first use and read once — a `secretKekSalts` / `credentialKekSalts`
|
|
12
|
+
table for kysely, a hash field for redis, a document for mongodb. `deriveKEK`
|
|
13
|
+
runs against that salt to produce a `CryptoKey`, which is what
|
|
14
|
+
`wrapDEK`/`unwrapDEK`/`envelope*` accept. The wrapped-DEK blob therefore carries
|
|
15
|
+
no salt of its own: `[iv:12][ct+tag]`.
|
|
16
|
+
|
|
17
|
+
Envelope encryption exists so the expensive derivation happens once and many
|
|
18
|
+
cheap DEK unwraps follow. A per-secret salt destroys that: every
|
|
19
|
+
`envelopeDecrypt` re-derives, so `getSecrets` over 50 rows cost 50 × PBKDF2-600k
|
|
20
|
+
(~2.3s) and rotation cost twice that. Scoping the salt to the key version
|
|
21
|
+
restored the intended shape — one derivation for a bulk read, two for a
|
|
22
|
+
rotation.
|
|
23
|
+
|
|
24
|
+
Keying by version rather than storing a single salt is what lets `getKEK` keep
|
|
25
|
+
serving `previousKey` for older rows during rotation.
|
|
26
|
+
|
|
27
|
+
A salt's job is to defeat precomputation across deployments and passphrases; one
|
|
28
|
+
random salt per deployment per key version achieves that fully. Per-ciphertext
|
|
29
|
+
salt only buys something when each ciphertext might use a different passphrase,
|
|
30
|
+
which is never the case here. Storing the salt rather than taking it as
|
|
31
|
+
configuration keeps it off the operator's plate — it needs to be
|
|
32
|
+
deployment-random, not secret.
|
|
33
|
+
|
|
34
|
+
**What this rules out:** deriving the KEK inside a per-row loop; a salt shared
|
|
35
|
+
across key versions, which would break rotation; a deterministic salt derived
|
|
36
|
+
from the version number, which is the same salt in every deployment and so
|
|
37
|
+
restores the rainbow table; and caching derived keys anywhere but a read-through
|
|
38
|
+
instance map whose source of truth is the store.
|
|
39
|
+
|
|
40
|
+
See [[passphrases-are-stretched-key-material-is-expanded]].
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: decision
|
|
3
|
+
title: The schema service is never stubbed, or tests validate nothing
|
|
4
|
+
description: createStubProxy returns undefined for the schema property so the real schema service is built — a stubbed one turns validation into a silent no-op
|
|
5
|
+
tags: services
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# The schema service is never stubbed, or tests validate nothing
|
|
9
|
+
|
|
10
|
+
`createStubProxy` (`packages/core/src/services/stub-tracker.ts`) is passed as
|
|
11
|
+
`existingServices` to `createSingletonServices`, and its proxy answers every
|
|
12
|
+
property with `tracker.stub(prop)` — every property except `schema`, which
|
|
13
|
+
returns `undefined` so the service factory goes on to construct a real schema
|
|
14
|
+
service.
|
|
15
|
+
|
|
16
|
+
A stub's methods resolve `undefined` and record the call. Applied to
|
|
17
|
+
`validateSchema`, that means validation always "passes": a request missing a
|
|
18
|
+
required field, or carrying a wrong type, sails through. Every scenario that
|
|
19
|
+
believed it was exercising input validation would be asserting on nothing, and
|
|
20
|
+
would keep passing after validation broke. The one-line exception in the proxy is
|
|
21
|
+
what keeps the test suite honest.
|
|
22
|
+
|
|
23
|
+
**What this rules out:** simplifying the `get` trap to `return tracker.stub(prop)`
|
|
24
|
+
for all properties, and adding `schema` to any list of services a scenario is
|
|
25
|
+
allowed to fake. If a scenario needs schema behaviour changed, change the schema,
|
|
26
|
+
not the service.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: decision
|
|
3
|
+
title: Trigger declaration is split from trigger source
|
|
4
|
+
description: Triggers are declared everywhere but subscribed only in the trigger worker, so app processes never open the underlying subscription
|
|
5
|
+
tags: trigger
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Trigger declaration is split from trigger source
|
|
9
|
+
|
|
10
|
+
`wireTrigger` and `wireTriggerSource` in
|
|
11
|
+
`packages/core/src/wirings/trigger/trigger-runner.ts` are deliberately two
|
|
12
|
+
separate registrations for the same trigger name. `wireTrigger` declares the
|
|
13
|
+
trigger name and the target pikku function; it is meant to be loaded by every
|
|
14
|
+
process, because the inspector extracts it at build time and every runtime needs
|
|
15
|
+
the name-to-function mapping in `pikkuState(null, 'trigger', 'meta')`.
|
|
16
|
+
`wireTriggerSource` carries the actual subscription implementation
|
|
17
|
+
(`CorePikkuTriggerFunction`, which opens the connection and returns a teardown)
|
|
18
|
+
and is only imported by the trigger worker process.
|
|
19
|
+
|
|
20
|
+
The split exists because the subscription has side effects at import-and-setup
|
|
21
|
+
time — a Redis `subscribe`, a socket, a poller. If every API instance loaded the
|
|
22
|
+
source, every instance would open its own subscription and the trigger would fire
|
|
23
|
+
once per instance instead of once per event. Keeping the source out of the
|
|
24
|
+
general bundle also keeps the trigger's transport dependency out of runtimes that
|
|
25
|
+
never need it. `CoreTriggerSource.name` must therefore match a `wireTrigger`
|
|
26
|
+
name exactly; nothing in the type system enforces that, and a mismatch shows up
|
|
27
|
+
only at `setupTrigger` time as `Trigger source not found`.
|
|
28
|
+
|
|
29
|
+
**What this rules out:** merging `wireTrigger` and `wireTriggerSource` into one
|
|
30
|
+
registration call, re-exporting trigger sources from a package barrel that
|
|
31
|
+
application code imports, or "simplifying" by having `wireTrigger` accept the
|
|
32
|
+
subscription function directly. Any of these pulls the subscription into every
|
|
33
|
+
process and silently multiplies trigger firings by the instance count.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: decision
|
|
3
|
+
title: TypedSecretService caches for the process lifetime
|
|
4
|
+
description: Resolved secrets are cached with no TTL, so a secret rotated out of band is not picked up until restart — tracked as pikkujs/pikku#964
|
|
5
|
+
tags: services
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# TypedSecretService caches for the process lifetime
|
|
9
|
+
|
|
10
|
+
`TypedSecretService` (`packages/core/src/services/typed-secret-service.ts`) keeps
|
|
11
|
+
an in-process `Map` of resolved secrets so callers can read naively without
|
|
12
|
+
hitting the underlying secret service on every call. Only successful reads are
|
|
13
|
+
cached — a miss throws and is not stored, so the cache never memoises a negative
|
|
14
|
+
— and `setSecret` / `deleteSecret` invalidate the key they touch.
|
|
15
|
+
|
|
16
|
+
There is no TTL and no background refresh. One instance is created per
|
|
17
|
+
`createSingletonServices`, which in practice means once per process, so a secret
|
|
18
|
+
rotated out of band is not observed until the process restarts. That is a known
|
|
19
|
+
gap, tracked as pikkujs/pikku#964, not an oversight to rediscover.
|
|
20
|
+
|
|
21
|
+
**What this rules out:** assuming a rotation flow that writes to the secret
|
|
22
|
+
backend takes effect in a running instance — it does not; the deployment has to
|
|
23
|
+
cycle. It also means this cache is not a read-through cache in the usual sense,
|
|
24
|
+
so do not "fix" it by caching misses. Related: the cached values are plaintext
|
|
25
|
+
secrets held on the long-lived singleton services object, which is why a batch
|
|
26
|
+
`getSecrets([...everything])` should be avoided.
|
package/knowledge/decisions/internals/webhook-delivery-history-records-every-attempt-best-effort.md
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: decision
|
|
3
|
+
title: Webhook delivery history records every attempt, best effort
|
|
4
|
+
description: The webhook worker persists each attempt before it throws, and a failure to persist is logged rather than allowed to mask the delivery result
|
|
5
|
+
tags: services
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Webhook delivery history records every attempt, best effort
|
|
9
|
+
|
|
10
|
+
`pikkuWebhookWorkerFunc` (`packages/core/src/services/queue-webhook-service.ts`)
|
|
11
|
+
POSTs the delivery, then — when the job carries a `deliveryId` — calls
|
|
12
|
+
`webhookService.recordAttempt` with the outcome *before* throwing on a non-2xx.
|
|
13
|
+
The throw is what makes the queue retry, so recording first is the only way the
|
|
14
|
+
console's delivery history shows every try rather than just the final one.
|
|
15
|
+
|
|
16
|
+
A `deliveryId` is only present when a store-backed implementation (e.g.
|
|
17
|
+
`KyselyWebhookService`) enqueued the job, which is why the queue-only default's
|
|
18
|
+
base `recordAttempt` — which throws `NotImplementedError` — is never reached.
|
|
19
|
+
The `recordAttempt` call is wrapped in `.catch(log)`: history is bookkeeping, and
|
|
20
|
+
a store outage must not turn a delivered webhook into a failed one, nor a failed
|
|
21
|
+
one into an unexplained crash.
|
|
22
|
+
|
|
23
|
+
**What this rules out:** awaiting `recordAttempt` without the catch, moving it
|
|
24
|
+
after the throw, or making the worker's success depend on the store being
|
|
25
|
+
reachable. Equally, do not delete the `.catch` as a swallowed error — the log
|
|
26
|
+
line is the intended handling.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: decision
|
|
3
|
+
title: Webhook service collaborators are constructor args, not locator lookups
|
|
4
|
+
description: QueueWebhookService takes its queue as a constructor parameter so a project wiring webhooks without a queue fails to compile instead of at first send
|
|
5
|
+
tags: services
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Webhook service collaborators are constructor args, not locator lookups
|
|
9
|
+
|
|
10
|
+
`QueueWebhookService` (`packages/core/src/services/queue-webhook-service.ts`)
|
|
11
|
+
takes `queueService` as a constructor parameter, even though it reaches for
|
|
12
|
+
`config` and `secrets` through `getSingletonServices()` inside its methods. The
|
|
13
|
+
queue is different on purpose: it is the collaborator without which the class
|
|
14
|
+
cannot do anything at all.
|
|
15
|
+
|
|
16
|
+
A `getSingletonServices()` lookup turns a wiring mistake into a runtime failure
|
|
17
|
+
on the first `send()` — typically in production, typically on the first webhook a
|
|
18
|
+
customer was waiting for. A constructor parameter turns the same mistake into a
|
|
19
|
+
type error at the point where the service is wired.
|
|
20
|
+
|
|
21
|
+
**What this rules out:** "simplifying" the constructor away so the class looks
|
|
22
|
+
like its neighbours and pulls the queue from the service locator like everything
|
|
23
|
+
else. The inconsistency inside this class is the decision, not an oversight; if
|
|
24
|
+
anything the pressure should run the other way, toward making `config` and
|
|
25
|
+
`secrets` constructor args too.
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: decision
|
|
3
|
+
title: Whether a run is inline is read from the run record
|
|
4
|
+
description: The runContexts map is a read-through cache over WorkflowRun.inline and a lifetime for replay ordinals, never the answer to what a run is
|
|
5
|
+
tags: workflow, state
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Whether a run is inline is read from the run record
|
|
9
|
+
|
|
10
|
+
`PikkuWorkflowService.isInline` resolves `WorkflowRun.inline` from the store and
|
|
11
|
+
caches it on the process-local `runContexts` entry for the duration of the
|
|
12
|
+
execution that is running the step. `registerInlineRun` / `unregisterInlineRun`
|
|
13
|
+
prime and evict that cache; they no longer decide anything. Both callers pass
|
|
14
|
+
the same `shouldInline` to `createRun` that they pass to `registerInlineRun`, so
|
|
15
|
+
the prime is only ever a saved read.
|
|
16
|
+
|
|
17
|
+
`inline` decides two forks: whether a failed step retries in-process or is
|
|
18
|
+
handed to the queue, and whether a sleep blocks here or goes to the
|
|
19
|
+
`schedulerService`. Answering them from a `Map` that only the instance that
|
|
20
|
+
called `startWorkflow` ever populated makes both forks instance-dependent. On
|
|
21
|
+
Lambda, Workers, or any multi-instance container deployment, instance B reads an
|
|
22
|
+
empty map, concludes the run is queued, and dispatches an orchestrator job for a
|
|
23
|
+
run instance A is already driving in-process — two executors on one run. The
|
|
24
|
+
durable field is the only thing every instance can agree on.
|
|
25
|
+
|
|
26
|
+
`isInline` is therefore `async`. Its four call sites — `inlineStep` and
|
|
27
|
+
`scheduleSleep` in core, `dispatchStep` and `scheduleSleep` in the Cloudflare
|
|
28
|
+
Durable Object service — were already `async`, so nothing had to become
|
|
29
|
+
asynchronous to accommodate it. Resolving it through `getRunIdentity` means a
|
|
30
|
+
run job that already read its run pays no second read, and caching the answer on
|
|
31
|
+
the context bounds a step worker to one read. `WorkflowRun.inline` is written
|
|
32
|
+
once at `createRun` and never mutated, so a cached value cannot go stale.
|
|
33
|
+
|
|
34
|
+
The same entry carries per-replay ordinals, the last step name, and the step
|
|
35
|
+
snapshot. Those are genuinely process-local — they exist to give
|
|
36
|
+
`name`, `name#1`, `name#2` to repeated reaches of one logical step within a
|
|
37
|
+
single walk of a workflow, and to let a replay read its rows once. Their
|
|
38
|
+
lifetime is now an explicit `activeExecutions` count incremented by
|
|
39
|
+
`runWorkflowJob` and by `executeWorkflowStep` and decremented in their `finally`
|
|
40
|
+
blocks; the entry is deleted when the count reaches zero, and a terminal
|
|
41
|
+
`updateRunStatus` that arrives mid-execution defers to that. Previously
|
|
42
|
+
`nextStepKey` lazily created a `replay` object on every step invocation while
|
|
43
|
+
`releaseContext` refused to free any entry that had one, so any step reached
|
|
44
|
+
outside a `beginReplay` bracket — a graph node's RPC using its workflow wire
|
|
45
|
+
from the step-worker queue — stranded an ordinals map and a step-state snapshot
|
|
46
|
+
for the life of the process.
|
|
47
|
+
|
|
48
|
+
Resetting ordinals per execution rather than letting them accumulate across
|
|
49
|
+
separate step-worker invocations also makes step naming independent of how the
|
|
50
|
+
work happened to be distributed. Two step workers in one process used to see
|
|
51
|
+
different ordinals than the same two workers on different instances.
|
|
52
|
+
|
|
53
|
+
**What this rules out:** answering `isInline` from the map alone and defaulting
|
|
54
|
+
to `false` on a miss, which is the split-brain; a synchronous `isInline` backed
|
|
55
|
+
by a cache nothing guarantees is populated; keeping a run's entry alive because
|
|
56
|
+
a value is cached in it, since a cache that is never evicted is a leak; and
|
|
57
|
+
mutating `WorkflowRun.inline` after creation, which the cache is only safe
|
|
58
|
+
because nothing does.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: decision
|
|
3
|
+
title: Workflow approval expiry is decided from a recorded deadline, not from a timer firing
|
|
4
|
+
description: The wake-up job is best-effort liveness; losing, duplicating or delaying it cannot change the gate's answer
|
|
5
|
+
tags: workflow
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Workflow approval expiry is decided from a recorded deadline, not from a timer firing
|
|
9
|
+
|
|
10
|
+
On its first reach, `approvalStep` in `pikku-workflow-service.ts` stamps
|
|
11
|
+
`expiresAt` into the gate's run-state record and calls `scheduleRunWake`. Every
|
|
12
|
+
later replay decides expiry by comparing `Date.now()` against that recorded
|
|
13
|
+
deadline. A duplicate, late, or entirely dropped timer therefore all produce the
|
|
14
|
+
same answer, and losing the wake costs liveness — the run sits until something
|
|
15
|
+
else resumes it — never correctness.
|
|
16
|
+
|
|
17
|
+
`scheduleRunWake` deliberately enqueues a delayed *orchestrator* pass rather
|
|
18
|
+
than reusing `scheduleSleep`. `scheduleSleep` resolves the step it is given,
|
|
19
|
+
which for an approval would resolve the gate itself; the wake only nudges the
|
|
20
|
+
run to replay and re-evaluate, leaving the gate the sole judge of its own
|
|
21
|
+
outcome. It is wrapped in a try/catch that logs and continues, because a
|
|
22
|
+
scheduling failure must not fail the run.
|
|
23
|
+
|
|
24
|
+
An approval returns an `ApprovalOutcome` union (`decided` | `expired`) rather
|
|
25
|
+
than throwing on the deadline, so callers are forced to handle expiry and "skip
|
|
26
|
+
it and carry on" stays trivial. `decided` means a human answered — whether the
|
|
27
|
+
answer was yes or no rides in `data` and is the application's business.
|
|
28
|
+
|
|
29
|
+
**What this rules out:** treating the timer's delivery as the expiry signal,
|
|
30
|
+
routing the wake through `scheduleSleep` or any path that writes the gate's step
|
|
31
|
+
result, letting a `scheduleRunWake` failure propagate, or turning expiry into a
|
|
32
|
+
thrown error. Expiry also fires unconditionally once enqueued — a durable timer
|
|
33
|
+
cannot be retracted — so the replay must no-op it when a decision has already
|
|
34
|
+
landed.
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: decision
|
|
3
|
+
title: Core declares the scenario browser surface structurally and never imports a driver
|
|
4
|
+
description: `@pikku/core` must stay dependency-free for edge runtimes, so playwright augments the interface instead of being imported by it
|
|
5
|
+
tags: workflow
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Core declares the scenario browser surface structurally and never imports a driver
|
|
9
|
+
|
|
10
|
+
`PikkuBrowserWire`, `TestIdSelector`, `ScenarioBrowserProvider` and
|
|
11
|
+
`ScenarioBrowserFailure` all live in `scenario-step.types.ts` as plain
|
|
12
|
+
structural types. `@pikku/core` deliberately never imports playwright — it has
|
|
13
|
+
to stay dependency-free for edge runtimes — so `@pikku/playwright` augments
|
|
14
|
+
`PikkuBrowserWire` via `declare module`, and `wire.browser.page` becomes a fully
|
|
15
|
+
typed Playwright `Page` only in a project that installs it. Declaring the
|
|
16
|
+
provider contract here is also what lets the CLI depend on core alone.
|
|
17
|
+
|
|
18
|
+
`reset` and `captureFailure` are optional on `ScenarioBrowserProvider` so a
|
|
19
|
+
driver written against an earlier version keeps compiling; the runner treats a
|
|
20
|
+
driver without them as one offering no isolation and no diagnostics.
|
|
21
|
+
`captureFailure` must never throw — a failure to capture must not replace the
|
|
22
|
+
failure being captured — and exists because a browser step fails with a selector
|
|
23
|
+
timeout that says nothing about *why* the page never rendered; the answer is
|
|
24
|
+
almost always in the page's own console and request errors, which the driver has
|
|
25
|
+
been collecting all along.
|
|
26
|
+
|
|
27
|
+
`TestIdSelector` is richer than a bare `data-testid` because one rarely names
|
|
28
|
+
exactly one element: `where` matches the element's own data attributes (so a
|
|
29
|
+
step asserts a status without reading translated copy back to the app), `prefix`
|
|
30
|
+
matches a family of ids, `containing` picks the match holding a piece of text,
|
|
31
|
+
and `within` scopes the lookup to one row or section. Core defines the shape;
|
|
32
|
+
the driver resolves it against a real page.
|
|
33
|
+
|
|
34
|
+
The same dependency discipline applies at runtime: `resolveScenarioActors` in
|
|
35
|
+
`pikku-scenario-service.ts` imports the HTTP actor client lazily, so even a
|
|
36
|
+
runner bundle only pays for the AI persona conversation loop when a scenario
|
|
37
|
+
actually signs an actor in.
|
|
38
|
+
|
|
39
|
+
**What this rules out:** importing playwright (or any driver) from core,
|
|
40
|
+
tightening `reset`/`captureFailure` to required, letting `captureFailure` throw,
|
|
41
|
+
reducing `TestIdSelector` to a plain string, or making the actor-client import
|
|
42
|
+
static.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: decision
|
|
3
|
+
title: Workflow DSL meta keeps runtime expressions in their own field, apart from literal values
|
|
4
|
+
description: A string `value` regenerates as a string literal; an `expression` regenerates as code, so the two can never share a field
|
|
5
|
+
tags: workflow
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Workflow DSL meta keeps runtime expressions in their own field, apart from literal values
|
|
9
|
+
|
|
10
|
+
Several step metas in `dsl/workflow-dsl.types.ts` carry a literal field and a
|
|
11
|
+
parallel `expression` field: `SetStepMeta` has `value` and `expression`
|
|
12
|
+
(`count + 1`), `SleepStepMeta` has `duration` and `expression` (a duration known
|
|
13
|
+
only at runtime, e.g. a loop variable). They are mutually exclusive by
|
|
14
|
+
construction because regenerated code has to emit them differently — a string
|
|
15
|
+
`value` becomes a string literal, an `expression` becomes raw code. Collapsing
|
|
16
|
+
them would make every computed assignment regenerate as a quoted string.
|
|
17
|
+
|
|
18
|
+
Two related shapes exist for the same "the extractor cannot see this
|
|
19
|
+
statically" reason. `ReturnStepMeta.spread` records variables spread into a
|
|
20
|
+
returned object (`return { ...r }`) or a sole returned variable (`return r`) by
|
|
21
|
+
name, because their fields are not enumerable statically and cannot be expanded
|
|
22
|
+
into `outputs`. `FeatureMeta.unresolvedEntries` counts feature entries that
|
|
23
|
+
could not be read statically (a spread, a `.map()`), so a non-zero count marks
|
|
24
|
+
the listing as partial rather than pretending it is complete.
|
|
25
|
+
|
|
26
|
+
`FanoutStepMeta.stepName` is optional for a different structural reason: a
|
|
27
|
+
fanout is not itself a cached step, and node ids are step names — borrowing a
|
|
28
|
+
body step's name would give the loop and that step the same id, collapsing one
|
|
29
|
+
onto the other.
|
|
30
|
+
|
|
31
|
+
Free-text documentation (`GraphNodeConfig.notes`,
|
|
32
|
+
`PikkuWorkflowGraphConfig.notes`) is excluded from the graph topology hash, so
|
|
33
|
+
editing a note never marks the workflow as changed and never triggers a version
|
|
34
|
+
mismatch on in-flight runs.
|
|
35
|
+
|
|
36
|
+
**What this rules out:** merging `expression` into `value`/`duration`, expanding
|
|
37
|
+
`spread` into concrete `outputs`, giving a fanout a required `stepName` taken
|
|
38
|
+
from its body, and folding `notes` into `graphHash`.
|
package/knowledge/decisions/internals/workflow-features-resolve-scenarios-by-object-identity.md
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: decision
|
|
3
|
+
title: A feature resolves its scenarios by object identity, never by name or shape
|
|
4
|
+
description: An unregistered scenario comes back explicitly unresolved rather than silently running as something else
|
|
5
|
+
tags: workflow
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# A feature resolves its scenarios by object identity, never by name or shape
|
|
9
|
+
|
|
10
|
+
`resolveFeatureScenarios` in `feature.ts` builds a `Map` keyed by the registered
|
|
11
|
+
config object itself and looks each feature entry up in it. `pikkuScenario`
|
|
12
|
+
returns its config verbatim and `addWorkflow` registers that same object, so a
|
|
13
|
+
feature holding the imported identifier holds the very object that was
|
|
14
|
+
registered. Nothing is matched by shape, by name, or by any other guess.
|
|
15
|
+
|
|
16
|
+
That is also why a scenario built inline inside a feature — and therefore never
|
|
17
|
+
registered — comes back in `unresolved` rather than silently running as
|
|
18
|
+
something else. Because scenarios are referenced by imported identifier, a
|
|
19
|
+
renamed or deleted scenario is a compile error rather than a silent skip.
|
|
20
|
+
|
|
21
|
+
Entries are returned in declaration order and features in registration order,
|
|
22
|
+
since a feature's reading order is its declaration order. A scenario's effective
|
|
23
|
+
tags are its own unioned with the containing feature's, so a tag filter selects
|
|
24
|
+
through the feature.
|
|
25
|
+
|
|
26
|
+
**What this rules out:** falling back to name matching when identity lookup
|
|
27
|
+
misses, structurally comparing configs, or dropping unresolved entries silently
|
|
28
|
+
instead of reporting them — each turns "this scenario is not registered" into
|
|
29
|
+
"some other scenario ran instead".
|
package/knowledge/decisions/internals/workflow-graph-inline-and-queued-runs-share-one-planner.md
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: decision
|
|
3
|
+
title: Inline and queued workflow graph runs share one transition planner
|
|
4
|
+
description: A second, weaker inline traversal would lose joins, cycle revisits and step provenance that the queued path has
|
|
5
|
+
tags: workflow
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Inline and queued workflow graph runs share one transition planner
|
|
9
|
+
|
|
10
|
+
`planGraphTransitions` in `graph/graph-runner.ts` is the single place that
|
|
11
|
+
decides which nodes fire next. `continueGraph` (queued) and
|
|
12
|
+
`continueGraphInline` (in-process loop) both call it; they differ only in
|
|
13
|
+
whether the planned wave is dispatched via `queueGraphNode` or executed by
|
|
14
|
+
`executeGraphNodeInline`. Sharing the planner is what gives the inline path
|
|
15
|
+
joins, cycle revisits and `fromStepName` provenance identical to the queue,
|
|
16
|
+
instead of a second and weaker traversal. `executeGraphNodeInline` persists
|
|
17
|
+
under the same physical instance key and records the same predecessor as
|
|
18
|
+
`queueGraphNode` for the same reason.
|
|
19
|
+
|
|
20
|
+
The planner distinguishes two kinds of edge. A forward edge is node-once: the
|
|
21
|
+
target fires only if it has no instance yet, so converging edges (joins)
|
|
22
|
+
collapse to a single run. A back-edge — one whose target can reach the source,
|
|
23
|
+
detected by `closesCycle` — is a revisit: it fires a fresh ordinal instance
|
|
24
|
+
(`target#1`, …) and is edge-once on `from → target` so it does not re-fire every
|
|
25
|
+
tick. Cycles terminate when branch routing stops looping back, and every
|
|
26
|
+
instance records the predecessor it was reached from.
|
|
27
|
+
|
|
28
|
+
`remapStepNamesToNodeIds` and `remapBranchKeys` are called on the completed and
|
|
29
|
+
branch sets even where their results are discarded: planning keys steps
|
|
30
|
+
physically, so those calls exist only to surface an ambiguous template-node
|
|
31
|
+
config as an error.
|
|
32
|
+
|
|
33
|
+
On the queued path a node that sets no `retries` falls back to
|
|
34
|
+
`DEFAULT_STEP_RETRIES` rather than to zero, so the persisted step's retry count
|
|
35
|
+
matches the queue job's `attempts` (see `resolveStepJobOptions`). A step row
|
|
36
|
+
claiming one attempt while the queue silently delivers five is the kind of
|
|
37
|
+
disagreement that makes a retry bug unreadable from the outside.
|
|
38
|
+
|
|
39
|
+
**What this rules out:** writing a separate traversal for the inline path,
|
|
40
|
+
making forward edges fire per-edge (which breaks joins) or back-edges fire
|
|
41
|
+
node-once (which breaks loops), dropping `fromStepName` from either path, and
|
|
42
|
+
deleting the "unused" remap calls as dead code.
|
package/knowledge/decisions/internals/workflow-graph-node-notes-are-excluded-from-the-graph-hash.md
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: decision
|
|
3
|
+
title: Workflow graph node notes are non-semantic and excluded from the graph hash
|
|
4
|
+
description: Documentation on a node must not count as a topology change, or editing a comment redeploys the workflow
|
|
5
|
+
tags: workflow
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Workflow graph node notes are non-semantic and excluded from the graph hash
|
|
9
|
+
|
|
10
|
+
`GraphNodeConfig.notes` (`graph/workflow-graph.types.ts`, mirrored on the typed
|
|
11
|
+
builder in `graph/graph-node.ts`) is free-text documentation attached to a node.
|
|
12
|
+
It is deliberately excluded from the graph topology hash (`graphHash`), so
|
|
13
|
+
editing a note never marks the workflow as changed. The graph-level `notes`
|
|
14
|
+
field on `wireWorkflowGraph` (`graph/wire-workflow-graph.ts`) — which carries
|
|
15
|
+
things like imported sticky notes — is excluded for the same reason.
|
|
16
|
+
|
|
17
|
+
The hash exists to answer "is this the same workflow the running instances were
|
|
18
|
+
started against?". Prose about a node has no bearing on that. If notes were
|
|
19
|
+
hashed, adding a sentence of documentation would register as a topology change
|
|
20
|
+
and the only safe habit would be to never document a node.
|
|
21
|
+
|
|
22
|
+
**What this rules out:** folding `notes` into `graphHash` "for completeness",
|
|
23
|
+
or using `notes` to carry anything semantic — a routing hint, a version marker,
|
|
24
|
+
a flag some other code reads — since nothing that changes behaviour may live in
|
|
25
|
+
a field the change-detection hash ignores.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: decision
|
|
3
|
+
title: An inline workflow run reports its run id the moment it exists, because a failure throws instead of returning
|
|
4
|
+
description: `onRunCreated` is the only moment guaranteed to happen whether the run passes, fails or suspends
|
|
5
|
+
tags: workflow
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# An inline workflow run reports its run id the moment it exists, because a failure throws instead of returning
|
|
9
|
+
|
|
10
|
+
`startWorkflow` in `pikku-workflow-service.ts` calls `options.onRunCreated(runId)`
|
|
11
|
+
immediately after `createRun`, before any execution. An inline run that fails
|
|
12
|
+
throws rather than returning `{ runId }`, so a caller wanting to read the run
|
|
13
|
+
back — its steps, which one failed, what it was called with — would otherwise
|
|
14
|
+
have nothing to read it by. Run creation is the only moment guaranteed to happen
|
|
15
|
+
whether the run goes on to pass, fail or suspend.
|
|
16
|
+
|
|
17
|
+
The failure path is equally deliberate. `WorkflowAsyncException`,
|
|
18
|
+
`WorkflowCancelledException`, `WorkflowSuspendedException` and
|
|
19
|
+
`WorkflowDispatchException` are all excluded from the "mark the run failed"
|
|
20
|
+
branch: the first three already recorded their own status, and the fourth is
|
|
21
|
+
transient. When a run does fail, an *expected* error (a `PikkuError`, e.g. a
|
|
22
|
+
build gate tripping) logs only its message — the message is the whole story, and
|
|
23
|
+
the `expected` flag survives the step-boundary rehydration that strips the
|
|
24
|
+
class. Anything else is logged in full so the trace is there to debug.
|
|
25
|
+
|
|
26
|
+
**What this rules out:** moving `onRunCreated` to after execution or into the
|
|
27
|
+
success path, returning a sentinel run id instead of throwing, folding the four
|
|
28
|
+
control-flow exceptions into the generic failure branch, or dumping a stack for
|
|
29
|
+
every expected error.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: decision
|
|
3
|
+
title: `invocationId` is a workflow step's dedupe key; `stepId` is store-specific and must never be used as one
|
|
4
|
+
description: The invocation id is a frozen UUIDv5 of runId + stepName, identical across retries on every backend
|
|
5
|
+
tags: workflow
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# `invocationId` is a workflow step's dedupe key; `stepId` is store-specific and must never be used as one
|
|
9
|
+
|
|
10
|
+
`deriveInvocationId` (`workflow-invocation-id.ts`) is `uuidv5(runId:stepName)`.
|
|
11
|
+
Because both inputs are stable across replays, the same call yields the same
|
|
12
|
+
UUID on every attempt and on every storage backend — so a step can
|
|
13
|
+
`INSERT … ON CONFLICT (invocation_id)` or pass it as an external idempotency
|
|
14
|
+
key (a Stripe key, say) and have a retry of a half-applied side effect collapse
|
|
15
|
+
onto the first attempt.
|
|
16
|
+
|
|
17
|
+
`stepId` cannot do that job. Whether it stays the same or is minted fresh per
|
|
18
|
+
attempt is store-specific: the in-memory store mints a new one each attempt
|
|
19
|
+
while the SQL store reuses the row. `WorkflowStepWire` documents `invocationId`
|
|
20
|
+
as the dedupe key for exactly this reason.
|
|
21
|
+
|
|
22
|
+
`PIKKU_WORKFLOW_NAMESPACE` in `workflow-invocation-id.ts` is frozen. Changing it
|
|
23
|
+
would alter every derived invocation id and break dedupe across a deploy — steps
|
|
24
|
+
that already ran would look new. The v5 implementation is hand-rolled (SHA-1
|
|
25
|
+
plus the version and variant bit twiddling) rather than pulled from the `uuid`
|
|
26
|
+
package, and `workflow-invocation-id.test.ts` pins it against the known
|
|
27
|
+
`www.example.com`-in-DNS-namespace vector.
|
|
28
|
+
|
|
29
|
+
Calling the same step name more than once in a run *is* disambiguated. Ordinals
|
|
30
|
+
are already in: `nextStepKey` (`pikku-workflow-service.ts`) mints a physical key
|
|
31
|
+
per reach — `name`, then `name#1`, `name#2` — and every step entry point routes
|
|
32
|
+
through it, so `deriveInvocationId` hashes the physical key, not the logical
|
|
33
|
+
name. Each reach therefore gets its own row and its own invocation id. The first
|
|
34
|
+
reach keeps the bare name, so ids minted before ordinals landed are unchanged,
|
|
35
|
+
and the ordinal counters reset at the start of each replay, so a given call site
|
|
36
|
+
resolves to the same key on every attempt. `workflow-step-ordinal.test.ts` pins
|
|
37
|
+
all three properties.
|
|
38
|
+
|
|
39
|
+
**What this rules out:** using `stepId` as an idempotency key, regenerating the
|
|
40
|
+
namespace UUID, swapping in a different hash or a random id, passing the logical
|
|
41
|
+
step name to `deriveInvocationId` instead of the physical key from
|
|
42
|
+
`nextStepKey`, or "simplifying" the derivation to include anything that varies
|
|
43
|
+
between replays.
|
package/knowledge/decisions/internals/workflow-queued-step-dispatch-requires-an-explicit-opt-in.md
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: decision
|
|
3
|
+
title: A workflow step goes through the queue only if its function opts in, and there is no inline fallback
|
|
4
|
+
description: `workflowQueued: true` is the whole decision; a missing queue service is a hard error, not a silent downgrade
|
|
5
|
+
tags: workflow
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# A workflow step goes through the queue only if its function opts in, and there is no inline fallback
|
|
9
|
+
|
|
10
|
+
`dispatchStep` in `pikku-workflow-service.ts` decides queued-vs-inline purely
|
|
11
|
+
from the step function's `workflowQueued` flag in function meta, which defaults
|
|
12
|
+
to false. If the flag is not set the method returns false and the caller runs
|
|
13
|
+
the step inline. If it IS set and no queue service is configured, that is a hard
|
|
14
|
+
error naming the step and the function — never a quiet downgrade to inline
|
|
15
|
+
execution, because a function marked `workflowQueued` was marked that way for a
|
|
16
|
+
reason (isolation, a long tail latency, a resource limit) that inline execution
|
|
17
|
+
does not honour.
|
|
18
|
+
|
|
19
|
+
Inline execution is not merely a degraded queue path. `runInlineRetryLoop` wraps
|
|
20
|
+
the same `running → result` / `fail → retry-attempt → backoff → retry`
|
|
21
|
+
scaffolding around a step-specific body and stays O(K) — no suspend, no replay —
|
|
22
|
+
which is what makes an inline run cheap. Its optional `onError` hook exists for
|
|
23
|
+
terminal errors that must NOT retry (RPC-not-found suspends the run for
|
|
24
|
+
redeploy); if the hook throws, the loop exits immediately without recording a
|
|
25
|
+
step error or retrying.
|
|
26
|
+
|
|
27
|
+
**What this rules out:** inferring "should queue" from the presence of a queue
|
|
28
|
+
service, falling back to inline when the queue is missing, and reusing
|
|
29
|
+
`runInlineRetryLoop` for anything that needs to suspend between attempts.
|