@pikku/core 0.12.72 → 0.12.74
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +793 -0
- package/dist/crypto-utils.d.ts +30 -5
- package/dist/crypto-utils.js +146 -41
- package/dist/dev/hot-reload.js +11 -30
- package/dist/dev/module-runner.d.ts +3 -7
- package/dist/dev/module-runner.js +4 -10
- package/dist/dev/reload-meta.d.ts +8 -20
- package/dist/dev/reload-meta.js +9 -29
- package/dist/errors/error-handler.d.ts +5 -30
- package/dist/errors/error-handler.js +16 -32
- package/dist/errors/errors.d.ts +32 -151
- package/dist/errors/errors.js +55 -157
- package/dist/function/abort-scope.d.ts +47 -0
- package/dist/function/abort-scope.js +63 -0
- package/dist/function/function-runner.js +37 -32
- package/dist/function/functions.types.d.ts +44 -136
- package/dist/function/functions.types.js +0 -58
- package/dist/function/list.types.d.ts +12 -62
- package/dist/function/list.types.js +4 -25
- package/dist/handle-error.d.ts +0 -13
- package/dist/handle-error.js +0 -18
- package/dist/index.d.ts +5 -2
- package/dist/index.js +4 -1
- package/dist/middleware/auth-apikey.d.ts +3 -18
- package/dist/middleware/auth-apikey.js +0 -17
- package/dist/middleware/auth-bearer.d.ts +6 -41
- package/dist/middleware/auth-bearer.js +3 -40
- package/dist/middleware/auth-cookie.d.ts +5 -27
- package/dist/middleware/auth-cookie.js +2 -26
- package/dist/middleware/cors.d.ts +7 -34
- package/dist/middleware/cors.js +7 -34
- package/dist/middleware/remote-auth.d.ts +3 -1
- package/dist/middleware/remote-auth.js +3 -2
- package/dist/middleware/telemetry.d.ts +8 -33
- package/dist/middleware/telemetry.js +2 -31
- package/dist/middleware-runner.d.ts +4 -55
- package/dist/middleware-runner.js +5 -74
- package/dist/permissions.d.ts +3 -44
- package/dist/permissions.js +19 -71
- package/dist/pikku-request.d.ts +0 -6
- package/dist/pikku-request.js +0 -6
- package/dist/pikku-state.d.ts +0 -26
- package/dist/pikku-state.js +2 -30
- package/dist/remote.d.ts +3 -5
- package/dist/remote.js +8 -7
- package/dist/schema.d.ts +5 -39
- package/dist/schema.js +5 -39
- package/dist/scopes.d.ts +4 -23
- package/dist/scopes.js +7 -48
- package/dist/services/ai-agent-runner-service.d.ts +20 -0
- package/dist/services/ai-embedding-service.d.ts +2 -25
- package/dist/services/audit-service.js +1 -2
- package/dist/services/content-service.d.ts +1 -46
- package/dist/services/credential-service.d.ts +3 -40
- package/dist/services/deployment-service.d.ts +3 -9
- package/dist/services/gateway-service.d.ts +0 -15
- package/dist/services/http-personas.d.ts +80 -0
- package/dist/services/http-personas.js +233 -0
- package/dist/services/in-memory-queue-service.d.ts +0 -14
- package/dist/services/in-memory-queue-service.js +1 -15
- package/dist/services/in-memory-trigger-service.d.ts +0 -18
- package/dist/services/in-memory-trigger-service.js +1 -18
- package/dist/services/in-memory-workflow-service.d.ts +0 -16
- package/dist/services/in-memory-workflow-service.js +4 -33
- package/dist/services/index.d.ts +5 -6
- package/dist/services/index.js +2 -5
- package/dist/services/istanbul-coverage-service.d.ts +1 -5
- package/dist/services/istanbul-coverage-service.js +2 -8
- package/dist/services/jwt-service.d.ts +1 -16
- package/dist/services/local-content.d.ts +13 -2
- package/dist/services/local-content.js +40 -13
- package/dist/services/local-gateway-service.d.ts +0 -16
- package/dist/services/local-gateway-service.js +2 -17
- package/dist/services/local-secrets.d.ts +0 -4
- package/dist/services/local-secrets.js +0 -4
- package/dist/services/logger-console.d.ts +3 -7
- package/dist/services/logger-console.js +3 -7
- package/dist/services/logger.d.ts +2 -37
- package/dist/services/meta-service.d.ts +23 -26
- package/dist/services/meta-service.js +22 -36
- package/dist/services/personas-service.d.ts +134 -0
- package/dist/services/personas-service.js +40 -0
- package/dist/services/pikku-user-id.js +0 -4
- package/dist/services/queue-webhook-service.d.ts +2 -36
- package/dist/services/queue-webhook-service.js +9 -41
- package/dist/services/scheduler-service.d.ts +1 -50
- package/dist/services/scheduler-service.js +0 -10
- package/dist/services/schema-service.d.ts +1 -24
- package/dist/services/scope-service.d.ts +49 -34
- package/dist/services/scoped-secret-service.d.ts +0 -4
- package/dist/services/scoped-secret-service.js +0 -4
- package/dist/services/secret-host-binding.d.ts +8 -0
- package/dist/services/secret-host-binding.js +36 -0
- package/dist/services/secret-service.d.ts +5 -33
- package/dist/services/secretless.d.ts +6 -0
- package/dist/services/secretless.js +21 -0
- package/dist/services/stub-tracker.d.ts +7 -18
- package/dist/services/stub-tracker.js +8 -18
- package/dist/services/system-role-guard.d.ts +33 -0
- package/dist/services/system-role-guard.js +38 -0
- package/dist/services/trigger-service.d.ts +0 -12
- package/dist/services/typed-secret-service.d.ts +0 -7
- package/dist/services/typed-secret-service.js +1 -7
- package/dist/services/v8-coverage-service.d.ts +2 -3
- package/dist/services/v8-coverage-service.js +1 -2
- package/dist/services/variables-service.d.ts +1 -8
- package/dist/services/webhook-service.d.ts +19 -63
- package/dist/services/webhook-service.js +6 -20
- package/dist/services/workflow-service.d.ts +3 -15
- package/dist/testing/service-tests.js +0 -17
- package/dist/time-utils.d.ts +0 -16
- package/dist/time-utils.js +1 -19
- package/dist/types/core.types.d.ts +99 -219
- package/dist/types/core.types.js +0 -42
- package/dist/types/state.types.d.ts +4 -9
- package/dist/utils/hmac.d.ts +4 -10
- package/dist/utils/hmac.js +4 -10
- package/dist/utils/safe-fetch.d.ts +7 -35
- package/dist/utils/safe-fetch.js +13 -53
- package/dist/utils.d.ts +1 -6
- package/dist/utils.js +6 -15
- package/dist/wirings/actor-flow/actor-flow.types.d.ts +1 -34
- package/dist/wirings/actor-flow/index.d.ts +0 -9
- package/dist/wirings/actor-flow/run-conversation.d.ts +5 -5
- package/dist/wirings/actor-flow/run-conversation.js +14 -7
- package/dist/wirings/ai-agent/ai-agent-agui.d.ts +0 -5
- package/dist/wirings/ai-agent/ai-agent-agui.js +34 -12
- package/dist/wirings/ai-agent/ai-agent-helpers.d.ts +7 -0
- package/dist/wirings/ai-agent/ai-agent-helpers.js +7 -0
- package/dist/wirings/ai-agent/ai-agent-interrupt.d.ts +153 -0
- package/dist/wirings/ai-agent/ai-agent-interrupt.js +256 -0
- package/dist/wirings/ai-agent/ai-agent-memory.js +0 -2
- package/dist/wirings/ai-agent/ai-agent-model-config.d.ts +0 -9
- package/dist/wirings/ai-agent/ai-agent-model-config.js +1 -9
- package/dist/wirings/ai-agent/ai-agent-prepare.d.ts +10 -99
- package/dist/wirings/ai-agent/ai-agent-prepare.js +58 -131
- package/dist/wirings/ai-agent/ai-agent-registry.d.ts +2 -1
- package/dist/wirings/ai-agent/ai-agent-registry.js +5 -1
- package/dist/wirings/ai-agent/ai-agent-runner.js +49 -20
- package/dist/wirings/ai-agent/ai-agent-stream.d.ts +25 -2
- package/dist/wirings/ai-agent/ai-agent-stream.js +153 -63
- package/dist/wirings/ai-agent/ai-agent.types.d.ts +84 -4
- package/dist/wirings/ai-agent/index.d.ts +6 -4
- package/dist/wirings/ai-agent/index.js +5 -4
- package/dist/wirings/ai-agent/voice-input.d.ts +39 -1
- package/dist/wirings/ai-agent/voice-input.js +46 -3
- package/dist/wirings/ai-agent/voice-output.d.ts +54 -1
- package/dist/wirings/ai-agent/voice-output.js +153 -50
- package/dist/wirings/channel/channel-common.d.ts +7 -20
- package/dist/wirings/channel/channel-common.js +7 -21
- package/dist/wirings/channel/channel-handler.js +25 -6
- package/dist/wirings/channel/channel-host-rpc.d.ts +25 -0
- package/dist/wirings/channel/channel-host-rpc.js +38 -0
- package/dist/wirings/channel/channel-middleware-runner.d.ts +0 -12
- package/dist/wirings/channel/channel-middleware-runner.js +0 -12
- package/dist/wirings/channel/channel-rpc-registry.d.ts +31 -0
- package/dist/wirings/channel/channel-rpc-registry.js +89 -0
- package/dist/wirings/channel/channel-rpc-responder.d.ts +15 -0
- package/dist/wirings/channel/channel-rpc-responder.js +71 -0
- package/dist/wirings/channel/channel-rpc-service.d.ts +40 -0
- package/dist/wirings/channel/channel-rpc-service.js +106 -0
- package/dist/wirings/channel/channel-rpc-validators.d.ts +14 -0
- package/dist/wirings/channel/channel-rpc-validators.js +30 -0
- package/dist/wirings/channel/channel-rpc.d.ts +5 -0
- package/dist/wirings/channel/channel-rpc.js +5 -0
- package/dist/wirings/channel/channel-rpc.types.d.ts +90 -0
- package/dist/wirings/channel/channel-rpc.types.js +50 -0
- package/dist/wirings/channel/channel-runner.d.ts +0 -4
- package/dist/wirings/channel/channel-runner.js +0 -14
- package/dist/wirings/channel/channel-store.d.ts +0 -10
- package/dist/wirings/channel/channel.types.d.ts +12 -1
- package/dist/wirings/channel/define-channel-routes.d.ts +0 -20
- package/dist/wirings/channel/define-channel-routes.js +0 -20
- package/dist/wirings/channel/eventhub-service.d.ts +0 -18
- package/dist/wirings/channel/index.d.ts +4 -1
- package/dist/wirings/channel/index.js +2 -0
- package/dist/wirings/channel/local/local-channel-runner.js +3 -1
- package/dist/wirings/channel/local/local-eventhub-service.d.ts +0 -33
- package/dist/wirings/channel/local/local-eventhub-service.js +2 -36
- package/dist/wirings/channel/log-channels.d.ts +0 -4
- package/dist/wirings/channel/log-channels.js +0 -4
- package/dist/wirings/channel/pikku-abstract-channel-handler.js +6 -0
- package/dist/wirings/channel/serverless/serverless-channel-runner.js +2 -5
- package/dist/wirings/cli/channel/cli-approval.d.ts +41 -0
- package/dist/wirings/cli/channel/cli-approval.js +81 -0
- package/dist/wirings/cli/channel/cli-channel-runner.d.ts +0 -4
- package/dist/wirings/cli/channel/cli-channel-runner.js +3 -25
- package/dist/wirings/cli/channel/cli-raw-channel-runner.d.ts +47 -9
- package/dist/wirings/cli/channel/cli-raw-channel-runner.js +24 -16
- package/dist/wirings/cli/channel/cli-raw-client-runner.d.ts +20 -0
- package/dist/wirings/cli/channel/cli-raw-client-runner.js +121 -0
- package/dist/wirings/cli/channel/index.d.ts +4 -0
- package/dist/wirings/cli/channel/index.js +2 -0
- package/dist/wirings/cli/cli-runner.d.ts +20 -20
- package/dist/wirings/cli/cli-runner.js +28 -89
- package/dist/wirings/cli/cli.types.d.ts +14 -3
- package/dist/wirings/cli/command-parser.d.ts +1 -10
- package/dist/wirings/cli/command-parser.js +10 -87
- package/dist/wirings/cli/define-cli-commands.d.ts +1 -17
- package/dist/wirings/cli/define-cli-commands.js +1 -17
- package/dist/wirings/credential/credential.types.d.ts +0 -12
- package/dist/wirings/credential/define-credential.d.ts +48 -0
- package/dist/wirings/credential/define-credential.js +47 -0
- package/dist/wirings/credential/index.d.ts +1 -1
- package/dist/wirings/credential/index.js +1 -1
- package/dist/wirings/credential/validate-credential-definitions.d.ts +2 -4
- package/dist/wirings/gateway/gateway-runner.d.ts +1 -20
- package/dist/wirings/gateway/gateway-runner.js +8 -105
- package/dist/wirings/gateway/gateway.types.d.ts +7 -80
- package/dist/wirings/http/http-routes.d.ts +0 -63
- package/dist/wirings/http/http-routes.js +0 -63
- package/dist/wirings/http/http-runner.d.ts +0 -99
- package/dist/wirings/http/http-runner.js +9 -165
- package/dist/wirings/http/http.types.d.ts +14 -55
- package/dist/wirings/http/log-http-routes.d.ts +0 -4
- package/dist/wirings/http/log-http-routes.js +0 -4
- package/dist/wirings/http/pikku-fetch-http-request.d.ts +0 -40
- package/dist/wirings/http/pikku-fetch-http-request.js +0 -58
- package/dist/wirings/http/pikku-fetch-http-response.js +0 -3
- package/dist/wirings/http/routers/path-to-regex.js +2 -13
- package/dist/wirings/http/web-request.d.ts +0 -8
- package/dist/wirings/http/web-request.js +25 -17
- package/dist/wirings/mcp/mcp-runner.d.ts +1 -4
- package/dist/wirings/mcp/mcp-runner.js +1 -14
- package/dist/wirings/mcp/mcp.types.d.ts +2 -35
- package/dist/wirings/oauth2/oauth2.types.d.ts +0 -28
- package/dist/wirings/oauth2/oauth2.types.js +0 -3
- package/dist/wirings/persona/define-personas.d.ts +28 -0
- package/dist/wirings/persona/define-personas.js +27 -0
- package/dist/wirings/persona/index.d.ts +21 -0
- package/dist/wirings/persona/index.js +17 -0
- package/dist/wirings/persona/persona-email.d.ts +37 -0
- package/dist/wirings/persona/persona-email.js +69 -0
- package/dist/wirings/persona/persona-environments.d.ts +45 -0
- package/dist/wirings/persona/persona-environments.js +81 -0
- package/dist/wirings/persona/persona-mailbox.d.ts +101 -0
- package/dist/wirings/persona/persona-mailbox.js +53 -0
- package/dist/wirings/persona/persona.types.d.ts +125 -0
- package/dist/wirings/persona/persona.types.js +1 -0
- package/dist/wirings/persona/validate-personas.d.ts +53 -0
- package/dist/wirings/persona/validate-personas.js +94 -0
- package/dist/wirings/queue/index.d.ts +3 -0
- package/dist/wirings/queue/index.js +2 -3
- package/dist/wirings/queue/queue-identity.d.ts +28 -0
- package/dist/wirings/queue/queue-identity.js +102 -0
- package/dist/wirings/queue/queue-runner.d.ts +0 -19
- package/dist/wirings/queue/queue-runner.js +9 -30
- package/dist/wirings/queue/queue.types.d.ts +18 -89
- package/dist/wirings/queue/register-queue-helper.d.ts +0 -12
- package/dist/wirings/queue/register-queue-helper.js +0 -11
- package/dist/wirings/queue/signed-queue-service.d.ts +16 -0
- package/dist/wirings/queue/signed-queue-service.js +42 -0
- package/dist/wirings/queue/validate-worker-config.d.ts +2 -23
- package/dist/wirings/queue/validate-worker-config.js +0 -14
- package/dist/wirings/role/define-system-role.d.ts +32 -0
- package/dist/wirings/role/define-system-role.js +31 -0
- package/dist/wirings/role/index.d.ts +3 -0
- package/dist/wirings/role/index.js +2 -0
- package/dist/wirings/role/role.types.d.ts +43 -0
- package/dist/wirings/role/role.types.js +1 -0
- package/dist/wirings/role/validate-role-definitions.d.ts +21 -0
- package/dist/wirings/role/validate-role-definitions.js +71 -0
- package/dist/wirings/rpc/addon-runner.d.ts +0 -19
- package/dist/wirings/rpc/addon-runner.js +0 -51
- package/dist/wirings/rpc/remote-addon-auth.d.ts +1 -12
- package/dist/wirings/rpc/remote-addon-auth.js +1 -9
- package/dist/wirings/rpc/rpc-runner.d.ts +11 -18
- package/dist/wirings/rpc/rpc-runner.js +88 -105
- package/dist/wirings/rpc/rpc-types.d.ts +7 -6
- package/dist/wirings/rpc/wire-addon.d.ts +25 -0
- package/dist/wirings/rpc/wire-addon.js +62 -0
- package/dist/wirings/rpc/wire-remote-addon.d.ts +3 -28
- package/dist/wirings/rpc/wire-remote-addon.js +0 -8
- package/dist/wirings/scheduler/log-schedulers.d.ts +0 -4
- package/dist/wirings/scheduler/log-schedulers.js +0 -4
- package/dist/wirings/scheduler/scheduler-runner.d.ts +0 -1
- package/dist/wirings/scheduler/scheduler-runner.js +0 -1
- package/dist/wirings/scheduler/scheduler.types.d.ts +1 -14
- package/dist/wirings/scope/define-scope.d.ts +32 -0
- package/dist/wirings/scope/define-scope.js +31 -0
- package/dist/wirings/scope/index.d.ts +1 -1
- package/dist/wirings/scope/index.js +1 -1
- package/dist/wirings/scope/scope.types.d.ts +7 -9
- package/dist/wirings/scope/validate-scope-definitions.d.ts +2 -21
- package/dist/wirings/scope/validate-scope-definitions.js +3 -21
- package/dist/wirings/secret/index.d.ts +1 -1
- package/dist/wirings/secret/index.js +1 -1
- package/dist/wirings/secret/secret.types.d.ts +19 -15
- package/dist/wirings/secret/secret.types.js +1 -1
- package/dist/wirings/secret/validate-secret-definitions.d.ts +2 -4
- package/dist/wirings/trigger/trigger-runner.d.ts +0 -27
- package/dist/wirings/trigger/trigger-runner.js +1 -24
- package/dist/wirings/trigger/trigger.types.d.ts +1 -82
- package/dist/wirings/trigger/trigger.types.js +0 -34
- package/dist/wirings/variable/index.d.ts +1 -1
- package/dist/wirings/variable/index.js +1 -1
- package/dist/wirings/variable/validate-variable-definitions.d.ts +2 -4
- package/dist/wirings/variable/variable.types.d.ts +1 -13
- package/dist/wirings/variable/variable.types.js +1 -1
- package/dist/wirings/virtual-user/index.d.ts +27 -0
- package/dist/wirings/virtual-user/index.js +8 -0
- package/dist/wirings/virtual-user/run-virtual-user.d.ts +92 -0
- package/dist/wirings/virtual-user/run-virtual-user.js +478 -0
- package/dist/wirings/virtual-user/virtual-user-agents.d.ts +38 -0
- package/dist/wirings/virtual-user/virtual-user-agents.js +24 -0
- package/dist/wirings/virtual-user/virtual-user-catalogue.d.ts +92 -0
- package/dist/wirings/virtual-user/virtual-user-catalogue.js +134 -0
- package/dist/wirings/virtual-user/virtual-user-derive.d.ts +26 -0
- package/dist/wirings/virtual-user/virtual-user-derive.js +137 -0
- package/dist/wirings/virtual-user/virtual-user-dispositions.d.ts +79 -0
- package/dist/wirings/virtual-user/virtual-user-dispositions.js +128 -0
- package/dist/wirings/virtual-user/virtual-user-intents.d.ts +78 -0
- package/dist/wirings/virtual-user/virtual-user-intents.js +142 -0
- package/dist/wirings/virtual-user/virtual-user-rng.d.ts +24 -0
- package/dist/wirings/virtual-user/virtual-user-rng.js +44 -0
- package/dist/wirings/virtual-user/virtual-user-target.d.ts +21 -0
- package/dist/wirings/virtual-user/virtual-user-target.js +34 -0
- package/dist/wirings/virtual-user/virtual-user.types.d.ts +199 -0
- package/dist/wirings/virtual-user/virtual-user.types.js +8 -0
- package/dist/wirings/workflow/dsl/workflow-dsl.types.d.ts +5 -5
- package/dist/wirings/workflow/dsl/workflow-runner.d.ts +0 -4
- package/dist/wirings/workflow/dsl/workflow-runner.js +0 -4
- package/dist/wirings/workflow/feature.d.ts +0 -19
- package/dist/wirings/workflow/feature.js +0 -19
- package/dist/wirings/workflow/graph/graph-node.d.ts +0 -98
- package/dist/wirings/workflow/graph/graph-node.js +0 -34
- package/dist/wirings/workflow/graph/graph-runner.js +6 -41
- package/dist/wirings/workflow/graph/wire-workflow-graph.d.ts +0 -4
- package/dist/wirings/workflow/graph/workflow-graph.types.d.ts +0 -58
- package/dist/wirings/workflow/graph/workflow-graph.types.js +0 -6
- package/dist/wirings/workflow/index.d.ts +5 -7
- package/dist/wirings/workflow/index.js +3 -17
- package/dist/wirings/workflow/pikku-scenario-service.d.ts +87 -5
- package/dist/wirings/workflow/pikku-scenario-service.js +204 -42
- package/dist/wirings/workflow/pikku-workflow-service.d.ts +7 -459
- package/dist/wirings/workflow/pikku-workflow-service.js +58 -551
- package/dist/wirings/workflow/run-timeline.d.ts +0 -47
- package/dist/wirings/workflow/run-timeline.js +0 -22
- package/dist/wirings/workflow/scenario-cookie-jar.d.ts +0 -23
- package/dist/wirings/workflow/scenario-cookie-jar.js +0 -16
- package/dist/wirings/workflow/scenario-poll.d.ts +0 -15
- package/dist/wirings/workflow/scenario-poll.js +0 -12
- package/dist/wirings/workflow/scenario-prose.d.ts +0 -28
- package/dist/wirings/workflow/scenario-prose.js +0 -18
- package/dist/wirings/workflow/scenario-step-guards.d.ts +0 -13
- package/dist/wirings/workflow/scenario-step-guards.js +1 -14
- package/dist/wirings/workflow/scenario-step.types.d.ts +72 -6
- package/dist/wirings/workflow/scenario-step.types.js +5 -1
- package/dist/wirings/workflow/scenario-surface.d.ts +16 -0
- package/dist/wirings/workflow/scenario-surface.js +56 -0
- package/dist/wirings/workflow/workflow-invocation-id.d.ts +0 -18
- package/dist/wirings/workflow/workflow-invocation-id.js +2 -22
- package/dist/wirings/workflow/workflow-queue-workers.d.ts +0 -20
- package/dist/wirings/workflow/workflow-queue-workers.js +0 -19
- package/dist/wirings/workflow/workflow.types.d.ts +0 -197
- package/knowledge/decisions/index.md +19 -0
- package/knowledge/decisions/internals/a-secret-that-fails-to-decrypt-fails-the-whole-read.md +49 -0
- package/knowledge/decisions/internals/actor-flow-conversations-seed-a-hidden-kickoff-message.md +23 -0
- package/knowledge/decisions/internals/actor-flow-drives-the-target-through-a-transport-seam.md +24 -0
- package/knowledge/decisions/internals/actor-flow-verdicts-are-llm-self-evaluations.md +25 -0
- package/knowledge/decisions/internals/addon-package-roots-resolve-by-walking-node-module-search-paths.md +26 -0
- package/knowledge/decisions/internals/addon-singleton-services-are-cached-per-namespace-not-per-package.md +33 -0
- package/knowledge/decisions/internals/addon-workflow-names-are-prefixed-with-the-consumer-namespace.md +28 -0
- package/knowledge/decisions/internals/ai-agent-agui-bridge-obeys-the-client-ordering-contract.md +29 -0
- package/knowledge/decisions/internals/ai-agent-audio-chunks-carry-the-format-the-provider-returned.md +20 -0
- package/knowledge/decisions/internals/ai-agent-credential-suspensions-hide-the-tool-result.md +26 -0
- package/knowledge/decisions/internals/ai-agent-delegate-and-supervise-hide-different-text.md +26 -0
- package/knowledge/decisions/internals/ai-agent-llm-tool-arguments-have-nulls-stripped.md +23 -0
- package/knowledge/decisions/internals/ai-agent-model-config-stays-a-single-resolution-seam.md +25 -0
- package/knowledge/decisions/internals/ai-agent-onerror-hooks-cannot-change-the-failure.md +22 -0
- package/knowledge/decisions/internals/ai-agent-runner-methods-must-keep-their-receiver.md +22 -0
- package/knowledge/decisions/internals/ai-agent-stream-persistence-is-best-effort.md +27 -0
- package/knowledge/decisions/internals/ai-agent-sub-agents-inherit-the-parent-context-block.md +26 -0
- package/knowledge/decisions/internals/ai-agent-tool-execute-failures-are-logged-unconditionally.md +25 -0
- package/knowledge/decisions/internals/ai-agent-voice-input-transcribes-audio-parts-in-place.md +22 -0
- package/knowledge/decisions/internals/ai-agent-working-memory-is-persisted-only-when-valid.md +25 -0
- package/knowledge/decisions/internals/channel-message-handlers-accept-three-config-shapes.md +30 -0
- package/knowledge/decisions/internals/channel-middleware-caches-only-statically-resolved-middleware.md +31 -0
- package/knowledge/decisions/internals/channel-state-is-per-socket-session-state-is-per-user.md +29 -0
- package/knowledge/decisions/internals/channel-user-id-is-persisted-after-onconnect-middleware-runs.md +28 -0
- package/knowledge/decisions/internals/cli-option-names-are-camelcase-in-state-and-kebab-on-the-command-line.md +27 -0
- package/knowledge/decisions/internals/cli-parse-errors-are-routed-by-message-prefix.md +28 -0
- package/knowledge/decisions/internals/cli-stdout-is-reserved-for-machine-readable-output.md +34 -0
- package/knowledge/decisions/internals/cli-unknown-long-options-warn-instead-of-failing.md +29 -0
- package/knowledge/decisions/internals/core-data-classification-brand-is-an-optional-property.md +34 -0
- package/knowledge/decisions/internals/core-function-runner-restores-the-wire-fields-it-overwrites.md +44 -0
- package/knowledge/decisions/internals/core-hot-reload-merges-generated-meta-never-replaces-it.md +39 -0
- package/knowledge/decisions/internals/core-hot-reload-owns-its-module-registry.md +42 -0
- package/knowledge/decisions/internals/core-middleware-order-is-scope-then-priority.md +39 -0
- package/knowledge/decisions/internals/core-schema-defaults-apply-on-every-transport.md +43 -0
- package/knowledge/decisions/internals/core-scopes-are-an-and-gate-separate-from-permissions.md +38 -0
- package/knowledge/decisions/internals/core-state-is-a-global-map-written-only-at-registration-time.md +44 -0
- package/knowledge/decisions/internals/email-meta-is-read-uncached-because-codegen-rewrites-it-mid-session.md +27 -0
- package/knowledge/decisions/internals/gateway-adapters-resolve-lazily-and-are-promise-cached.md +32 -0
- package/knowledge/decisions/internals/gateway-webhook-challenges-echo-bytes-not-json.md +28 -0
- package/knowledge/decisions/internals/gateway-wiring-is-a-meta-wiring-over-http-and-channels.md +31 -0
- package/knowledge/decisions/internals/generated-src-paths-in-pikku-meta-are-absolute.md +26 -0
- package/knowledge/decisions/internals/http-request-bodies-are-read-once-and-shared.md +32 -0
- package/knowledge/decisions/internals/http-route-groups-cascade-config-in-a-fixed-order.md +28 -0
- package/knowledge/decisions/internals/http-router-matches-normalized-paths-but-returns-registered-ones.md +30 -0
- package/knowledge/decisions/internals/http-runner-logs-through-a-trace-scoped-logger-functions-do-not.md +26 -0
- package/knowledge/decisions/internals/http-set-cookie-headers-are-appended-never-joined.md +28 -0
- package/knowledge/decisions/internals/http-sse-streams-flush-headers-only-after-middleware.md +32 -0
- package/knowledge/decisions/internals/http-wiring-without-metadata-is-skipped-not-fatal.md +26 -0
- package/knowledge/decisions/internals/in-a-scenario-a-4xx-is-data-not-an-exception.md +25 -0
- package/knowledge/decisions/internals/in-memory-workflow-history-aliases-the-live-step-object.md +26 -0
- package/knowledge/decisions/internals/index.md +113 -0
- package/knowledge/decisions/internals/istanbul-statement-counts-attach-to-the-start-line-only.md +25 -0
- package/knowledge/decisions/internals/local-trigger-and-gateway-services-assume-a-single-process.md +26 -0
- package/knowledge/decisions/internals/node-only-builtins-are-imported-dynamically.md +24 -0
- package/knowledge/decisions/internals/queue-group-concurrency-keeps-one-shared-queue-fair.md +28 -0
- package/knowledge/decisions/internals/queue-jobs-always-carry-an-explicit-attempts-count.md +27 -0
- package/knowledge/decisions/internals/remote-addons-dispatch-over-http-instead-of-local-meta.md +31 -0
- package/knowledge/decisions/internals/rpc-names-resolve-through-package-scope-before-root.md +32 -0
- package/knowledge/decisions/internals/scenario-agent-calls-sign-in-on-401-only.md +27 -0
- package/knowledge/decisions/internals/scenario-meta-lives-apart-from-app-meta-but-merges-when-read-off-disk.md +26 -0
- package/knowledge/decisions/internals/scenario-steps-return-drained-response-records.md +27 -0
- package/knowledge/decisions/internals/scope-roots-may-be-co-declared-by-an-addon-and-its-host-app.md +30 -0
- package/knowledge/decisions/internals/serverless-channel-disconnect-must-tolerate-a-missing-channel.md +28 -0
- package/knowledge/decisions/internals/the-dev-queue-copies-prod-timing-and-serialization-semantics.md +30 -0
- package/knowledge/decisions/internals/the-embedding-model-is-pinned-per-service-and-doc-query-embedding-is-split.md +29 -0
- package/knowledge/decisions/internals/the-in-memory-workflow-service-is-inline-only-and-single-process.md +27 -0
- package/knowledge/decisions/internals/the-kek-salt-is-scoped-to-the-key-version.md +40 -0
- package/knowledge/decisions/internals/the-schema-service-is-never-stubbed.md +26 -0
- package/knowledge/decisions/internals/trigger-declaration-is-split-from-trigger-source.md +33 -0
- package/knowledge/decisions/internals/typed-secret-service-caches-for-the-process-lifetime.md +26 -0
- package/knowledge/decisions/internals/webhook-delivery-history-records-every-attempt-best-effort.md +26 -0
- package/knowledge/decisions/internals/webhook-service-collaborators-are-constructor-args-not-locator-lookups.md +25 -0
- package/knowledge/decisions/internals/whether-a-run-is-inline-is-read-from-the-run-record.md +58 -0
- package/knowledge/decisions/internals/workflow-approval-expiry-is-decided-from-a-recorded-deadline.md +34 -0
- package/knowledge/decisions/internals/workflow-core-never-imports-a-browser-driver.md +42 -0
- package/knowledge/decisions/internals/workflow-dsl-meta-separates-runtime-expressions-from-literals.md +38 -0
- package/knowledge/decisions/internals/workflow-features-resolve-scenarios-by-object-identity.md +29 -0
- package/knowledge/decisions/internals/workflow-graph-inline-and-queued-runs-share-one-planner.md +42 -0
- package/knowledge/decisions/internals/workflow-graph-node-notes-are-excluded-from-the-graph-hash.md +25 -0
- package/knowledge/decisions/internals/workflow-inline-runs-report-their-run-id-before-they-can-fail.md +29 -0
- package/knowledge/decisions/internals/workflow-invocation-id-is-the-dedupe-key-not-step-id.md +43 -0
- package/knowledge/decisions/internals/workflow-queued-step-dispatch-requires-an-explicit-opt-in.md +29 -0
- package/knowledge/decisions/internals/workflow-queues-are-per-workflow-by-default.md +42 -0
- package/knowledge/decisions/internals/workflow-repeated-step-names-get-an-ordinal-suffix.md +33 -0
- package/knowledge/decisions/internals/workflow-replay-reads-its-steps-once-and-caches-only-the-immutable-half.md +32 -0
- package/knowledge/decisions/internals/workflow-retries-are-owned-by-the-workflow-not-the-queue.md +31 -0
- package/knowledge/decisions/internals/workflow-run-capabilities-are-extensions-not-subclasses.md +39 -0
- package/knowledge/decisions/internals/workflow-run-mirror-is-never-a-source-of-truth.md +29 -0
- package/knowledge/decisions/internals/workflow-run-polling-backs-off-to-the-callers-ceiling.md +33 -0
- package/knowledge/decisions/internals/workflow-run-timeline-is-a-pure-fold-over-durable-history.md +37 -0
- package/knowledge/decisions/internals/workflow-scenario-assertions-never-retry-and-record-one-step.md +50 -0
- package/knowledge/decisions/internals/workflow-scenario-hooks-are-a-scenario-only-affordance.md +43 -0
- package/knowledge/decisions/internals/workflow-scenario-prose-is-rendered-from-typed-calls-not-parsed-from-english.md +32 -0
- package/knowledge/decisions/internals/workflow-scenario-quarantine-reason-lives-in-code.md +18 -0
- package/knowledge/decisions/internals/workflow-scenario-step-targets-are-string-literals-for-the-inspector.md +34 -0
- package/knowledge/decisions/internals/workflow-step-compensation-runs-as-its-own-durable-step.md +26 -0
- package/knowledge/decisions/internals/workflow-step-dispatch-failure-is-transient-not-a-run-failure.md +33 -0
- package/knowledge/decisions/internals/workflow-step-lock-is-held-only-to-claim-the-step.md +27 -0
- package/knowledge/decisions/internals/workflow-step-rpc-name-is-provenance-only.md +34 -0
- package/knowledge/decisions/internals/workflow-suspend-and-approval-reasons-are-durable-step-identities.md +38 -0
- package/knowledge/decisions/internals/workflow-suspended-runs-keep-their-in-process-context.md +30 -0
- package/knowledge/decisions/security/a-dropped-audit-write-is-always-logged.md +26 -0
- package/knowledge/decisions/security/actor-flow-missing-approval-decisions-default-to-denied.md +22 -0
- package/knowledge/decisions/security/actor-sign-in-is-proven-by-set-cookie-not-a-non-empty-jar.md +27 -0
- package/knowledge/decisions/security/actor-sign-in-only-works-for-actor-flagged-users.md +27 -0
- package/knowledge/decisions/security/addon-auth-and-tags-only-tighten.md +43 -0
- package/knowledge/decisions/security/addon-config-gates-apply-only-at-the-namespaced-rpc-boundary.md +52 -0
- package/knowledge/decisions/security/addon-scopes-are-resolved-where-the-function-runs.md +46 -0
- package/knowledge/decisions/security/ai-agent-approval-forwarding-requires-a-symbol-brand.md +27 -0
- package/knowledge/decisions/security/ai-agent-credential-requests-are-symbol-branded.md +37 -0
- package/knowledge/decisions/security/ai-agent-gate-requires-a-session-only-when-auth-is-true.md +31 -0
- package/knowledge/decisions/security/ai-agent-ownership-failures-never-echo-the-resource.md +23 -0
- package/knowledge/decisions/security/ai-agent-resume-re-runs-the-authorization-gate.md +22 -0
- package/knowledge/decisions/security/ai-agent-sessionless-deployments-have-no-thread-ownership.md +39 -0
- package/knowledge/decisions/security/ai-agent-thread-ownership-composes-the-session-principal.md +30 -0
- package/knowledge/decisions/security/ai-agent-tool-filtering-reads-the-live-function-config.md +24 -0
- package/knowledge/decisions/security/an-empty-owners-constraint-matches-nothing.md +30 -0
- package/knowledge/decisions/security/an-exposed-ungated-function-is-a-codegen-warning.md +49 -0
- package/knowledge/decisions/security/console-addon-privileged-functions-gate-themselves.md +76 -0
- package/knowledge/decisions/security/core-safe-fetch-blocks-ssrf-by-host-literal-not-dns.md +39 -0
- package/knowledge/decisions/security/core-secrets-use-a-per-secret-dek-wrapped-by-a-kek.md +37 -0
- package/knowledge/decisions/security/gateway-handlers-run-through-the-function-runner-gate.md +31 -0
- package/knowledge/decisions/security/gateway-middleware-sessions-must-be-bridged-onto-the-wire.md +30 -0
- package/knowledge/decisions/security/global-permissions-and-function-permissions-are-independent-gates.md +40 -0
- package/knowledge/decisions/security/http-error-detail-is-withheld-from-clients-in-production.md +33 -0
- package/knowledge/decisions/security/http-request-bodies-are-bounded-before-they-are-buffered.md +46 -0
- package/knowledge/decisions/security/index.md +55 -0
- package/knowledge/decisions/security/mcp-internal-error-details-are-double-gated-on-production.md +27 -0
- package/knowledge/decisions/security/passphrases-are-stretched-key-material-is-expanded.md +40 -0
- package/knowledge/decisions/security/permission-auth-filtering-requires-live-permission-functions.md +31 -0
- package/knowledge/decisions/security/pikku-carries-actor-scopes-as-data-and-the-app-grants-them.md +26 -0
- package/knowledge/decisions/security/queue-job-identities-are-signed-at-enqueue.md +69 -0
- package/knowledge/decisions/security/queue-jobs-carry-the-producers-pikku-user-id.md +39 -0
- package/knowledge/decisions/security/remote-addon-tokens-are-client-credentials-not-mesh-trust.md +34 -0
- package/knowledge/decisions/security/scaffold-features-are-authenticated-unless-opted-out.md +49 -0
- package/knowledge/decisions/security/scenario-step-functions-are-never-externally-invocable.md +30 -0
- package/knowledge/decisions/security/scope-resolution-happens-at-the-session-boundary-and-sync-never-deletes.md +28 -0
- package/knowledge/decisions/security/self-authentication-is-declared-not-detected.md +34 -0
- package/knowledge/decisions/security/signed-content-urls-bind-the-request-path.md +37 -0
- package/knowledge/decisions/security/webhook-bodies-are-signed-before-they-are-enqueued.md +25 -0
- package/knowledge/decisions/security/workflow-actor-steps-always-use-the-real-transport.md +34 -0
- package/knowledge/decisions/security/workflow-approval-payloads-are-validated-on-replay-inside-the-workflow.md +40 -0
- package/knowledge/decisions/security/workflow-queued-steps-rehydrate-their-session-from-the-run-wire.md +32 -0
- package/knowledge/decisions/security/workflow-scenario-sessions-are-isolated-per-actor-and-per-scenario.md +32 -0
- package/knowledge/decisions/security/workflow-scenario-steps-are-never-network-invocable.md +31 -0
- package/knowledge/index.md +24 -0
- package/knowledge/questions/index.md +15 -0
- package/package.json +4 -1
- package/run-tests.sh +0 -0
- package/src/crypto-utils.test.ts +460 -19
- package/src/crypto-utils.ts +283 -55
- package/src/data-classification.ts +1 -7
- package/src/dev/hot-reload.test.ts +0 -4
- package/src/dev/hot-reload.ts +11 -30
- package/src/dev/module-runner.ts +7 -32
- package/src/dev/reload-meta.ts +9 -29
- package/src/errors/error-handler.ts +20 -35
- package/src/errors/error.test.ts +30 -1
- package/src/errors/errors.ts +73 -157
- package/src/function/abort-scope.test.ts +97 -0
- package/src/function/abort-scope.ts +80 -0
- package/src/function/function-runner.test.ts +0 -7
- package/src/function/function-runner.ts +62 -32
- package/src/function/functions.types.ts +56 -136
- package/src/function/list.types.test.ts +3 -25
- package/src/function/list.types.ts +12 -62
- package/src/handle-error.ts +0 -18
- package/src/index.ts +60 -1
- package/src/middleware/auth-apikey.test.ts +0 -1
- package/src/middleware/auth-apikey.ts +0 -17
- package/src/middleware/auth-bearer.test.ts +0 -3
- package/src/middleware/auth-bearer.ts +3 -40
- package/src/middleware/auth-cookie.test.ts +0 -6
- package/src/middleware/auth-cookie.ts +2 -26
- package/src/middleware/cors.test.ts +34 -0
- package/src/middleware/cors.ts +12 -33
- package/src/middleware/remote-auth.test.ts +24 -9
- package/src/middleware/remote-auth.ts +10 -2
- package/src/middleware/telemetry.ts +2 -31
- package/src/middleware-runner.test.ts +0 -2
- package/src/middleware-runner.ts +5 -74
- package/src/permissions.test.ts +30 -0
- package/src/permissions.ts +24 -74
- package/src/pikku-request.ts +0 -6
- package/src/pikku-state.ts +2 -30
- package/src/production-barrels-stay-lean.test.ts +110 -0
- package/src/remote.test.ts +172 -0
- package/src/remote.ts +17 -7
- package/src/schema.ts +5 -39
- package/src/scopes.ts +7 -48
- package/src/services/ai-agent-runner-service.ts +20 -0
- package/src/services/ai-embedding-service.ts +3 -25
- package/src/services/audit-service.ts +1 -2
- package/src/services/content-service.ts +1 -46
- package/src/services/credential-service.ts +3 -40
- package/src/services/credential-wire-service.test.ts +0 -2
- package/src/services/deployment-service.ts +3 -9
- package/src/services/gateway-service.ts +0 -15
- package/src/services/{http-scenario-actors-converse.test.ts → http-personas-converse.test.ts} +38 -9
- package/src/services/{http-scenario-actors.test.ts → http-personas.test.ts} +39 -22
- package/src/services/{http-scenario-actors.ts → http-personas.ts} +85 -45
- package/src/services/in-memory-queue-service.ts +1 -15
- package/src/services/in-memory-trigger-service.ts +1 -18
- package/src/services/in-memory-workflow-service.test.ts +0 -13
- package/src/services/in-memory-workflow-service.ts +4 -38
- package/src/services/index.ts +20 -15
- package/src/services/istanbul-coverage-service.ts +2 -8
- package/src/services/jwt-service.ts +1 -16
- package/src/services/local-content.test.ts +159 -27
- package/src/services/local-content.ts +55 -23
- package/src/services/local-gateway-service.ts +2 -17
- package/src/services/local-secrets.ts +0 -4
- package/src/services/logger-console.test.ts +0 -1
- package/src/services/logger-console.ts +3 -7
- package/src/services/logger.ts +2 -37
- package/src/services/meta-service.test.ts +1 -5
- package/src/services/meta-service.ts +41 -61
- package/src/services/{scenario-actors-service.ts → personas-service.ts} +48 -43
- package/src/services/pikku-user-id.ts +0 -4
- package/src/services/queue-webhook-service.ts +9 -41
- package/src/services/scheduler-service.ts +1 -50
- package/src/services/schema-service.ts +1 -24
- package/src/services/scope-service.ts +50 -34
- package/src/services/scoped-secret-service.ts +0 -4
- package/src/services/secret-host-binding.test.ts +138 -0
- package/src/services/secret-host-binding.ts +51 -0
- package/src/services/secret-service.ts +5 -33
- package/src/services/secretless.test.ts +54 -0
- package/src/services/secretless.ts +29 -0
- package/src/services/stub-tracker.ts +8 -18
- package/src/services/system-role-guard.test.ts +93 -0
- package/src/services/system-role-guard.ts +71 -0
- package/src/services/trigger-service.ts +0 -12
- package/src/services/typed-secret-service.ts +1 -7
- package/src/services/v8-coverage-service.ts +3 -6
- package/src/services/variables-service.ts +1 -8
- package/src/services/webhook-service.ts +19 -63
- package/src/services/workflow-service.ts +3 -20
- package/src/testing/service-tests.ts +0 -26
- package/src/time-utils.ts +1 -19
- package/src/types/core.types.ts +116 -229
- package/src/types/state.types.ts +7 -9
- package/src/utils/hmac.ts +4 -10
- package/src/utils/safe-fetch.ts +13 -54
- package/src/utils.test.ts +11 -2
- package/src/utils.ts +6 -15
- package/src/wirings/actor-flow/actor-flow.types.ts +1 -34
- package/src/wirings/actor-flow/index.ts +0 -9
- package/src/wirings/actor-flow/run-conversation.test.ts +11 -6
- package/src/wirings/actor-flow/run-conversation.ts +19 -12
- package/src/wirings/ai-agent/ai-agent-agui.test.ts +75 -10
- package/src/wirings/ai-agent/ai-agent-agui.ts +36 -17
- package/src/wirings/ai-agent/ai-agent-helpers.ts +20 -0
- package/src/wirings/ai-agent/ai-agent-interrupt.test.ts +842 -0
- package/src/wirings/ai-agent/ai-agent-interrupt.ts +399 -0
- package/src/wirings/ai-agent/ai-agent-memory.ts +0 -2
- package/src/wirings/ai-agent/ai-agent-model-config.ts +1 -9
- package/src/wirings/ai-agent/ai-agent-prepare.test.ts +202 -31
- package/src/wirings/ai-agent/ai-agent-prepare.ts +82 -138
- package/src/wirings/ai-agent/ai-agent-registry.test.ts +191 -6
- package/src/wirings/ai-agent/ai-agent-registry.ts +18 -1
- package/src/wirings/ai-agent/ai-agent-resume-authorization.test.ts +0 -2
- package/src/wirings/ai-agent/ai-agent-runner.test.ts +11 -9
- package/src/wirings/ai-agent/ai-agent-runner.ts +67 -33
- package/src/wirings/ai-agent/ai-agent-stream.test.ts +205 -103
- package/src/wirings/ai-agent/ai-agent-stream.ts +192 -75
- package/src/wirings/ai-agent/ai-agent-thread-ownership.test.ts +301 -0
- package/src/wirings/ai-agent/ai-agent.types.ts +85 -4
- package/src/wirings/ai-agent/index.ts +35 -3
- package/src/wirings/ai-agent/voice-input.test.ts +72 -7
- package/src/wirings/ai-agent/voice-input.ts +48 -3
- package/src/wirings/ai-agent/voice-output.test.ts +422 -0
- package/src/wirings/ai-agent/voice-output.ts +216 -56
- package/src/wirings/channel/channel-common.ts +15 -20
- package/src/wirings/channel/channel-handler.test.ts +50 -0
- package/src/wirings/channel/channel-handler.ts +32 -11
- package/src/wirings/channel/channel-host-rpc.test.ts +150 -0
- package/src/wirings/channel/channel-host-rpc.ts +69 -0
- package/src/wirings/channel/channel-middleware-runner.test.ts +0 -1
- package/src/wirings/channel/channel-middleware-runner.ts +0 -12
- package/src/wirings/channel/channel-rpc-registry.ts +116 -0
- package/src/wirings/channel/channel-rpc-responder.ts +117 -0
- package/src/wirings/channel/channel-rpc-service.ts +146 -0
- package/src/wirings/channel/channel-rpc-validators.ts +65 -0
- package/src/wirings/channel/channel-rpc.test.ts +820 -0
- package/src/wirings/channel/channel-rpc.ts +5 -0
- package/src/wirings/channel/channel-rpc.types.ts +150 -0
- package/src/wirings/channel/channel-runner.ts +0 -14
- package/src/wirings/channel/channel-store.ts +0 -10
- package/src/wirings/channel/channel.types.ts +19 -8
- package/src/wirings/channel/define-channel-routes.ts +0 -20
- package/src/wirings/channel/eventhub-service.ts +0 -18
- package/src/wirings/channel/index.ts +35 -0
- package/src/wirings/channel/local/local-channel-handler.ts +3 -1
- package/src/wirings/channel/local/local-channel-runner.test.ts +0 -10
- package/src/wirings/channel/local/local-channel-runner.ts +3 -1
- package/src/wirings/channel/local/local-eventhub-service.test.ts +0 -13
- package/src/wirings/channel/local/local-eventhub-service.ts +2 -37
- package/src/wirings/channel/log-channels.ts +0 -4
- package/src/wirings/channel/pikku-abstract-channel-handler.test.ts +83 -2
- package/src/wirings/channel/pikku-abstract-channel-handler.ts +7 -0
- package/src/wirings/channel/serverless/serverless-channel-runner.ts +2 -5
- package/src/wirings/cli/channel/cli-approval.test.ts +177 -0
- package/src/wirings/cli/channel/cli-approval.ts +135 -0
- package/src/wirings/cli/channel/cli-channel-runner.ts +4 -26
- package/src/wirings/cli/channel/cli-raw-channel-runner.test.ts +169 -0
- package/src/wirings/cli/channel/cli-raw-channel-runner.ts +59 -16
- package/src/wirings/cli/channel/cli-raw-client-runner.test.ts +480 -0
- package/src/wirings/cli/channel/cli-raw-client-runner.ts +155 -0
- package/src/wirings/cli/channel/index.ts +9 -0
- package/src/wirings/cli/cli-runner.test.ts +0 -1
- package/src/wirings/cli/cli-runner.ts +46 -88
- package/src/wirings/cli/cli.types.ts +14 -3
- package/src/wirings/cli/command-parser.test.ts +0 -4
- package/src/wirings/cli/command-parser.ts +11 -91
- package/src/wirings/cli/define-cli-commands.ts +1 -17
- package/src/wirings/credential/credential.types.ts +0 -12
- package/src/wirings/credential/{wire-credential.ts → define-credential.ts} +7 -7
- package/src/wirings/credential/index.ts +1 -1
- package/src/wirings/credential/validate-credential-definitions.ts +2 -4
- package/src/wirings/gateway/gateway-runner.test.ts +1 -21
- package/src/wirings/gateway/gateway-runner.ts +8 -110
- package/src/wirings/gateway/gateway.types.ts +7 -80
- package/src/wirings/http/http-routes.test.ts +0 -3
- package/src/wirings/http/http-routes.ts +0 -86
- package/src/wirings/http/http-runner.test.ts +0 -1
- package/src/wirings/http/http-runner.ts +8 -167
- package/src/wirings/http/http.types.ts +15 -62
- package/src/wirings/http/log-http-routes.ts +0 -4
- package/src/wirings/http/pikku-fetch-http-request.test.ts +2 -10
- package/src/wirings/http/pikku-fetch-http-request.ts +0 -58
- package/src/wirings/http/pikku-fetch-http-response.test.ts +1 -1
- package/src/wirings/http/pikku-fetch-http-response.ts +0 -3
- package/src/wirings/http/routers/path-to-regex.test.ts +4 -17
- package/src/wirings/http/routers/path-to-regex.ts +2 -13
- package/src/wirings/http/web-request.test.ts +33 -2
- package/src/wirings/http/web-request.ts +30 -17
- package/src/wirings/mcp/mcp-endpoint-registry.test.ts +0 -1
- package/src/wirings/mcp/mcp-runner.ts +2 -17
- package/src/wirings/mcp/mcp.types.ts +7 -42
- package/src/wirings/oauth2/oauth2.types.ts +0 -30
- package/src/wirings/persona/define-personas.ts +29 -0
- package/src/wirings/persona/index.ts +62 -0
- package/src/wirings/persona/persona-email.ts +87 -0
- package/src/wirings/persona/persona-environments.test.ts +183 -0
- package/src/wirings/persona/persona-environments.ts +138 -0
- package/src/wirings/persona/persona-mailbox.ts +156 -0
- package/src/wirings/persona/persona.test.ts +220 -0
- package/src/wirings/persona/persona.types.ts +131 -0
- package/src/wirings/persona/validate-personas.ts +133 -0
- package/src/wirings/queue/index.ts +13 -3
- package/src/wirings/queue/queue-identity.test.ts +453 -0
- package/src/wirings/queue/queue-identity.ts +173 -0
- package/src/wirings/queue/queue-runner.ts +12 -31
- package/src/wirings/queue/queue.types.ts +19 -89
- package/src/wirings/queue/register-queue-helper.ts +0 -14
- package/src/wirings/queue/signed-queue-service.ts +59 -0
- package/src/wirings/queue/validate-worker-config.ts +2 -28
- package/src/wirings/role/define-system-role.ts +33 -0
- package/src/wirings/role/index.ts +13 -0
- package/src/wirings/role/role.test.ts +104 -0
- package/src/wirings/role/role.types.ts +47 -0
- package/src/wirings/role/validate-role-definitions.ts +93 -0
- package/src/wirings/rpc/addon-auth-tags.test.ts +223 -0
- package/src/wirings/rpc/addon-runner.ts +0 -56
- package/src/wirings/rpc/addon-scopes.test.ts +225 -0
- package/src/wirings/rpc/remote-addon-auth.ts +1 -13
- package/src/wirings/rpc/rpc-runner.test.ts +186 -2
- package/src/wirings/rpc/rpc-runner.ts +145 -127
- package/src/wirings/rpc/rpc-types.ts +11 -6
- package/src/wirings/rpc/wire-addon.test.ts +43 -1
- package/src/wirings/rpc/wire-addon.ts +99 -0
- package/src/wirings/rpc/wire-remote-addon.ts +9 -29
- package/src/wirings/scheduler/log-schedulers.ts +0 -4
- package/src/wirings/scheduler/scheduler-runner.test.ts +1 -8
- package/src/wirings/scheduler/scheduler-runner.ts +0 -2
- package/src/wirings/scheduler/scheduler.types.ts +1 -14
- package/src/wirings/scope/{wire-scope.ts → define-scope.ts} +5 -6
- package/src/wirings/scope/index.ts +1 -1
- package/src/wirings/scope/scope.test.ts +1 -2
- package/src/wirings/scope/scope.types.ts +7 -9
- package/src/wirings/scope/validate-scope-definitions.ts +3 -21
- package/src/wirings/secret/index.ts +1 -1
- package/src/wirings/secret/secret.types.ts +19 -15
- package/src/wirings/secret/validate-secret-definitions.ts +2 -4
- package/src/wirings/trigger/trigger-runner.ts +1 -27
- package/src/wirings/trigger/trigger.types.ts +1 -82
- package/src/wirings/variable/index.ts +1 -1
- package/src/wirings/variable/validate-variable-definitions.ts +2 -4
- package/src/wirings/variable/variable.types.ts +1 -13
- package/src/wirings/virtual-user/index.ts +76 -0
- package/src/wirings/virtual-user/run-virtual-user.test.ts +765 -0
- package/src/wirings/virtual-user/run-virtual-user.ts +671 -0
- package/src/wirings/virtual-user/virtual-user-agents.test.ts +65 -0
- package/src/wirings/virtual-user/virtual-user-agents.ts +57 -0
- package/src/wirings/virtual-user/virtual-user-catalogue.test.ts +215 -0
- package/src/wirings/virtual-user/virtual-user-catalogue.ts +184 -0
- package/src/wirings/virtual-user/virtual-user-derive.test.ts +398 -0
- package/src/wirings/virtual-user/virtual-user-derive.ts +173 -0
- package/src/wirings/virtual-user/virtual-user-dispositions.test.ts +63 -0
- package/src/wirings/virtual-user/virtual-user-dispositions.ts +213 -0
- package/src/wirings/virtual-user/virtual-user-intents.test.ts +208 -0
- package/src/wirings/virtual-user/virtual-user-intents.ts +185 -0
- package/src/wirings/virtual-user/virtual-user-rng.test.ts +72 -0
- package/src/wirings/virtual-user/virtual-user-rng.ts +50 -0
- package/src/wirings/virtual-user/virtual-user-target.ts +47 -0
- package/src/wirings/virtual-user/virtual-user.types.ts +219 -0
- package/src/wirings/workflow/dsl/workflow-dsl.types.ts +5 -4
- package/src/wirings/workflow/dsl/workflow-runner.ts +0 -4
- package/src/wirings/workflow/feature.ts +0 -19
- package/src/wirings/workflow/graph/graph-node.ts +0 -136
- package/src/wirings/workflow/graph/graph-runner.test.ts +20 -19
- package/src/wirings/workflow/graph/graph-runner.ts +6 -41
- package/src/wirings/workflow/graph/wire-workflow-graph.ts +0 -4
- package/src/wirings/workflow/graph/workflow-graph.types.ts +0 -58
- package/src/wirings/workflow/index.ts +10 -44
- package/src/wirings/workflow/pikku-scenario-service.ts +235 -52
- package/src/wirings/workflow/pikku-workflow-service.test.ts +0 -39
- package/src/wirings/workflow/pikku-workflow-service.ts +77 -674
- package/src/wirings/workflow/run-timeline.test.ts +7 -19
- package/src/wirings/workflow/run-timeline.ts +0 -56
- package/src/wirings/workflow/scenario-cookie-jar.test.ts +0 -1
- package/src/wirings/workflow/scenario-cookie-jar.ts +0 -25
- package/src/wirings/workflow/scenario-expectations.test.ts +2 -7
- package/src/wirings/workflow/scenario-hooks.test.ts +2 -7
- package/src/wirings/workflow/scenario-poll.test.ts +0 -2
- package/src/wirings/workflow/scenario-poll.ts +0 -15
- package/src/wirings/workflow/scenario-prose.ts +0 -28
- package/src/wirings/workflow/scenario-service.test.ts +2 -9
- package/src/wirings/workflow/scenario-step-guards.ts +1 -14
- package/src/wirings/workflow/scenario-step.test.ts +159 -14
- package/src/wirings/workflow/scenario-step.types.ts +82 -6
- package/src/wirings/workflow/scenario-surface.test.ts +145 -0
- package/src/wirings/workflow/scenario-surface.ts +71 -0
- package/src/wirings/workflow/workflow-dispatch-durability.test.ts +14 -15
- package/src/wirings/workflow/workflow-dispatch-payload.test.ts +0 -4
- package/src/wirings/workflow/workflow-inline-authority.test.ts +169 -0
- package/src/wirings/workflow/workflow-invocation-id.test.ts +0 -2
- package/src/wirings/workflow/workflow-invocation-id.ts +2 -22
- package/src/wirings/workflow/workflow-mirror.test.ts +0 -7
- package/src/wirings/workflow/workflow-on-error.test.ts +0 -9
- package/src/wirings/workflow/workflow-queue-workers.ts +0 -21
- package/src/wirings/workflow/workflow-replay-snapshot.test.ts +8 -7
- package/src/wirings/workflow/workflow-retry-policy.test.ts +0 -5
- package/src/wirings/workflow/workflow-run-context.test.ts +5 -10
- package/src/wirings/workflow/workflow-run-polling.test.ts +0 -5
- package/src/wirings/workflow/workflow-step-ordinal.test.ts +19 -4
- package/src/wirings/workflow/workflow-step-session.test.ts +0 -7
- package/src/wirings/workflow/workflow.types.ts +0 -203
- package/tsconfig.tsbuildinfo +1 -1
- package/src/middleware/timeout.ts +0 -22
- package/src/pikku-response.ts +0 -5
- package/src/wirings/mcp/mcp-endpoint-registry.test.d.ts +0 -1
- package/src/wirings/workflow/dsl/index.ts +0 -36
- package/src/wirings/workflow/graph/index.ts +0 -15
|
@@ -3,6 +3,18 @@ import { pikkuAIMiddleware } from '../../types/core.types.js'
|
|
|
3
3
|
|
|
4
4
|
type VoiceOutputState = {
|
|
5
5
|
textBuffer?: string
|
|
6
|
+
/**
|
|
7
|
+
* The audio emitted so far, as a chain.
|
|
8
|
+
*
|
|
9
|
+
* Synthesis for every finished sentence starts immediately, so the second
|
|
10
|
+
* sentence is being generated while the first is still being spoken. That
|
|
11
|
+
* also means the second can finish first — a short sentence after a long one
|
|
12
|
+
* routinely does. Chaining the *emissions* keeps the client receiving audio
|
|
13
|
+
* in the order it was written while leaving the generation overlapped.
|
|
14
|
+
*/
|
|
15
|
+
tail?: Promise<void>
|
|
16
|
+
/** Whether this reply has already said it cannot be spoken aloud. */
|
|
17
|
+
reportedUnspeakable?: boolean
|
|
6
18
|
}
|
|
7
19
|
|
|
8
20
|
function bufferToBase64(data: Uint8Array): string {
|
|
@@ -19,6 +31,102 @@ function isSentenceBoundary(text: string): boolean {
|
|
|
19
31
|
return SENTENCE_BOUNDARY.test(text)
|
|
20
32
|
}
|
|
21
33
|
|
|
34
|
+
/**
|
|
35
|
+
* Scripts a speech model can pronounce, as named by {@link voiceOutput}'s
|
|
36
|
+
* `speakableScripts`.
|
|
37
|
+
*
|
|
38
|
+
* Script rather than language because script is what is decidable from the text
|
|
39
|
+
* alone. Spanish, French and English are one test, not three — and the models
|
|
40
|
+
* that handle one Latin-script language generally handle the others. What a
|
|
41
|
+
* model cannot do is invent a phoneme set it was never given.
|
|
42
|
+
*/
|
|
43
|
+
const SCRIPT_RANGES: Record<string, RegExp> = {
|
|
44
|
+
latin: /[A-ɏ]/,
|
|
45
|
+
devanagari: /[ऀ-ॿ]/,
|
|
46
|
+
han: /[㐀-鿿]/,
|
|
47
|
+
kana: /[-ヿ]/,
|
|
48
|
+
arabic: /[-ۿݐ-ݿ]/,
|
|
49
|
+
cyrillic: /[Ѐ-ӿ]/,
|
|
50
|
+
hangul: /[가-ᄀ-ᇿ]/,
|
|
51
|
+
hebrew: /[-]/,
|
|
52
|
+
greek: /[Ͱ-Ͽ]/,
|
|
53
|
+
thai: /[-]/,
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* The scripts a model can pronounce, and optionally the voice each one needs.
|
|
58
|
+
*
|
|
59
|
+
* The array form is for a model whose voice is the same whatever the script.
|
|
60
|
+
* The record form exists because that is not the common case: Kokoro speaks
|
|
61
|
+
* Mandarin, Japanese and Hindi well, but only through a voice belonging to that
|
|
62
|
+
* language. Asked for Chinese in its default American-English voice it spells
|
|
63
|
+
* the characters out — 9.9 seconds of "Chinese letter, Chinese letter" for a
|
|
64
|
+
* sentence the right voice says in 3.5. Same model, same text, same price.
|
|
65
|
+
*/
|
|
66
|
+
export type SpeakableScripts = string[] | Record<string, string>
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Which script decides the voice when a sentence contains several.
|
|
70
|
+
*
|
|
71
|
+
* The ordering is by how much a script narrows down the language, not by
|
|
72
|
+
* preference. Kana appears only in Japanese, so a sentence holding any settles
|
|
73
|
+
* it — while Han is written in both Chinese and Japanese and so cannot, which
|
|
74
|
+
* matters because ordinary Japanese is mostly kanji. Latin comes last for the
|
|
75
|
+
* same reason from the other end: it turns up inside sentences in every other
|
|
76
|
+
* script, and it is the one whose voice is least often the one that counts. A
|
|
77
|
+
* Chinese voice reading an English word is accented; an English voice reading
|
|
78
|
+
* Chinese spells it out.
|
|
79
|
+
*
|
|
80
|
+
* Everything else — Han included — sits between the two in config order.
|
|
81
|
+
*/
|
|
82
|
+
const SCRIPT_PRECEDENCE: Record<string, number> = { kana: -1, latin: 1 }
|
|
83
|
+
|
|
84
|
+
const scriptRank = (name: string): number => SCRIPT_PRECEDENCE[name] ?? 0
|
|
85
|
+
|
|
86
|
+
const scriptNames = (speakable: SpeakableScripts): string[] =>
|
|
87
|
+
Array.isArray(speakable) ? speakable : Object.keys(speakable)
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* The scripts present in `text` that the model was not said to handle.
|
|
91
|
+
*
|
|
92
|
+
* Silence would be the wrong answer here and so would a best effort. Handed
|
|
93
|
+
* Arabic — which it has no voice for at all — Kokoro does not fail and does not
|
|
94
|
+
* stay quiet: it reads out the *letter names*, twenty-four seconds of "Arabic
|
|
95
|
+
* meem, Arabic ra" for a one-line sentence, which is worse than either. Naming
|
|
96
|
+
* the gap lets the caller say so instead.
|
|
97
|
+
*/
|
|
98
|
+
export const unspeakableScripts = (
|
|
99
|
+
text: string,
|
|
100
|
+
speakable: SpeakableScripts
|
|
101
|
+
): string[] => {
|
|
102
|
+
const allowed = new Set(scriptNames(speakable))
|
|
103
|
+
return Object.entries(SCRIPT_RANGES)
|
|
104
|
+
.filter(([name, range]) => !allowed.has(name) && range.test(text))
|
|
105
|
+
.map(([name]) => name)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* The voice to speak `text` in, or `fallback` if no mapped script appears.
|
|
110
|
+
*
|
|
111
|
+
* A sentence is regularly in more than one script, so which one decides is
|
|
112
|
+
* settled by {@link SCRIPT_PRECEDENCE} rather than by the order the config
|
|
113
|
+
* happens to be written in.
|
|
114
|
+
*/
|
|
115
|
+
export const voiceForText = (
|
|
116
|
+
text: string,
|
|
117
|
+
speakable: SpeakableScripts,
|
|
118
|
+
fallback?: string
|
|
119
|
+
): string | undefined => {
|
|
120
|
+
if (Array.isArray(speakable)) return fallback
|
|
121
|
+
const mapped = Object.entries(speakable).sort(
|
|
122
|
+
([a], [b]) => scriptRank(a) - scriptRank(b)
|
|
123
|
+
)
|
|
124
|
+
for (const [name, voice] of mapped) {
|
|
125
|
+
if (SCRIPT_RANGES[name]?.test(text)) return voice
|
|
126
|
+
}
|
|
127
|
+
return fallback
|
|
128
|
+
}
|
|
129
|
+
|
|
22
130
|
async function synthesizeAudio(
|
|
23
131
|
aiAgentRunner: AIAgentRunnerService,
|
|
24
132
|
input: {
|
|
@@ -29,6 +137,7 @@ async function synthesizeAudio(
|
|
|
29
137
|
instructions?: string
|
|
30
138
|
speed?: number
|
|
31
139
|
language?: string
|
|
140
|
+
abortSignal?: AbortSignal
|
|
32
141
|
}
|
|
33
142
|
): Promise<{ bytes: Uint8Array; format: string }> {
|
|
34
143
|
const result = await aiAgentRunner.generateSpeech?.({
|
|
@@ -39,14 +148,13 @@ async function synthesizeAudio(
|
|
|
39
148
|
instructions: input.instructions,
|
|
40
149
|
speed: input.speed,
|
|
41
150
|
language: input.language,
|
|
151
|
+
abortSignal: input.abortSignal,
|
|
42
152
|
})
|
|
43
153
|
if (!result) {
|
|
44
154
|
throw new Error(
|
|
45
155
|
'voiceOutput requires an aiAgentRunner with generateSpeech support'
|
|
46
156
|
)
|
|
47
157
|
}
|
|
48
|
-
// Label chunks with the format the provider actually returned (config.format
|
|
49
|
-
// is only a request), falling back to the requested format then pcm16.
|
|
50
158
|
return {
|
|
51
159
|
bytes: result.audio.uint8Array,
|
|
52
160
|
format: result.audio.format || input.format || 'pcm16',
|
|
@@ -60,75 +168,127 @@ export const voiceOutput = (config?: {
|
|
|
60
168
|
instructions?: string
|
|
61
169
|
speed?: number
|
|
62
170
|
language?: string
|
|
171
|
+
/**
|
|
172
|
+
* Scripts the configured model can actually pronounce, and the voice each
|
|
173
|
+
* needs — see {@link SpeakableScripts}. Anything written in another script is
|
|
174
|
+
* left unspoken and reported once, as a `voice-unsupported` data event,
|
|
175
|
+
* rather than sent to a model that will mangle it.
|
|
176
|
+
*
|
|
177
|
+
* Omitted means no check: every model is trusted with every script, which is
|
|
178
|
+
* the right default for a provider that genuinely is multilingual.
|
|
179
|
+
*/
|
|
180
|
+
speakableScripts?: SpeakableScripts
|
|
63
181
|
}) =>
|
|
64
182
|
pikkuAIMiddleware<VoiceOutputState>({
|
|
65
|
-
modifyOutputStream: async (services, { event, state }) => {
|
|
66
|
-
const aiAgentRunner =
|
|
183
|
+
modifyOutputStream: async (services, { event, state, emit, signal }) => {
|
|
184
|
+
const { aiAgentRunner, logger } = services as {
|
|
67
185
|
aiAgentRunner?: AIAgentRunnerService
|
|
68
|
-
|
|
186
|
+
logger?: { error: (message: string) => void }
|
|
187
|
+
}
|
|
69
188
|
if (!aiAgentRunner?.generateSpeech) return event
|
|
70
189
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
190
|
+
/**
|
|
191
|
+
* Queue a finished sentence for speech.
|
|
192
|
+
*
|
|
193
|
+
* Nothing here is awaited by the caller. Synthesis is kicked off at once
|
|
194
|
+
* and its emission is chained behind the previous sentence, which is
|
|
195
|
+
* what keeps the text flowing at the speed the model writes it instead
|
|
196
|
+
* of the speed the speech provider answers.
|
|
197
|
+
*/
|
|
198
|
+
const speak = (text: string) => {
|
|
199
|
+
if (!config?.model) {
|
|
200
|
+
throw new Error(
|
|
201
|
+
'voiceOutput requires a speech model (e.g. openai/tts-1)'
|
|
202
|
+
)
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Checked per sentence but announced once per reply: a bilingual answer
|
|
206
|
+
// should still speak the half it can, and repeating the notice for
|
|
207
|
+
// every sentence of a long one would bury the reply itself.
|
|
208
|
+
//
|
|
209
|
+
// Per sentence is also what makes the voice right. A reply that answers
|
|
210
|
+
// in English and then quotes a Chinese title is two sentences in two
|
|
211
|
+
// scripts, and each is synthesized in the voice its own script needs.
|
|
212
|
+
let voice = config.voice
|
|
213
|
+
if (config.speakableScripts) {
|
|
214
|
+
const unspeakable = unspeakableScripts(text, config.speakableScripts)
|
|
215
|
+
voice = voiceForText(text, config.speakableScripts, config.voice)
|
|
216
|
+
if (unspeakable.length > 0) {
|
|
217
|
+
if (!state.reportedUnspeakable) {
|
|
218
|
+
state.reportedUnspeakable = true
|
|
219
|
+
state.tail = (state.tail ?? Promise.resolve()).then(() =>
|
|
220
|
+
emit({
|
|
221
|
+
type: 'data',
|
|
222
|
+
name: 'voice-unsupported',
|
|
223
|
+
data: { model: config.model, scripts: unspeakable },
|
|
224
|
+
})
|
|
225
|
+
)
|
|
226
|
+
}
|
|
227
|
+
return
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
// Settled into a value rather than left as a rejectable promise: it
|
|
231
|
+
// sits unobserved until the chain reaches it, and an unhandled
|
|
232
|
+
// rejection in that window would take the process down.
|
|
233
|
+
const synthesis = synthesizeAudio(aiAgentRunner, {
|
|
234
|
+
model: config.model,
|
|
235
|
+
text,
|
|
236
|
+
voice,
|
|
237
|
+
format: config?.format,
|
|
238
|
+
instructions: config?.instructions,
|
|
239
|
+
speed: config?.speed,
|
|
240
|
+
language: config?.language,
|
|
241
|
+
// Barge-in should stop the bill, not just the playback. Without this
|
|
242
|
+
// every sentence already in flight is synthesized in full and paid
|
|
243
|
+
// for after the user has stopped listening.
|
|
244
|
+
abortSignal: signal,
|
|
245
|
+
}).then(
|
|
246
|
+
(audio) => ({ ok: true as const, audio }),
|
|
247
|
+
(error: unknown) => ({ ok: false as const, error })
|
|
248
|
+
)
|
|
249
|
+
|
|
250
|
+
state.tail = (state.tail ?? Promise.resolve()).then(async () => {
|
|
251
|
+
const result = await synthesis
|
|
252
|
+
if (!result.ok) {
|
|
253
|
+
// One sentence going unspoken is better than the rest of the reply
|
|
254
|
+
// never arriving, so the chain carries on.
|
|
255
|
+
logger?.error(
|
|
256
|
+
`voiceOutput could not synthesize a sentence: ${String(result.error)}`
|
|
78
257
|
)
|
|
258
|
+
return
|
|
79
259
|
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
format: config?.format,
|
|
85
|
-
instructions: config?.instructions,
|
|
86
|
-
speed: config?.speed,
|
|
87
|
-
language: config?.language,
|
|
260
|
+
await emit({
|
|
261
|
+
type: 'audio-delta',
|
|
262
|
+
data: bufferToBase64(result.audio.bytes),
|
|
263
|
+
format: result.audio.format,
|
|
88
264
|
})
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
]
|
|
265
|
+
})
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
if (event.type === 'done') {
|
|
269
|
+
const remaining = state.textBuffer ?? ''
|
|
270
|
+
state.textBuffer = ''
|
|
271
|
+
if (remaining.trim()) {
|
|
272
|
+
speak(remaining)
|
|
98
273
|
}
|
|
274
|
+
// The only place this hook waits. The reply is already over, so the
|
|
275
|
+
// wait costs the reader nothing, and it is what makes `audio-done`
|
|
276
|
+
// mean what it says rather than racing the last chunk.
|
|
277
|
+
await state.tail
|
|
278
|
+
state.tail = undefined
|
|
99
279
|
return [{ type: 'audio-done' as const }, event]
|
|
100
280
|
}
|
|
101
281
|
|
|
102
282
|
if (event.type !== 'text-delta') return event
|
|
103
283
|
|
|
104
284
|
state.textBuffer = `${state.textBuffer ?? ''}${event.text}`
|
|
105
|
-
if (
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
if (!config?.model) {
|
|
111
|
-
throw new Error(
|
|
112
|
-
'voiceOutput requires a speech model (e.g. openai/tts-1)'
|
|
113
|
-
)
|
|
285
|
+
if (isSentenceBoundary(state.textBuffer)) {
|
|
286
|
+
const text = state.textBuffer
|
|
287
|
+
state.textBuffer = ''
|
|
288
|
+
speak(text)
|
|
114
289
|
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
voice: config?.voice,
|
|
119
|
-
format: config?.format,
|
|
120
|
-
instructions: config?.instructions,
|
|
121
|
-
speed: config?.speed,
|
|
122
|
-
language: config?.language,
|
|
123
|
-
})
|
|
124
|
-
|
|
125
|
-
return [
|
|
126
|
-
event,
|
|
127
|
-
{
|
|
128
|
-
type: 'audio-delta' as const,
|
|
129
|
-
data: bufferToBase64(audio.bytes),
|
|
130
|
-
format: audio.format,
|
|
131
|
-
},
|
|
132
|
-
]
|
|
290
|
+
|
|
291
|
+
// Returned without waiting for any audio — the entire point.
|
|
292
|
+
return event
|
|
133
293
|
},
|
|
134
294
|
})
|
|
@@ -8,30 +8,15 @@ import type {
|
|
|
8
8
|
import type { CoreChannel, ChannelMessageMeta } from './channel.types.js'
|
|
9
9
|
import { combineMiddleware, runMiddleware } from '../../middleware-runner.js'
|
|
10
10
|
import { runPikkuFunc } from '../../function/function-runner.js'
|
|
11
|
+
import {
|
|
12
|
+
type PikkuSessionService,
|
|
13
|
+
createMiddlewareSessionWireProps,
|
|
14
|
+
} from '../../services/user-session-service.js'
|
|
11
15
|
import {
|
|
12
16
|
combineChannelMiddleware,
|
|
13
17
|
wrapChannelWithMiddleware,
|
|
14
18
|
} from './channel-middleware-runner.js'
|
|
15
19
|
|
|
16
|
-
/**
|
|
17
|
-
* Runs a channel lifecycle function (onConnect or onDisconnect) with proper middleware handling.
|
|
18
|
-
*
|
|
19
|
-
* This function:
|
|
20
|
-
* 1. Extracts inline middleware from the lifecycle config if present
|
|
21
|
-
* 2. Combines metadata middleware with inline middleware using combineMiddleware()
|
|
22
|
-
* 3. Wraps the channel with channel middleware if present
|
|
23
|
-
* 4. Runs the lifecycle function with middleware support
|
|
24
|
-
*
|
|
25
|
-
* @param channelConfig - The channel configuration
|
|
26
|
-
* @param meta - Metadata for the lifecycle function (connect or disconnect)
|
|
27
|
-
* @param lifecycleConfig - The onConnect or onDisconnect config (can be function or object with middleware)
|
|
28
|
-
* @param lifecycleType - Type of lifecycle for cache key ('connect' or 'disconnect')
|
|
29
|
-
* @param services - All services (singleton + session)
|
|
30
|
-
* @param channel - The channel instance
|
|
31
|
-
* @param data - Optional data to pass to the lifecycle function (for onConnect)
|
|
32
|
-
* @param channelMiddlewareMeta - Optional channel middleware metadata for wrapping channel.send()
|
|
33
|
-
* @returns Promise<unknown> - Result from the lifecycle function (if any)
|
|
34
|
-
*/
|
|
35
20
|
export const runChannelLifecycleWithMiddleware = async ({
|
|
36
21
|
channelConfig,
|
|
37
22
|
meta,
|
|
@@ -41,6 +26,7 @@ export const runChannelLifecycleWithMiddleware = async ({
|
|
|
41
26
|
channel,
|
|
42
27
|
data,
|
|
43
28
|
channelMiddlewareMeta,
|
|
29
|
+
userSession,
|
|
44
30
|
}: {
|
|
45
31
|
channelConfig: CoreChannel<unknown, any>
|
|
46
32
|
meta: ChannelMessageMeta
|
|
@@ -50,6 +36,11 @@ export const runChannelLifecycleWithMiddleware = async ({
|
|
|
50
36
|
channel: any
|
|
51
37
|
data?: unknown
|
|
52
38
|
channelMiddlewareMeta?: MiddlewareMetadata[]
|
|
39
|
+
/**
|
|
40
|
+
* Established while the socket was upgrading, so `onConnect` can tell who
|
|
41
|
+
* just connected.
|
|
42
|
+
*/
|
|
43
|
+
userSession?: PikkuSessionService<any>
|
|
53
44
|
}): Promise<unknown> => {
|
|
54
45
|
const lifecycleMiddleware =
|
|
55
46
|
typeof lifecycleConfig === 'object' && 'middleware' in lifecycleConfig
|
|
@@ -74,7 +65,10 @@ export const runChannelLifecycleWithMiddleware = async ({
|
|
|
74
65
|
}
|
|
75
66
|
)
|
|
76
67
|
|
|
77
|
-
let wire: PikkuRawWire = {
|
|
68
|
+
let wire: PikkuRawWire = {
|
|
69
|
+
channel,
|
|
70
|
+
...(userSession ? createMiddlewareSessionWireProps(userSession) : {}),
|
|
71
|
+
}
|
|
78
72
|
if (allChannelMiddleware.length > 0) {
|
|
79
73
|
wire = wrapChannelWithMiddleware(wire, services, allChannelMiddleware)
|
|
80
74
|
}
|
|
@@ -84,6 +78,7 @@ export const runChannelLifecycleWithMiddleware = async ({
|
|
|
84
78
|
singletonServices: services,
|
|
85
79
|
data: () => data as any,
|
|
86
80
|
wire,
|
|
81
|
+
sessionService: userSession,
|
|
87
82
|
tags: meta.tags ?? [],
|
|
88
83
|
packageName: meta.packageName ?? null,
|
|
89
84
|
})
|
|
@@ -4,6 +4,8 @@ import assert from 'node:assert/strict'
|
|
|
4
4
|
import { addFunction } from '../../function/function-runner.js'
|
|
5
5
|
import { pikkuState, resetPikkuState } from '../../pikku-state.js'
|
|
6
6
|
import { processMessageHandlers } from './channel-handler.js'
|
|
7
|
+
import { getChannelHostRPC, releaseChannelHostRPC } from './channel-host-rpc.js'
|
|
8
|
+
import { CHANNEL_RPC_RESPONSE } from './channel-rpc.js'
|
|
7
9
|
|
|
8
10
|
const createLogger = () => {
|
|
9
11
|
const errors: string[] = []
|
|
@@ -240,6 +242,54 @@ describe('processMessageHandlers', () => {
|
|
|
240
242
|
assert.match(errors[1] || '', /Channel config name: chat/)
|
|
241
243
|
})
|
|
242
244
|
|
|
245
|
+
test('takes a reverse-RPC answer off the socket before routing', async () => {
|
|
246
|
+
const { errors, logger } = createLogger()
|
|
247
|
+
const { sent, handler } = createChannelHandler()
|
|
248
|
+
|
|
249
|
+
pikkuState(null, 'channel', 'meta').chat = {
|
|
250
|
+
name: 'chat',
|
|
251
|
+
route: '/chat',
|
|
252
|
+
input: null,
|
|
253
|
+
connect: null,
|
|
254
|
+
disconnect: null,
|
|
255
|
+
message: { pikkuFuncId: 'defaultFunc', packageName: null },
|
|
256
|
+
messageWirings: {},
|
|
257
|
+
} as never
|
|
258
|
+
registerFunction('defaultFunc', async () => ({ ok: true }))
|
|
259
|
+
|
|
260
|
+
const { onMessage } = processMessageHandlers(
|
|
261
|
+
{ logger } as never,
|
|
262
|
+
{
|
|
263
|
+
name: 'chat',
|
|
264
|
+
route: '/chat',
|
|
265
|
+
auth: false,
|
|
266
|
+
onMessage: { func: async () => ({ ok: true }) } as never,
|
|
267
|
+
} as never,
|
|
268
|
+
handler as never
|
|
269
|
+
)
|
|
270
|
+
|
|
271
|
+
const transport = getChannelHostRPC(handler.getChannel() as never)
|
|
272
|
+
const call = transport.invoke('localCheckout', {})
|
|
273
|
+
const request = sent[0] as { id: string }
|
|
274
|
+
|
|
275
|
+
const result = await onMessage(
|
|
276
|
+
JSON.stringify({
|
|
277
|
+
action: CHANNEL_RPC_RESPONSE,
|
|
278
|
+
id: request.id,
|
|
279
|
+
ok: true,
|
|
280
|
+
result: { sha: 'deadbeef' },
|
|
281
|
+
})
|
|
282
|
+
)
|
|
283
|
+
|
|
284
|
+
assert.deepEqual(await call, { sha: 'deadbeef' })
|
|
285
|
+
// The channel declares no route for replies and its default handler is
|
|
286
|
+
// never reached — the answer belongs to the waiting caller, not the app.
|
|
287
|
+
assert.equal(result, undefined)
|
|
288
|
+
assert.deepEqual(errors, [])
|
|
289
|
+
|
|
290
|
+
await releaseChannelHostRPC(handler.getChannel().channelId)
|
|
291
|
+
})
|
|
292
|
+
|
|
243
293
|
test('blocks binary messages when a session is required and missing', async () => {
|
|
244
294
|
const { errors, logger } = createLogger()
|
|
245
295
|
const { handler } = createChannelHandler()
|
|
@@ -11,6 +11,8 @@ import type {
|
|
|
11
11
|
} from './channel.types.js'
|
|
12
12
|
import { pikkuState } from '../../pikku-state.js'
|
|
13
13
|
import { runPikkuFunc } from '../../function/function-runner.js'
|
|
14
|
+
import { isChannelRPCPending, isChannelRPCResponse } from './channel-rpc.js'
|
|
15
|
+
import { handleChannelRPCResponse } from './channel-host-rpc.js'
|
|
14
16
|
import type { SessionService } from '../../services/user-session-service.js'
|
|
15
17
|
import { createMiddlewareSessionWireProps } from '../../services/user-session-service.js'
|
|
16
18
|
const getChannelMeta = (channelName: string) => {
|
|
@@ -159,14 +161,26 @@ export const processMessageHandlers = (
|
|
|
159
161
|
let result: unknown
|
|
160
162
|
let processed = false
|
|
161
163
|
|
|
162
|
-
|
|
163
|
-
if (typeof rawData === 'string'
|
|
164
|
-
let messageData: any
|
|
164
|
+
let frame: any
|
|
165
|
+
if (typeof rawData === 'string') {
|
|
165
166
|
try {
|
|
166
|
-
|
|
167
|
+
frame = JSON.parse(rawData)
|
|
167
168
|
} catch {
|
|
168
169
|
// Most likely a json error.. ignore
|
|
169
170
|
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Taken ahead of routing so every channel gets reverse RPC without
|
|
174
|
+
// declaring a route for replies, and so an answer to a call someone is
|
|
175
|
+
// still awaiting can never be mistaken for a new message.
|
|
176
|
+
if (isChannelRPCResponse(frame) || isChannelRPCPending(frame)) {
|
|
177
|
+
handleChannelRPCResponse(channelHandler.getChannel().channelId, frame)
|
|
178
|
+
return
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Route-specific handling
|
|
182
|
+
if (typeof rawData === 'string' && channelConfig.onMessageWiring) {
|
|
183
|
+
const messageData: any = frame
|
|
170
184
|
|
|
171
185
|
if (messageData) {
|
|
172
186
|
const entries = Object.entries(channelConfig.onMessageWiring)
|
|
@@ -176,14 +190,21 @@ export const processMessageHandlers = (
|
|
|
176
190
|
const { [routingProperty]: _, ...data } = messageData
|
|
177
191
|
|
|
178
192
|
processed = true
|
|
193
|
+
const routeResult = await processMessage(
|
|
194
|
+
data as any,
|
|
195
|
+
routes[routerValue],
|
|
196
|
+
routingProperty,
|
|
197
|
+
routerValue
|
|
198
|
+
)
|
|
199
|
+
// The routing key is echoed so the client can match the reply.
|
|
200
|
+
// Assigning it to a primitive throws under ESM strict mode, so a
|
|
201
|
+
// primitive result is wrapped rather than mutated.
|
|
179
202
|
result =
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
)) || {}
|
|
186
|
-
;(result as any)[routingProperty] = routerValue
|
|
203
|
+
routeResult !== null && typeof routeResult === 'object'
|
|
204
|
+
? Object.assign(routeResult, { [routingProperty]: routerValue })
|
|
205
|
+
: routeResult === undefined
|
|
206
|
+
? { [routingProperty]: routerValue }
|
|
207
|
+
: { [routingProperty]: routerValue, result: routeResult }
|
|
187
208
|
break
|
|
188
209
|
}
|
|
189
210
|
}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { describe, test } from 'node:test'
|
|
2
|
+
import * as assert from 'node:assert/strict'
|
|
3
|
+
|
|
4
|
+
import { CHANNEL_RPC_RESPONSE } from './channel-rpc.js'
|
|
5
|
+
import {
|
|
6
|
+
getChannelHostRPC,
|
|
7
|
+
handleChannelRPCResponse,
|
|
8
|
+
releaseChannelHostRPC,
|
|
9
|
+
} from './channel-host-rpc.js'
|
|
10
|
+
import type { PikkuChannel } from './channel.types.js'
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* A channel stripped to what the host RPC registry touches: an id to key the
|
|
14
|
+
* transport by, and a send it writes requests to.
|
|
15
|
+
*/
|
|
16
|
+
const fakeChannel = (channelId: string) => {
|
|
17
|
+
const sent: unknown[] = []
|
|
18
|
+
const channel = {
|
|
19
|
+
channelId,
|
|
20
|
+
send: async (data: unknown) => {
|
|
21
|
+
sent.push(data)
|
|
22
|
+
},
|
|
23
|
+
} as unknown as PikkuChannel<unknown, any>
|
|
24
|
+
return { channel, sent }
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
describe('channel host RPC', () => {
|
|
28
|
+
test('one transport per connection, reused across commands', async () => {
|
|
29
|
+
const { channel } = fakeChannel('reuse')
|
|
30
|
+
try {
|
|
31
|
+
assert.equal(getChannelHostRPC(channel), getChannelHostRPC(channel))
|
|
32
|
+
} finally {
|
|
33
|
+
await releaseChannelHostRPC('reuse')
|
|
34
|
+
}
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
test('separate connections get separate transports', async () => {
|
|
38
|
+
const a = fakeChannel('a')
|
|
39
|
+
const b = fakeChannel('b')
|
|
40
|
+
try {
|
|
41
|
+
assert.notEqual(
|
|
42
|
+
getChannelHostRPC(a.channel),
|
|
43
|
+
getChannelHostRPC(b.channel)
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
// Requests must land on their own socket, not the other connection's.
|
|
47
|
+
// Nothing answers this one, so it is rejected by the release below.
|
|
48
|
+
getChannelHostRPC(a.channel)
|
|
49
|
+
.invoke('localCheckout', {})
|
|
50
|
+
.catch(() => {})
|
|
51
|
+
assert.equal(a.sent.length, 1)
|
|
52
|
+
assert.equal(b.sent.length, 0)
|
|
53
|
+
} finally {
|
|
54
|
+
await releaseChannelHostRPC('a')
|
|
55
|
+
await releaseChannelHostRPC('b')
|
|
56
|
+
}
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
test('a response is routed back to the call it belongs to', async () => {
|
|
60
|
+
const { channel, sent } = fakeChannel('routed')
|
|
61
|
+
try {
|
|
62
|
+
const service = getChannelHostRPC(channel)
|
|
63
|
+
const call = service.invoke('localCheckout', {})
|
|
64
|
+
const { id } = sent[0] as { id: string }
|
|
65
|
+
|
|
66
|
+
assert.equal(
|
|
67
|
+
handleChannelRPCResponse('routed', {
|
|
68
|
+
action: CHANNEL_RPC_RESPONSE,
|
|
69
|
+
id,
|
|
70
|
+
ok: true,
|
|
71
|
+
result: { sha: 'deadbeef' },
|
|
72
|
+
}),
|
|
73
|
+
true
|
|
74
|
+
)
|
|
75
|
+
assert.deepEqual(await call, { sha: 'deadbeef' })
|
|
76
|
+
} finally {
|
|
77
|
+
await releaseChannelHostRPC('routed')
|
|
78
|
+
}
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
test('a frame for an unknown connection or id is dropped, not thrown', async () => {
|
|
82
|
+
const { channel, sent } = fakeChannel('dropped')
|
|
83
|
+
try {
|
|
84
|
+
const service = getChannelHostRPC(channel)
|
|
85
|
+
const call = service.invoke('localCheckout', {})
|
|
86
|
+
const { id } = sent[0] as { id: string }
|
|
87
|
+
|
|
88
|
+
// No transport for that connection at all.
|
|
89
|
+
assert.equal(
|
|
90
|
+
handleChannelRPCResponse('never-connected', {
|
|
91
|
+
action: CHANNEL_RPC_RESPONSE,
|
|
92
|
+
id,
|
|
93
|
+
ok: true,
|
|
94
|
+
result: null,
|
|
95
|
+
}),
|
|
96
|
+
false
|
|
97
|
+
)
|
|
98
|
+
// Right connection, but an id that was never registered — a late frame
|
|
99
|
+
// from a call that already timed out looks exactly like this.
|
|
100
|
+
assert.equal(
|
|
101
|
+
handleChannelRPCResponse('dropped', {
|
|
102
|
+
action: CHANNEL_RPC_RESPONSE,
|
|
103
|
+
id: 'not-an-id',
|
|
104
|
+
ok: true,
|
|
105
|
+
result: null,
|
|
106
|
+
}),
|
|
107
|
+
false
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
handleChannelRPCResponse('dropped', {
|
|
111
|
+
action: CHANNEL_RPC_RESPONSE,
|
|
112
|
+
id,
|
|
113
|
+
ok: true,
|
|
114
|
+
result: 'ok',
|
|
115
|
+
})
|
|
116
|
+
assert.equal(await call, 'ok')
|
|
117
|
+
} finally {
|
|
118
|
+
await releaseChannelHostRPC('dropped')
|
|
119
|
+
}
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
test('releasing rejects everything still in flight', async () => {
|
|
123
|
+
const { channel } = fakeChannel('released')
|
|
124
|
+
const call = getChannelHostRPC(channel).invoke('localCheckout', {})
|
|
125
|
+
|
|
126
|
+
await releaseChannelHostRPC('released')
|
|
127
|
+
|
|
128
|
+
// Without this a caller waits out the full timeout on a socket that is
|
|
129
|
+
// already gone.
|
|
130
|
+
await assert.rejects(call)
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
test('releasing forgets the connection', async () => {
|
|
134
|
+
const { channel } = fakeChannel('forgotten')
|
|
135
|
+
const first = getChannelHostRPC(channel)
|
|
136
|
+
await releaseChannelHostRPC('forgotten')
|
|
137
|
+
|
|
138
|
+
try {
|
|
139
|
+
// A stale entry would hand a reconnecting channel a stopped transport,
|
|
140
|
+
// and the map would grow for the life of the process.
|
|
141
|
+
assert.notEqual(getChannelHostRPC(channel), first)
|
|
142
|
+
} finally {
|
|
143
|
+
await releaseChannelHostRPC('forgotten')
|
|
144
|
+
}
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
test('releasing an unknown connection is a no-op', async () => {
|
|
148
|
+
await releaseChannelHostRPC('was-never-here')
|
|
149
|
+
})
|
|
150
|
+
})
|