@pikku/core 0.12.79 → 0.12.82
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 +473 -0
- package/dist/dev/hot-reload.js +1 -6
- package/dist/ecosystem.d.ts +27 -0
- package/dist/ecosystem.js +26 -0
- package/dist/errors/error-handler.js +4 -2
- package/dist/errors/index.d.ts +1 -1
- package/dist/errors/index.js +1 -1
- package/dist/function/function-runner.js +22 -47
- package/dist/function/functions.types.d.ts +9 -5
- package/dist/function/index.d.ts +1 -1
- package/dist/index.d.ts +14 -19
- package/dist/index.js +6 -11
- package/dist/middleware/auth-cookie.d.ts +0 -4
- package/dist/middleware/auth-cookie.js +38 -2
- package/dist/middleware/cors.js +1 -0
- package/dist/middleware/index.d.ts +1 -1
- package/dist/middleware/index.js +1 -1
- package/dist/middleware/remote-auth.js +14 -3
- package/dist/permissions.d.ts +8 -10
- package/dist/permissions.js +0 -11
- package/dist/pikku-state.js +16 -5
- package/dist/schema.js +35 -1
- package/dist/services/ai-agent-runner-service.d.ts +7 -0
- package/dist/services/ai-run-state-service.d.ts +17 -1
- package/dist/services/in-memory-ai-run-state-service.d.ts +6 -2
- package/dist/services/in-memory-ai-run-state-service.js +11 -1
- package/dist/services/in-memory-workflow-service.js +2 -0
- package/dist/services/index.d.ts +15 -15
- package/dist/services/index.js +5 -5
- package/dist/services/local-content-request-handler.d.ts +21 -0
- package/dist/services/local-content-request-handler.js +72 -53
- package/dist/services/local-content.d.ts +6 -0
- package/dist/services/local-content.js +14 -1
- package/dist/services/scoped-credential-service.d.ts +21 -0
- package/dist/services/scoped-credential-service.js +53 -0
- package/dist/services/workflow-service.d.ts +4 -2
- package/dist/testing/service-tests/agent-run-service-tests.d.ts +10 -0
- package/dist/testing/service-tests/agent-run-service-tests.js +72 -0
- package/dist/testing/service-tests/ai-storage-service-tests.d.ts +3 -0
- package/dist/testing/service-tests/ai-storage-service-tests.js +302 -0
- package/dist/testing/service-tests/channel-store-tests.d.ts +3 -0
- package/dist/testing/service-tests/channel-store-tests.js +72 -0
- package/dist/testing/service-tests/credential-service-tests.d.ts +3 -0
- package/dist/testing/service-tests/credential-service-tests.js +109 -0
- package/dist/testing/service-tests/deployment-service-tests.d.ts +3 -0
- package/dist/testing/service-tests/deployment-service-tests.js +21 -0
- package/dist/testing/service-tests/event-hub-store-tests.d.ts +3 -0
- package/dist/testing/service-tests/event-hub-store-tests.js +34 -0
- package/dist/testing/service-tests/secret-service-tests.d.ts +3 -0
- package/dist/testing/service-tests/secret-service-tests.js +80 -0
- package/dist/testing/service-tests/session-store-tests.d.ts +3 -0
- package/dist/testing/service-tests/session-store-tests.js +43 -0
- package/dist/testing/service-tests/workflow-run-service-tests.d.ts +3 -0
- package/dist/testing/service-tests/workflow-run-service-tests.js +42 -0
- package/dist/testing/service-tests/workflow-service-tests.d.ts +3 -0
- package/dist/testing/service-tests/workflow-service-tests.js +150 -0
- package/dist/testing/service-tests.d.ts +6 -0
- package/dist/testing/service-tests.js +26 -791
- package/dist/types/core.types.d.ts +11 -3
- package/dist/types/state.types.d.ts +14 -1
- package/dist/wirings/actor-flow/index.d.ts +1 -1
- package/dist/wirings/actor-flow/run-conversation.js +1 -6
- package/dist/wirings/ai-agent/agent-rpc.d.ts +15 -0
- package/dist/wirings/ai-agent/agent-rpc.js +53 -0
- package/dist/wirings/ai-agent/ai-agent-agui.js +1 -5
- package/dist/wirings/ai-agent/ai-agent-finalize.d.ts +58 -0
- package/dist/wirings/ai-agent/ai-agent-finalize.js +138 -0
- package/dist/wirings/ai-agent/ai-agent-interrupt.js +1 -0
- package/dist/wirings/ai-agent/ai-agent-memory.d.ts +2 -8
- package/dist/wirings/ai-agent/ai-agent-memory.js +37 -18
- package/dist/wirings/ai-agent/ai-agent-model-config.d.ts +7 -0
- package/dist/wirings/ai-agent/ai-agent-model-config.js +44 -1
- package/dist/wirings/ai-agent/ai-agent-prepare.js +5 -9
- package/dist/wirings/ai-agent/ai-agent-runner.js +89 -147
- package/dist/wirings/ai-agent/ai-agent-stream.js +109 -97
- package/dist/wirings/ai-agent/ai-agent-turn.d.ts +57 -0
- package/dist/wirings/ai-agent/ai-agent-turn.js +82 -0
- package/dist/wirings/ai-agent/ai-agent.types.d.ts +47 -2
- package/dist/wirings/ai-agent/index.d.ts +8 -7
- package/dist/wirings/ai-agent/index.js +5 -4
- package/dist/wirings/ai-agent/voice-input.js +1 -6
- package/dist/wirings/ai-agent/voice-output.js +2 -12
- package/dist/wirings/ai-scorer/ai-scorer-grade.d.ts +26 -0
- package/dist/wirings/ai-scorer/ai-scorer-grade.js +33 -0
- package/dist/wirings/ai-scorer/ai-scorer-judge.d.ts +17 -0
- package/dist/wirings/ai-scorer/ai-scorer-judge.js +92 -0
- package/dist/wirings/ai-scorer/ai-scorer-live.d.ts +15 -0
- package/dist/wirings/ai-scorer/ai-scorer-live.js +38 -0
- package/dist/wirings/ai-scorer/ai-scorer-registry.d.ts +18 -0
- package/dist/wirings/ai-scorer/ai-scorer-registry.js +46 -0
- package/dist/wirings/ai-scorer/ai-scorer-sampling.d.ts +8 -0
- package/dist/wirings/ai-scorer/ai-scorer-sampling.js +31 -0
- package/dist/wirings/ai-scorer/ai-scorer-snapshots.d.ts +10 -0
- package/dist/wirings/ai-scorer/ai-scorer-snapshots.js +40 -0
- package/dist/wirings/ai-scorer/ai-scorer-worker.d.ts +15 -0
- package/dist/wirings/ai-scorer/ai-scorer-worker.js +58 -0
- package/dist/wirings/ai-scorer/ai-scorer.d.ts +39 -0
- package/dist/wirings/ai-scorer/ai-scorer.js +40 -0
- package/dist/wirings/ai-scorer/ai-scorer.types.d.ts +90 -0
- package/dist/wirings/ai-scorer/ai-scorer.types.js +4 -0
- package/dist/wirings/ai-scorer/index.d.ts +6 -0
- package/dist/wirings/ai-scorer/index.js +5 -0
- package/dist/wirings/channel/channel-common.js +1 -0
- package/dist/wirings/channel/channel-handler.js +3 -5
- package/dist/wirings/channel/channel-rpc-service.d.ts +0 -6
- package/dist/wirings/channel/channel-rpc-service.js +0 -8
- package/dist/wirings/channel/channel-rpc.types.d.ts +6 -0
- package/dist/wirings/channel/channel-rpc.types.js +8 -0
- package/dist/wirings/channel/channel-runner.d.ts +1 -3
- package/dist/wirings/channel/channel-runner.js +16 -8
- package/dist/wirings/channel/channel.types.d.ts +2 -0
- package/dist/wirings/channel/index.d.ts +5 -6
- package/dist/wirings/channel/index.js +3 -4
- package/dist/wirings/channel/local/local-channel-runner.js +8 -1
- package/dist/wirings/channel/pikku-abstract-channel-handler.js +1 -0
- package/dist/wirings/channel/serverless/serverless-channel-runner.js +3 -0
- package/dist/wirings/cli/channel/cli-channel-runner.js +2 -0
- package/dist/wirings/cli/channel/cli-raw-channel-runner.js +9 -1
- package/dist/wirings/cli/channel/index.d.ts +1 -2
- package/dist/wirings/cli/channel/index.js +0 -1
- package/dist/wirings/cli/cli-runner.js +17 -3
- package/dist/wirings/cli/cli.types.d.ts +0 -8
- package/dist/wirings/cli/command-parser.js +13 -0
- package/dist/wirings/credential/index.d.ts +1 -1
- package/dist/wirings/gateway/gateway-runner.js +9 -2
- package/dist/wirings/gateway/index.d.ts +1 -1
- package/dist/wirings/http/http-routes.js +2 -0
- package/dist/wirings/http/http-runner.d.ts +0 -10
- package/dist/wirings/http/http-runner.js +14 -15
- package/dist/wirings/http/http.types.d.ts +0 -10
- package/dist/wirings/http/index.d.ts +2 -3
- package/dist/wirings/http/index.js +1 -1
- package/dist/wirings/mcp/index.d.ts +1 -1
- package/dist/wirings/mcp/mcp-runner.d.ts +15 -7
- package/dist/wirings/mcp/mcp-runner.js +18 -11
- package/dist/wirings/persona/index.d.ts +3 -4
- package/dist/wirings/persona/index.js +2 -3
- package/dist/wirings/queue/index.d.ts +1 -3
- package/dist/wirings/queue/index.js +1 -3
- package/dist/wirings/rpc/addon-runner.d.ts +4 -0
- package/dist/wirings/rpc/addon-runner.js +19 -3
- package/dist/wirings/rpc/rpc-runner.d.ts +8 -51
- package/dist/wirings/rpc/rpc-runner.js +55 -86
- package/dist/wirings/rpc/rpc-types.d.ts +7 -0
- package/dist/wirings/rpc/wire-addon.d.ts +13 -0
- package/dist/wirings/rpc/wire-addon.js +4 -0
- package/dist/wirings/scheduler/index.d.ts +1 -1
- package/dist/wirings/secret/validate-secret-definitions.js +2 -2
- package/dist/wirings/trigger/index.d.ts +1 -1
- package/dist/wirings/trigger/pikku-trigger-service.d.ts +0 -4
- package/dist/wirings/trigger/trigger-runner.js +1 -0
- package/dist/wirings/virtual-user/index.d.ts +5 -6
- package/dist/wirings/virtual-user/index.js +2 -4
- package/dist/wirings/virtual-user/run-virtual-user.js +11 -11
- package/dist/wirings/virtual-user/virtual-user-derive.js +4 -25
- package/dist/wirings/virtual-user/virtual-user-dispositions.js +1 -4
- package/dist/wirings/workflow/dsl/workflow-dsl.types.d.ts +100 -3
- package/dist/wirings/workflow/feature.js +7 -4
- package/dist/wirings/workflow/graph/graph-runner.d.ts +1 -2
- package/dist/wirings/workflow/graph/graph-runner.js +8 -7
- package/dist/wirings/workflow/graph/workflow-graph.types.d.ts +0 -4
- package/dist/wirings/workflow/index.d.ts +12 -7
- package/dist/wirings/workflow/index.js +7 -3
- package/dist/wirings/workflow/pikku-scenario-service.d.ts +17 -3
- package/dist/wirings/workflow/pikku-scenario-service.js +64 -10
- package/dist/wirings/workflow/pikku-workflow-service.d.ts +28 -147
- package/dist/wirings/workflow/pikku-workflow-service.js +85 -493
- package/dist/wirings/workflow/scenario-step.types.d.ts +8 -0
- package/dist/wirings/workflow/workflow-approval-audit.d.ts +16 -0
- package/dist/wirings/workflow/workflow-approval-audit.js +40 -0
- package/dist/wirings/workflow/workflow-approval-policy.d.ts +20 -0
- package/dist/wirings/workflow/workflow-approval-policy.js +48 -0
- package/dist/wirings/workflow/workflow-approval.d.ts +67 -0
- package/dist/wirings/workflow/workflow-approval.js +177 -0
- package/dist/wirings/workflow/workflow-constants.d.ts +26 -0
- package/dist/wirings/workflow/workflow-constants.js +35 -0
- package/dist/wirings/workflow/workflow-errors.d.ts +58 -0
- package/dist/wirings/workflow/workflow-errors.js +112 -0
- package/dist/wirings/workflow/workflow-meta-resolver.d.ts +11 -0
- package/dist/wirings/workflow/workflow-meta-resolver.js +31 -0
- package/dist/wirings/workflow/workflow-queue-routing.d.ts +8 -0
- package/dist/wirings/workflow/workflow-queue-routing.js +38 -0
- package/dist/wirings/workflow/workflow-queue-wiring.d.ts +20 -0
- package/dist/wirings/workflow/workflow-queue-wiring.js +79 -0
- package/dist/wirings/workflow/workflow-recovery.d.ts +68 -0
- package/dist/wirings/workflow/workflow-recovery.js +101 -0
- package/dist/wirings/workflow/workflow-run-engine.types.d.ts +54 -0
- package/dist/wirings/workflow/workflow-run-ownership.d.ts +17 -0
- package/dist/wirings/workflow/workflow-run-ownership.js +30 -0
- package/dist/wirings/workflow/workflow-suspend.d.ts +12 -0
- package/dist/wirings/workflow/workflow-suspend.js +33 -0
- package/dist/wirings/workflow/workflow.types.d.ts +8 -1
- package/knowledge/decisions/internals/a-non-streaming-agent-run-registers-with-airunstate-too.md +22 -0
- package/knowledge/decisions/internals/a-resumed-agent-turn-is-as-interruptible-as-the-first.md +20 -0
- package/knowledge/decisions/internals/a-scenario-step-template-is-offered-unfilled.md +21 -0
- package/knowledge/decisions/internals/a-virtual-user-decides-whether-to-trust-memory-once-per-turn.md +21 -0
- package/knowledge/decisions/internals/a-wall-clock-threshold-is-a-load-test-in-disguise.md +46 -0
- package/knowledge/decisions/internals/a-workflow-wire-is-built-from-the-run-not-from-the-rpc-service.md +45 -0
- package/knowledge/decisions/internals/addon-pikku-meta-ships-at-the-package-root-or-under-dist.md +32 -0
- package/knowledge/decisions/internals/agent-context-waits-for-a-tool-result-still-being-written.md +21 -0
- package/knowledge/decisions/internals/agent-speech-travels-as-a-custom-agui-event.md +22 -0
- package/knowledge/decisions/internals/an-addon-scope-root-loses-to-a-root-the-host-already-declares.md +39 -0
- package/knowledge/decisions/internals/an-agent-interrupt-is-not-a-failure.md +23 -0
- package/knowledge/decisions/internals/an-agent-run-owned-by-another-instance-says-so.md +25 -0
- package/knowledge/decisions/internals/an-agent-stream-send-must-return-the-inner-sends-promise.md +22 -0
- package/knowledge/decisions/internals/an-empty-text-part-is-omitted-from-an-agent-message.md +20 -0
- package/knowledge/decisions/internals/an-empty-transcript-is-not-recorded.md +22 -0
- package/knowledge/decisions/internals/an-unref-d-timer-cannot-be-awaited-under-node-test.md +66 -0
- package/knowledge/decisions/internals/channel-state-accessors-are-unsound-generics-that-every-implementation-asserts.md +33 -0
- package/knowledge/decisions/internals/gateway-listener-middleware-runs-without-an-rpc-on-the-wire.md +43 -0
- package/knowledge/decisions/internals/hot-reload-writes-into-the-function-map-captured-at-startup.md +26 -0
- package/knowledge/decisions/internals/index.md +42 -3
- package/knowledge/decisions/internals/only-exposed-functions-enter-a-virtual-user-catalogue.md +21 -0
- package/knowledge/decisions/internals/scenario-given-and-when-are-sugar-but-then-is-not.md +24 -0
- package/knowledge/decisions/internals/side-effects-are-an-allowlist-not-a-boolean.md +34 -0
- package/knowledge/decisions/internals/speech-synthesis-picks-a-voice-per-sentence-and-warns-once.md +24 -0
- package/knowledge/decisions/internals/the-actor-prompt-says-json-because-of-json-object-mode.md +22 -0
- package/knowledge/decisions/internals/the-agent-done-event-goes-through-the-middleware-and-is-awaited.md +26 -0
- package/knowledge/decisions/internals/the-api-report-pins-members-not-just-names.md +43 -0
- package/knowledge/decisions/internals/the-ecosystem-entry-point-carries-the-adapter-surface.md +58 -0
- package/knowledge/decisions/internals/the-middleware-resolution-cache-is-deliberately-unbounded.md +40 -0
- package/knowledge/decisions/internals/the-per-invocation-rpc-view-is-a-class.md +40 -0
- package/knowledge/decisions/internals/the-persona-runtime-is-exported-from-the-persona-entry-point.md +28 -0
- package/knowledge/decisions/internals/the-transcript-event-is-sent-ahead-of-the-run.md +25 -0
- package/knowledge/decisions/internals/the-virtual-user-catalogue-is-the-only-gate-on-what-may-be-called.md +21 -0
- package/knowledge/decisions/internals/the-worker-disposition-is-the-one-that-is-not-testing.md +22 -0
- package/knowledge/decisions/internals/thread-history-records-the-transcript-not-the-audio.md +25 -0
- package/knowledge/decisions/internals/validate-runs-checks-by-precondition.md +115 -0
- package/knowledge/decisions/internals/virtual-user-step-order-comes-from-insertion-order.md +21 -0
- package/knowledge/decisions/internals/voice-output-speaks-unless-voice-input-explicitly-says-otherwise.md +24 -0
- package/knowledge/decisions/internals/wiring-registries-erase-the-generics-their-wire-functions-capture.md +35 -0
- package/knowledge/decisions/security/a-dropped-audit-write-is-always-logged.md +4 -2
- package/knowledge/decisions/security/a-function-never-receives-the-secret-service.md +37 -0
- package/knowledge/decisions/security/a-graph-run-starts-at-an-entry-node-the-graph-declared.md +33 -0
- package/knowledge/decisions/security/a-permission-gets-a-wire-it-cannot-reply-on.md +33 -0
- package/knowledge/decisions/security/a-step-runs-the-function-the-workflow-dispatched-it-with.md +37 -0
- package/knowledge/decisions/security/a-virtual-user-is-never-offered-a-step-that-would-forge-its-own-oracle.md +28 -0
- package/knowledge/decisions/security/a-workflow-run-is-read-and-approved-by-its-owner.md +50 -0
- package/knowledge/decisions/security/an-agent-approval-is-claimed-before-the-tool-runs.md +33 -0
- package/knowledge/decisions/security/an-approval-answer-outlives-the-run-it-answered.md +59 -0
- package/knowledge/decisions/security/an-upload-is-counted-as-it-arrives-not-buffered-then-measured.md +24 -0
- package/knowledge/decisions/security/index.md +9 -0
- package/knowledge/questions/channel-middleware-accepts-bare-factories-that-nothing-resolves.md +35 -0
- package/knowledge/questions/index.md +2 -1
- package/knowledge/questions/unauthorized-channel-replies-escape-the-declared-out-type.md +44 -0
- package/package.json +15 -3
- package/scripts/generate-api-report.d.mts +1 -0
- package/scripts/generate-api-report.mjs +89 -0
- package/scripts/generate-api-report.mts +343 -0
- package/src/api-report.test.ts +69 -0
- package/src/column-form.ts +1 -4
- package/src/crypto-utils.test.ts +15 -2
- package/src/dev/hot-reload.ts +2 -7
- package/src/ecosystem.ts +40 -0
- package/src/errors/error-handler.ts +5 -3
- package/src/errors/error.test.ts +4 -1
- package/src/errors/index.ts +1 -1
- package/src/function/function-runner.test.ts +53 -1
- package/src/function/function-runner.ts +33 -48
- package/src/function/functions.types.ts +15 -9
- package/src/function/index.ts +0 -2
- package/src/handle-error.ts +1 -1
- package/src/index.ts +3 -57
- package/src/middleware/auth-cookie.test.ts +50 -0
- package/src/middleware/auth-cookie.ts +38 -2
- package/src/middleware/cors.ts +2 -1
- package/src/middleware/index.ts +1 -1
- package/src/middleware/remote-auth.test.ts +43 -0
- package/src/middleware/remote-auth.ts +17 -3
- package/src/middleware-runner.ts +4 -2
- package/src/no-any-casts.test.ts +57 -0
- package/src/permissions.test.ts +3 -1
- package/src/permissions.ts +22 -24
- package/src/pikku-state.ts +22 -6
- package/src/public-surface.json +554 -0
- package/src/public-surface.json.README +25 -0
- package/src/public-surface.test.ts +105 -0
- package/src/removed-legacy-exports.test.ts +62 -0
- package/src/schema.test.ts +78 -0
- package/src/schema.ts +36 -1
- package/src/services/ai-agent-runner-service.ts +12 -1
- package/src/services/ai-run-state-service.ts +18 -1
- package/src/services/audit-service.ts +2 -2
- package/src/services/in-memory-ai-run-state-service.ts +16 -2
- package/src/services/in-memory-workflow-service.ts +2 -0
- package/src/services/index.ts +4 -47
- package/src/services/local-content-request-handler.test.ts +43 -5
- package/src/services/local-content-request-handler.ts +103 -74
- package/src/services/local-content.ts +15 -1
- package/src/services/local-email-service.ts +5 -1
- package/src/services/scoped-credential-service.test.ts +86 -0
- package/src/services/scoped-credential-service.ts +63 -0
- package/src/services/system-role-guard.test.ts +4 -1
- package/src/services/workflow-service.ts +9 -2
- package/src/side-effects-are-declared.test.ts +84 -0
- package/src/source-files-stay-composable.test.ts +41 -0
- package/src/testing/service-tests/agent-run-service-tests.ts +98 -0
- package/src/testing/service-tests/ai-storage-service-tests.ts +379 -0
- package/src/testing/service-tests/channel-store-tests.ts +98 -0
- package/src/testing/service-tests/credential-service-tests.ts +143 -0
- package/src/testing/service-tests/deployment-service-tests.ts +33 -0
- package/src/testing/service-tests/event-hub-store-tests.ts +48 -0
- package/src/testing/service-tests/secret-service-tests.ts +105 -0
- package/src/testing/service-tests/session-store-tests.ts +62 -0
- package/src/testing/service-tests/workflow-run-service-tests.ts +59 -0
- package/src/testing/service-tests/workflow-service-tests.ts +308 -0
- package/src/testing/service-tests.ts +31 -1111
- package/src/types/core.types.ts +14 -3
- package/src/types/state.types.ts +17 -1
- package/src/wirings/actor-flow/index.ts +0 -3
- package/src/wirings/actor-flow/run-conversation.ts +1 -6
- package/src/wirings/ai-agent/agent-rpc.ts +120 -0
- package/src/wirings/ai-agent/ai-agent-agui.ts +1 -5
- package/src/wirings/ai-agent/ai-agent-finalize.test.ts +186 -0
- package/src/wirings/ai-agent/ai-agent-finalize.ts +197 -0
- package/src/wirings/ai-agent/ai-agent-interrupt.test.ts +2 -1
- package/src/wirings/ai-agent/ai-agent-interrupt.ts +1 -0
- package/src/wirings/ai-agent/ai-agent-memory.ts +62 -39
- package/src/wirings/ai-agent/ai-agent-model-config.test.ts +72 -3
- package/src/wirings/ai-agent/ai-agent-model-config.ts +49 -1
- package/src/wirings/ai-agent/ai-agent-prepare.ts +12 -12
- package/src/wirings/ai-agent/ai-agent-resume-authorization.test.ts +1 -0
- package/src/wirings/ai-agent/ai-agent-runner.test.ts +82 -2
- package/src/wirings/ai-agent/ai-agent-runner.ts +120 -160
- package/src/wirings/ai-agent/ai-agent-stream-output-hooks.test.ts +353 -0
- package/src/wirings/ai-agent/ai-agent-stream.test.ts +2 -0
- package/src/wirings/ai-agent/ai-agent-stream.ts +152 -117
- package/src/wirings/ai-agent/ai-agent-thread-ownership.test.ts +1 -1
- package/src/wirings/ai-agent/ai-agent-turn.test.ts +67 -0
- package/src/wirings/ai-agent/ai-agent-turn.ts +122 -0
- package/src/wirings/ai-agent/ai-agent.types.ts +65 -5
- package/src/wirings/ai-agent/index.ts +2 -16
- package/src/wirings/ai-agent/voice-input.ts +1 -6
- package/src/wirings/ai-agent/voice-output.ts +2 -12
- package/src/wirings/ai-scorer/ai-scorer-grade.test.ts +106 -0
- package/src/wirings/ai-scorer/ai-scorer-grade.ts +55 -0
- package/src/wirings/ai-scorer/ai-scorer-judge.test.ts +143 -0
- package/src/wirings/ai-scorer/ai-scorer-judge.ts +120 -0
- package/src/wirings/ai-scorer/ai-scorer-live.test.ts +174 -0
- package/src/wirings/ai-scorer/ai-scorer-live.ts +56 -0
- package/src/wirings/ai-scorer/ai-scorer-registry.ts +63 -0
- package/src/wirings/ai-scorer/ai-scorer-sampling.test.ts +34 -0
- package/src/wirings/ai-scorer/ai-scorer-sampling.ts +36 -0
- package/src/wirings/ai-scorer/ai-scorer-snapshots.test.ts +49 -0
- package/src/wirings/ai-scorer/ai-scorer-snapshots.ts +46 -0
- package/src/wirings/ai-scorer/ai-scorer-worker.test.ts +122 -0
- package/src/wirings/ai-scorer/ai-scorer-worker.ts +69 -0
- package/src/wirings/ai-scorer/ai-scorer.ts +76 -0
- package/src/wirings/ai-scorer/ai-scorer.types.ts +107 -0
- package/src/wirings/ai-scorer/index.ts +24 -0
- package/src/wirings/channel/channel-common.ts +5 -2
- package/src/wirings/channel/channel-handler-shapes.test.ts +72 -0
- package/src/wirings/channel/channel-handler.ts +7 -7
- package/src/wirings/channel/channel-rpc-service.ts +0 -14
- package/src/wirings/channel/channel-rpc.test.ts +25 -4
- package/src/wirings/channel/channel-rpc.types.ts +14 -0
- package/src/wirings/channel/channel-runner.ts +33 -20
- package/src/wirings/channel/channel.types.ts +2 -0
- package/src/wirings/channel/index.ts +1 -20
- package/src/wirings/channel/local/local-channel-runner.test.ts +68 -0
- package/src/wirings/channel/local/local-channel-runner.ts +9 -2
- package/src/wirings/channel/pikku-abstract-channel-handler.ts +2 -1
- package/src/wirings/channel/serverless/serverless-channel-runner.ts +6 -3
- package/src/wirings/cli/channel/cli-channel-runner.ts +3 -1
- package/src/wirings/cli/channel/cli-raw-channel-runner.test.ts +23 -0
- package/src/wirings/cli/channel/cli-raw-channel-runner.ts +12 -1
- package/src/wirings/cli/channel/cli-raw-client-runner.ts +4 -1
- package/src/wirings/cli/channel/index.ts +0 -7
- package/src/wirings/cli/cli-runner.test.ts +68 -0
- package/src/wirings/cli/cli-runner.ts +25 -5
- package/src/wirings/cli/cli.types.ts +0 -39
- package/src/wirings/cli/command-parser.test.ts +19 -0
- package/src/wirings/cli/command-parser.ts +17 -1
- package/src/wirings/credential/index.ts +0 -1
- package/src/wirings/gateway/gateway-channel-meta.test.ts +44 -0
- package/src/wirings/gateway/gateway-runner.ts +27 -19
- package/src/wirings/gateway/index.ts +0 -3
- package/src/wirings/http/http-routes.ts +4 -1
- package/src/wirings/http/http-runner.test.ts +66 -0
- package/src/wirings/http/http-runner.ts +21 -28
- package/src/wirings/http/http.types.ts +0 -15
- package/src/wirings/http/index.ts +2 -8
- package/src/wirings/http/pikku-fetch-http-request.ts +2 -2
- package/src/wirings/http/web-request.ts +1 -1
- package/src/wirings/mcp/index.ts +0 -1
- package/src/wirings/mcp/mcp-runner.test.ts +181 -0
- package/src/wirings/mcp/mcp-runner.ts +37 -21
- package/src/wirings/persona/index.ts +0 -8
- package/src/wirings/persona/persona-environments.test.ts +14 -3
- package/src/wirings/persona/persona.test.ts +13 -3
- package/src/wirings/persona/validate-personas.ts +5 -1
- package/src/wirings/queue/index.ts +0 -14
- package/src/wirings/queue/queue-runner.ts +1 -1
- package/src/wirings/rpc/addon-auth-tags.test.ts +1 -5
- package/src/wirings/rpc/addon-runner.ts +34 -3
- package/src/wirings/rpc/addon-secrets.test.ts +261 -0
- package/src/wirings/rpc/rpc-runner.test.ts +2 -0
- package/src/wirings/rpc/rpc-runner.ts +60 -136
- package/src/wirings/rpc/rpc-types.ts +11 -0
- package/src/wirings/rpc/wire-addon.ts +20 -1
- package/src/wirings/scheduler/index.ts +0 -1
- package/src/wirings/secret/validate-secret-definitions.test.ts +22 -0
- package/src/wirings/secret/validate-secret-definitions.ts +2 -2
- package/src/wirings/trigger/index.ts +0 -1
- package/src/wirings/trigger/pikku-trigger-service.ts +0 -5
- package/src/wirings/trigger/trigger-runner.ts +7 -5
- package/src/wirings/virtual-user/index.ts +0 -16
- package/src/wirings/virtual-user/run-virtual-user.test.ts +18 -9
- package/src/wirings/virtual-user/run-virtual-user.ts +28 -15
- package/src/wirings/virtual-user/virtual-user-agents.test.ts +4 -1
- package/src/wirings/virtual-user/virtual-user-derive.ts +4 -25
- package/src/wirings/virtual-user/virtual-user-dispositions.test.ts +7 -2
- package/src/wirings/virtual-user/virtual-user-dispositions.ts +1 -4
- package/src/wirings/virtual-user/virtual-user-intents.test.ts +13 -3
- package/src/wirings/workflow/dsl/workflow-dsl.types.ts +117 -4
- package/src/wirings/workflow/feature.ts +7 -4
- package/src/wirings/workflow/graph/graph-node.ts +1 -1
- package/src/wirings/workflow/graph/graph-runner.test.ts +76 -2
- package/src/wirings/workflow/graph/graph-runner.ts +13 -16
- package/src/wirings/workflow/graph/workflow-graph.types.ts +0 -5
- package/src/wirings/workflow/index.ts +14 -24
- package/src/wirings/workflow/pikku-scenario-service.ts +101 -27
- package/src/wirings/workflow/pikku-workflow-service.test.ts +15 -14
- package/src/wirings/workflow/pikku-workflow-service.ts +214 -743
- package/src/wirings/workflow/scenario-expectations.test.ts +75 -0
- package/src/wirings/workflow/scenario-hooks.test.ts +52 -0
- package/src/wirings/workflow/scenario-step.types.ts +8 -0
- package/src/wirings/workflow/workflow-approval-audit.ts +47 -0
- package/src/wirings/workflow/workflow-approval-policy.test.ts +524 -0
- package/src/wirings/workflow/workflow-approval-policy.ts +68 -0
- package/src/wirings/workflow/workflow-approval.ts +289 -0
- package/src/wirings/workflow/workflow-child-run-session.test.ts +89 -0
- package/src/wirings/workflow/workflow-constants.ts +47 -0
- package/src/wirings/workflow/workflow-dispatch-durability.test.ts +1 -1
- package/src/wirings/workflow/workflow-errors.ts +128 -0
- package/src/wirings/workflow/workflow-inline-authority.test.ts +12 -6
- package/src/wirings/workflow/workflow-meta-resolver.ts +45 -0
- package/src/wirings/workflow/workflow-missing-meta.test.ts +92 -0
- package/src/wirings/workflow/workflow-queue-routing.ts +85 -0
- package/src/wirings/workflow/workflow-queue-wiring.ts +149 -0
- package/src/wirings/workflow/workflow-recovery.ts +162 -0
- package/src/wirings/workflow/workflow-retry-policy.test.ts +1 -1
- package/src/wirings/workflow/workflow-run-authority.test.ts +212 -0
- package/src/wirings/workflow/workflow-run-engine.types.ts +91 -0
- package/src/wirings/workflow/workflow-run-ownership.ts +37 -0
- package/src/wirings/workflow/workflow-suspend.ts +61 -0
- package/src/wirings/workflow/workflow.types.ts +7 -9
- package/src/wirings-stay-decoupled.test.ts +127 -0
- package/tsconfig.json +1 -1
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/internal.d.ts +0 -3
- package/dist/internal.js +0 -2
- package/dist/middleware/timeout.d.ts +0 -9
- package/dist/middleware/timeout.js +0 -15
- package/dist/pikku-response.d.ts +0 -6
- package/dist/pikku-response.js +0 -6
- package/dist/services/gopass-secrets.d.ts +0 -15
- package/dist/services/gopass-secrets.js +0 -76
- package/dist/services/http-scenario-actors.d.ts +0 -75
- package/dist/services/http-scenario-actors.js +0 -195
- package/dist/services/http-user-flow-actors.d.ts +0 -67
- package/dist/services/http-user-flow-actors.js +0 -193
- package/dist/services/scenario-actors-service.d.ts +0 -127
- package/dist/services/scenario-actors-service.js +0 -40
- package/dist/services/user-flow-actors-service.d.ts +0 -39
- package/dist/wirings/credential/wire-credential.d.ts +0 -48
- package/dist/wirings/credential/wire-credential.js +0 -47
- package/dist/wirings/oauth2/oauth2-client.d.ts +0 -47
- package/dist/wirings/oauth2/oauth2-client.js +0 -263
- package/dist/wirings/oauth2/oauth2-routes.d.ts +0 -35
- package/dist/wirings/oauth2/oauth2-routes.js +0 -146
- package/dist/wirings/scope/wire-scope.d.ts +0 -33
- package/dist/wirings/scope/wire-scope.js +0 -32
- package/dist/wirings/workflow/dsl/index.d.ts +0 -5
- package/dist/wirings/workflow/dsl/index.js +0 -4
- package/dist/wirings/workflow/graph/index.d.ts +0 -5
- package/dist/wirings/workflow/graph/index.js +0 -4
- package/src/internal.ts +0 -10
- /package/dist/{services/user-flow-actors-service.js → wirings/workflow/workflow-run-engine.types.js} +0 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: decision
|
|
3
|
+
title: `given` and `when` are sugar for each other; `then` is not
|
|
4
|
+
description: The phase decides whether a step's bindings are alternatives or witnesses, so the same step function called as `when` and as `then` runs differently
|
|
5
|
+
tags: core, workflow
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# `given` and `when` are sugar for each other; `then` is not
|
|
9
|
+
|
|
10
|
+
All three run a named `pikkuScenarioStep` as one durable step. `given` and
|
|
11
|
+
`when` differ only in the prose a reporter renders — the step behaves
|
|
12
|
+
identically either way.
|
|
13
|
+
|
|
14
|
+
`then` is a different operation. The phase is what decides how a step's surface
|
|
15
|
+
bindings are treated: for an action, the bindings are *alternatives* and one is
|
|
16
|
+
chosen; for an assertion, they are *witnesses* and every applicable one runs and
|
|
17
|
+
must agree. So the same step function invoked as `when` and as `then` executes a
|
|
18
|
+
different number of times against a different number of surfaces.
|
|
19
|
+
|
|
20
|
+
See `resolveScenarioSurfaces` for the resolution itself.
|
|
21
|
+
|
|
22
|
+
**What this rules out:** collapsing the three into one call with a cosmetic
|
|
23
|
+
label, or letting a caller pass the phase as data. The phase changes execution,
|
|
24
|
+
so it has to be visible at the call site.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: decision
|
|
3
|
+
title: sideEffects is an allowlist, because core genuinely has some
|
|
4
|
+
description: The error registry is built by addError calls that run on import, so claiming sideEffects:false would let a bundler drop it and leave getErrorResponse unable to find any error
|
|
5
|
+
tags: core, packaging
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# `sideEffects` is an allowlist, not `false`
|
|
9
|
+
|
|
10
|
+
Without a `sideEffects` field a bundler must assume every module in the package
|
|
11
|
+
does something merely by being imported, so it cannot drop any of them. That is
|
|
12
|
+
the state core shipped in, and it costs every bundled consumer the whole package.
|
|
13
|
+
|
|
14
|
+
The reflex fix — `"sideEffects": false` — would be untrue. Five modules run code
|
|
15
|
+
at import, 59 top-level calls between them, and every one is `addError(...)`
|
|
16
|
+
registering an error class in the runtime registry:
|
|
17
|
+
|
|
18
|
+
- `errors/errors.js` (44)
|
|
19
|
+
- `wirings/workflow/pikku-scenario-service.js` (6)
|
|
20
|
+
- `wirings/workflow/workflow-errors.js` (5)
|
|
21
|
+
- `wirings/rpc/rpc-runner.js` (3)
|
|
22
|
+
- `wirings/rpc/remote-addon-auth.js` (1)
|
|
23
|
+
|
|
24
|
+
A bundler that dropped `errors/errors.js` because nothing imported a binding
|
|
25
|
+
from it would leave `getErrorResponse` unable to map any error to a status. The
|
|
26
|
+
app would build, and every error would come back as a generic 500.
|
|
27
|
+
|
|
28
|
+
So the field names those five exactly. Everything else can be tree-shaken.
|
|
29
|
+
|
|
30
|
+
**What this rules out:** flipping this to `false` as a performance change, and
|
|
31
|
+
adding a module-level `addError` (or any other registration) without adding the
|
|
32
|
+
module here. `side-effects-are-declared.test.ts` walks the source for top-level
|
|
33
|
+
calls and fails in both directions — a module with side effects missing from the
|
|
34
|
+
list, and a listed module that no longer has any.
|
package/knowledge/decisions/internals/speech-synthesis-picks-a-voice-per-sentence-and-warns-once.md
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: decision
|
|
3
|
+
title: Speech synthesis picks a voice per sentence but announces a limitation once
|
|
4
|
+
description: A bilingual reply should speak the half it can, and repeating the notice for every sentence would bury the reply itself
|
|
5
|
+
tags: core, ai-agent
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Speech synthesis picks a voice per sentence, and warns once per reply
|
|
9
|
+
|
|
10
|
+
`voiceOutput` checks the script of each sentence separately, but emits any
|
|
11
|
+
"cannot speak this" notice only once for the whole reply.
|
|
12
|
+
|
|
13
|
+
Per sentence is what makes the voice correct. A reply that answers in English
|
|
14
|
+
and then quotes a Chinese title is two sentences in two scripts, and each is
|
|
15
|
+
synthesized in the voice its own script needs. Checking once for the whole reply
|
|
16
|
+
would pick one voice and mispronounce the other half.
|
|
17
|
+
|
|
18
|
+
Announcing once is what keeps the reply audible. A bilingual answer should still
|
|
19
|
+
speak the part it can, and repeating the notice for every sentence of a long
|
|
20
|
+
reply buries the answer under its own caveats.
|
|
21
|
+
|
|
22
|
+
**What this rules out:** moving the script check up to the reply level to save
|
|
23
|
+
work, and moving the notice down to the sentence level for consistency with it.
|
|
24
|
+
The two belong at different granularities on purpose.
|
package/knowledge/decisions/internals/the-actor-prompt-says-json-because-of-json-object-mode.md
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: decision
|
|
3
|
+
title: The actor's instructions say the word "json" because a degraded gateway demands it
|
|
4
|
+
description: A gateway that cannot take a JSON schema falls back to OpenAI's json_object mode, which refuses any request whose prompt does not contain the word
|
|
5
|
+
tags: core, actor-flow
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# The actor's instructions say the word "json"
|
|
9
|
+
|
|
10
|
+
Every call the actor makes wants a schema'd object back. A gateway that cannot
|
|
11
|
+
accept a JSON *schema* degrades to OpenAI's `json_object` response mode, and
|
|
12
|
+
that mode refuses the request outright — a hard API error, not a bad answer —
|
|
13
|
+
unless the literal word "json" appears somewhere in the prompt.
|
|
14
|
+
|
|
15
|
+
The word is therefore in the shared instruction block, once. All three call
|
|
16
|
+
sites (turn, approvals, verdict) build on that block, so one mention covers
|
|
17
|
+
them, and it costs nothing on providers that never needed telling.
|
|
18
|
+
|
|
19
|
+
**What this rules out:** tidying the instructions by removing a word that reads
|
|
20
|
+
as redundant. It is load-bearing for exactly one provider configuration, and its
|
|
21
|
+
absence fails the request rather than degrading the output — so the failure will
|
|
22
|
+
not look like a prompt problem.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: decision
|
|
3
|
+
title: The agent `done` event goes through the middleware and is awaited
|
|
4
|
+
description: `done` is the only end-of-reply signal a stream hook gets, and buffering hooks flush on it — sending it raw discards work already paid for
|
|
5
|
+
tags: core, ai-agent
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# The agent `done` event goes through the middleware and is awaited
|
|
9
|
+
|
|
10
|
+
Both the first turn and the post-approval resume send their terminating `done`
|
|
11
|
+
event through the stream middleware rather than straight at the channel, and
|
|
12
|
+
await it.
|
|
13
|
+
|
|
14
|
+
`done` is the only signal a stream hook receives that the reply is over. The
|
|
15
|
+
hooks that buffer need it to flush: `voiceOutput` holds a trailing fragment that
|
|
16
|
+
never reached a full stop, and waits on audio it has already asked the provider
|
|
17
|
+
to synthesize and already been billed for. Sending `done` raw skips those hooks
|
|
18
|
+
entirely, and the `close()` immediately after throws the buffered work away.
|
|
19
|
+
|
|
20
|
+
It matters more on the resume path than the first turn. After an approval, most
|
|
21
|
+
of what gets spoken is the agent describing what it just did — so a dropped
|
|
22
|
+
flush silences the larger half of the reply.
|
|
23
|
+
|
|
24
|
+
**What this rules out:** treating the terminating event as a special case that
|
|
25
|
+
can bypass the chain because "nothing comes after it". Something does: the
|
|
26
|
+
flush.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: decision
|
|
3
|
+
title: The API report pins members, because the export list only pins names
|
|
4
|
+
description: public-surface.json catches an export appearing or vanishing; it cannot see a method added to an interface, which is the change that breaks a consumer's build
|
|
5
|
+
tags: core, api
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# The API report pins members, not just names
|
|
9
|
+
|
|
10
|
+
`public-surface.json` records `Object.keys(module)` for every entry point. That
|
|
11
|
+
is a real guard — it caught a new `./node-host-resolver` subpath and five
|
|
12
|
+
renames during a rebase — but it only sees *names*.
|
|
13
|
+
|
|
14
|
+
It cannot see:
|
|
15
|
+
|
|
16
|
+
- a method added to `MetaService`, which has 32 members
|
|
17
|
+
- a field on `ChannelMeta` becoming required
|
|
18
|
+
- a parameter added to a method on `PikkuWorkflowService`, which has 104
|
|
19
|
+
- any change at all to an `interface`, since types are erased before
|
|
20
|
+
`Object.keys` can enumerate them
|
|
21
|
+
|
|
22
|
+
Those are the changes that break a consumer's build, and there are far more of
|
|
23
|
+
them than there are exports — roughly two members for every name. The report's
|
|
24
|
+
own summary counts both, live, so the figure is not repeated here to rot: an
|
|
25
|
+
earlier hand-count in this note was already wrong by 40 members within a day of
|
|
26
|
+
being written.
|
|
27
|
+
|
|
28
|
+
Demonstrated rather than assumed: adding `probeAddedMember(): void` to
|
|
29
|
+
`MetaService` leaves `public-surface.test.ts` passing 3/0, and fails
|
|
30
|
+
`api-report.test.ts`.
|
|
31
|
+
|
|
32
|
+
So `api-report.md` is generated from the type checker — every exported symbol
|
|
33
|
+
with its full signature, interfaces and classes rendered as their declarations
|
|
34
|
+
so member changes land in the diff. It is committed, and the test fails when
|
|
35
|
+
the code and the report disagree. Regenerate with `yarn api-report`.
|
|
36
|
+
|
|
37
|
+
**What this rules out:** treating `public-surface.json` as the API guard. It
|
|
38
|
+
guards the front door; this guards the rooms. Both are needed, and the export
|
|
39
|
+
list is the cheaper of the two to run — which is why it stayed.
|
|
40
|
+
|
|
41
|
+
Re-exports are resolved through `getAliasedSymbol` before the signature is
|
|
42
|
+
taken. Without that, 624 of the symbols reported as `any`, which would have
|
|
43
|
+
pinned nothing while looking like it did.
|
package/knowledge/decisions/internals/the-ecosystem-entry-point-carries-the-adapter-surface.md
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: decision
|
|
3
|
+
title: The ecosystem entry point carries the adapter surface, so the root can promise stability
|
|
4
|
+
description: runPikkuFunc and the singleton-service accessors are what a runtime adapter reaches for; their signatures move, and the package root should not promise otherwise
|
|
5
|
+
tags: core, api
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# The ecosystem entry point carries the adapter surface
|
|
9
|
+
|
|
10
|
+
`@pikku/core` has two kinds of consumer. An **application** writes functions and
|
|
11
|
+
wires them: `pikkuFunc`, `wireHTTP`, `PikkuWire`, the error catalogue. A
|
|
12
|
+
**runtime adapter** — the express, fastify, uWS, Next, Cloudflare and Lambda
|
|
13
|
+
packages, plus tests — reaches past that to drive the runner itself.
|
|
14
|
+
|
|
15
|
+
Those two surfaces have measurably different stability. Over the nine months
|
|
16
|
+
since `PikkuWire` was introduced, its app-facing shape took 22 additions and
|
|
17
|
+
**zero removals** — every original field is still there. Over the same period
|
|
18
|
+
`runPikkuFunc` was reshaped: `allServices`/`getAllServices` became
|
|
19
|
+
`singletonServices`/`createWireServices`, the positional arguments became a
|
|
20
|
+
`wireType`/`wireId` pair plus an options object, and `session` became `auth`.
|
|
21
|
+
|
|
22
|
+
Both were exported from the package root. A compatibility promise made there
|
|
23
|
+
would therefore have been a promise about the weaker of the two.
|
|
24
|
+
|
|
25
|
+
So the adapter surface moved to `@pikku/core/ecosystem`: `runPikkuFunc`, the
|
|
26
|
+
singleton-service accessors, and the registration calls the code generator emits
|
|
27
|
+
— `addFunction`, `addGlobalMiddleware`, `addMiddleware` — alongside `pikkuState`
|
|
28
|
+
and `httpRouter`, which were already separated for the same reason. The wire
|
|
29
|
+
runners (`runQueueJob`, `runScheduledTask`, `runCLICommand`, `runMCP*`) left the
|
|
30
|
+
root too, but for a different reason: they already lived on their wire subpaths,
|
|
31
|
+
which is where every runtime package imports them from, so the root copies were
|
|
32
|
+
redundant. 108 runtime exports became 95.
|
|
33
|
+
|
|
34
|
+
**The name took two attempts.** Not `/internal`: the generated bootstrap
|
|
35
|
+
imports from here, so the specifier appears in the user's own `.pikku`
|
|
36
|
+
directory, and telling someone they are touching internals when the code
|
|
37
|
+
generator put it there is both wrong and self-defeating — it could never be
|
|
38
|
+
broken anyway. Not `/runtime` either: that reads as runtime-versus-compile-time,
|
|
39
|
+
i.e. _the real API_, which is the opposite of the intended signal, and
|
|
40
|
+
`packages/runtimes/*` already claims the word while `packages/cli` is the
|
|
41
|
+
largest consumer here at 22 files.
|
|
42
|
+
|
|
43
|
+
`/ecosystem` says the true thing: you are building a package in the Pikku
|
|
44
|
+
ecosystem — a runtime, a service, an addon, the CLI. `/internal` remains as an
|
|
45
|
+
alias to the same module because the pinned bootstrap CLI still emits it.
|
|
46
|
+
|
|
47
|
+
Nothing was deleted. What stayed public is what applications actually
|
|
48
|
+
hand-write: `addTagMiddleware` (8 files), `addGlobalPermission`, `fetch` (31),
|
|
49
|
+
`wireAddon`, and the authoring helpers.
|
|
50
|
+
|
|
51
|
+
**What this rules out:** re-exporting anything from `/internal` at the package
|
|
52
|
+
root for convenience. The split is the whole point — `/internal` may change in
|
|
53
|
+
any release, and the root may not. It also rules out treating `/internal` as
|
|
54
|
+
private: it is a published entry point that runtime authors are expected to use,
|
|
55
|
+
just without the compatibility guarantee.
|
|
56
|
+
|
|
57
|
+
`public-surface.json` pins both, so moving a symbol across the line is a visible
|
|
58
|
+
diff rather than an accident.
|
package/knowledge/decisions/internals/the-middleware-resolution-cache-is-deliberately-unbounded.md
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: decision
|
|
3
|
+
title: The middleware resolution cache is deliberately unbounded
|
|
4
|
+
description: Its keyspace is the set of registered wires, not request traffic, and middleware is dynamic — so eviction would buy nothing and cost the dedupe guarantee
|
|
5
|
+
tags: core, middleware
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# The middleware resolution cache is deliberately unbounded
|
|
9
|
+
|
|
10
|
+
`middlewareCache` in `packages/core/src/middleware-runner.ts` is a plain
|
|
11
|
+
`Record<PikkuWiringTypes, Record<string, readonly CorePikkuMiddleware[]>>` with
|
|
12
|
+
no size limit and no eviction. That reads like an unbounded-growth bug and is
|
|
13
|
+
not one.
|
|
14
|
+
|
|
15
|
+
**The keyspace is finite by construction.** The cache is keyed by wire type and
|
|
16
|
+
wire id, and wire ids come from registration, not from traffic — an HTTP route
|
|
17
|
+
pattern, a channel name, a queue name. A million requests to the same route
|
|
18
|
+
produce one entry. Nothing a caller sends creates a key: a channel message key
|
|
19
|
+
is `${channel}:${routingProperty}:${routerValue}`, and `routerValue` is only
|
|
20
|
+
reached after matching `routes[routerValue]`, so an unregistered value never
|
|
21
|
+
gets that far.
|
|
22
|
+
|
|
23
|
+
**Middleware is dynamic, and the cache is invalidated rather than aged out.**
|
|
24
|
+
`addGlobalMiddleware` and `addTagMiddleware` can run after startup, and dev
|
|
25
|
+
hot-reload rewires wholesale — so correctness comes from `clearMiddlewareCache()`
|
|
26
|
+
at the points where the middleware set actually changes, not from entries
|
|
27
|
+
expiring. An LRU would evict entries that are still current while doing nothing
|
|
28
|
+
about entries that are stale.
|
|
29
|
+
|
|
30
|
+
**Eviction would also weaken the chain itself.** `combineMiddleware` returns a
|
|
31
|
+
`freezeDedupe`d array, and dedupe is by function identity — a middleware
|
|
32
|
+
reachable through both a tag group and a direct wire registration runs exactly
|
|
33
|
+
once. Callers hold that array. Recomputing it under memory pressure hands out a
|
|
34
|
+
second array for the same wire, which is churn at best and, for anything
|
|
35
|
+
comparing chains by identity, a silent behaviour change.
|
|
36
|
+
|
|
37
|
+
**What this rules out:** adding a `MIDDLEWARE_CACHE_MAX` with LRU eviction as a
|
|
38
|
+
denial-of-service mitigation. There is no traffic-driven growth to mitigate. If
|
|
39
|
+
a future change ever keys this cache by something a caller controls, that is the
|
|
40
|
+
bug — bound the key, not the cache.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: decision
|
|
3
|
+
title: The per-invocation rpc view is a class, because an object literal with a getter is slow to build
|
|
4
|
+
description: An accessor declared on an object literal is defined per instance, which drops the literal off V8's fast construction path — measured at 1.15µs against 0.47µs, on every request
|
|
5
|
+
tags: core, rpc, performance
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# The per-invocation rpc view is a class
|
|
9
|
+
|
|
10
|
+
`getContextRPCService` builds `wire.rpc` once per invocation. It used to return
|
|
11
|
+
an object literal, and that literal declared `get agent()` so the agent facade
|
|
12
|
+
stayed lazy — most requests never touch it, and reading it builds five closures.
|
|
13
|
+
|
|
14
|
+
The laziness is right. Declaring the accessor *on the literal* was not: a
|
|
15
|
+
literal containing an accessor needs a real property descriptor per instance,
|
|
16
|
+
which takes it off V8's fast object-literal construction path and slows the
|
|
17
|
+
whole object, not just the accessor.
|
|
18
|
+
|
|
19
|
+
Measured with `benchmarks/bench-profile-granular.ts`, three runs each:
|
|
20
|
+
|
|
21
|
+
| shape | per call |
|
|
22
|
+
| --- | --- |
|
|
23
|
+
| object literal with `get agent()` | 1.106 / 1.433 / 1.147 µs |
|
|
24
|
+
| class with `agent` on the prototype | 0.523 / 0.461 / 0.525 µs |
|
|
25
|
+
|
|
26
|
+
Roughly 2.4×, well outside the run-to-run variance, on a path every request
|
|
27
|
+
takes. On the same machine a full `fetchData` measures 12–19µs, so this was on
|
|
28
|
+
the order of a tenth of a request spent constructing one object.
|
|
29
|
+
|
|
30
|
+
The obvious alternative is worse. Making `agent` an eager property removes the
|
|
31
|
+
accessor but builds those five closures unconditionally, and measured *slower*
|
|
32
|
+
than the original at 1.967µs. A prototype accessor is the only shape that keeps
|
|
33
|
+
the laziness and the fast construction path.
|
|
34
|
+
|
|
35
|
+
**What this rules out:** "simplifying" `ContextRPCView` back to an object
|
|
36
|
+
literal. It reads as the plainer option and costs double. It also rules out
|
|
37
|
+
trusting a synthetic microbenchmark here — a standalone benchmark of these
|
|
38
|
+
shapes pointed at the eager version, because its stand-in for the agent getter
|
|
39
|
+
was too cheap to represent the real one. The numbers above come from the
|
|
40
|
+
profiler running the real code.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: decision
|
|
3
|
+
title: The persona runtime is exported from @pikku/core/persona, never from services
|
|
4
|
+
description: Those values reach the actor-flow and agent runners, which no production server runs — and an unbundled deploy loads whatever the import graph names
|
|
5
|
+
tags: core, services
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# The persona runtime lives on `@pikku/core/persona`
|
|
9
|
+
|
|
10
|
+
`HttpPersona`, `createHttpPersonas`, `readScenarioHttpResponse` and
|
|
11
|
+
`postScenarioJson` are exported from `@pikku/core/persona`, not from
|
|
12
|
+
`@pikku/core/services`. Their *types* are re-exported freely — TypeScript erases
|
|
13
|
+
those and they cost a bundle nothing.
|
|
14
|
+
|
|
15
|
+
The values are different. They reach `http-personas`, which reaches the
|
|
16
|
+
actor-flow conversation runner, which reaches the agent runner: an entire
|
|
17
|
+
scenario and virtual-user runtime that no production server executes. An
|
|
18
|
+
unbundled Node or Lambda deploy does no tree-shaking — it loads whatever the
|
|
19
|
+
import graph names — so exporting one value from the services barrel pulls all
|
|
20
|
+
of it into every application that imports a service.
|
|
21
|
+
|
|
22
|
+
This is enforced, not merely intended: `production-barrels-stay-lean.test.ts`
|
|
23
|
+
walks the value-import graph from `services/index.ts` and
|
|
24
|
+
`wirings/workflow/index.ts` and fails if any of those modules is reachable.
|
|
25
|
+
|
|
26
|
+
**What this rules out:** re-exporting a persona value from the services barrel
|
|
27
|
+
for convenience, and "just one small helper" — the graph is transitive, and one
|
|
28
|
+
value is enough to pull the whole runtime.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: decision
|
|
3
|
+
title: A voice turn's transcript is sent before the run starts, on the raw channel
|
|
4
|
+
description: The client does not know what it said, and an answer starts streaming within a few hundred milliseconds — a question arriving after its answer reads as the wrong question
|
|
5
|
+
tags: core, ai-agent
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# A voice turn's transcript is sent ahead of the run
|
|
9
|
+
|
|
10
|
+
A voice client sends audio, so it does not know what it said. Until the
|
|
11
|
+
transcript reaches it, its own message renders as a blank bubble.
|
|
12
|
+
|
|
13
|
+
The event is sent *before* the run rather than alongside it because the answer
|
|
14
|
+
begins streaming within a few hundred milliseconds. Sent concurrently, the
|
|
15
|
+
transcript routinely lands after the first tokens of its own answer — and a
|
|
16
|
+
question that appears beneath its answer reads as a question about something
|
|
17
|
+
else entirely.
|
|
18
|
+
|
|
19
|
+
It goes on the raw channel rather than through the stream middleware because it
|
|
20
|
+
is not part of the reply: it is what the user said, and the stream hooks are
|
|
21
|
+
built to transform what the agent says.
|
|
22
|
+
|
|
23
|
+
**What this rules out:** folding the transcript into the run's event stream for
|
|
24
|
+
uniformity. Ordering is the whole point, and the stream is where ordering is
|
|
25
|
+
least under this code's control.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: decision
|
|
3
|
+
title: The catalogue is the only gate on what a virtual user may call
|
|
4
|
+
description: Narrowing happens once at derivation, so there is no second check in the run loop that could drift out of step with it
|
|
5
|
+
tags: core, virtual-user
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# The catalogue is the only gate on what a virtual user may call
|
|
9
|
+
|
|
10
|
+
What a virtual user may invoke is decided once, when the catalogue is narrowed
|
|
11
|
+
for its disposition: a read-only disposition is never offered a mutation, and an
|
|
12
|
+
approval-gated endpoint is never offered at all.
|
|
13
|
+
|
|
14
|
+
The run loop then indexes into that catalogue and calls what it finds. There is
|
|
15
|
+
deliberately no second permission check at call time, because a second check is
|
|
16
|
+
a second thing to keep correct — and the failure mode of the two disagreeing is
|
|
17
|
+
that one of them silently stops mattering.
|
|
18
|
+
|
|
19
|
+
**What this rules out:** adding a "defence in depth" guard in the run loop. It
|
|
20
|
+
would not be defence in depth; it would be a duplicate of the derivation rules,
|
|
21
|
+
maintained separately, and the one that runs first wins.
|
package/knowledge/decisions/internals/the-worker-disposition-is-the-one-that-is-not-testing.md
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: decision
|
|
3
|
+
title: The worker disposition is the only one not testing anything
|
|
4
|
+
description: It is doing the job, so it abandons rarely, runs cool, and is told to stop and say so rather than guess
|
|
5
|
+
tags: core, virtual-user
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# The worker disposition is the only one not testing anything
|
|
9
|
+
|
|
10
|
+
Every other disposition exists to probe the product. The worker exists to *use*
|
|
11
|
+
it, which changes every parameter: it abandons rarely, runs at a low temperature,
|
|
12
|
+
and is instructed to stop and report rather than guess its way past an obstacle.
|
|
13
|
+
|
|
14
|
+
The failure mode being guarded against is different too. For the probing
|
|
15
|
+
dispositions the risk is a missed bug. For this one it is a wrong action nobody
|
|
16
|
+
asked for — a real mutation performed because the model improvised when it
|
|
17
|
+
should have stopped.
|
|
18
|
+
|
|
19
|
+
**What this rules out:** tuning this disposition toward the others for
|
|
20
|
+
consistency, in particular raising its temperature or its tolerance for
|
|
21
|
+
guessing. Those settings are what make the others useful and what would make this
|
|
22
|
+
one dangerous.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: decision
|
|
3
|
+
title: Thread history records what the model was asked, which for a spoken turn is the transcript
|
|
4
|
+
description: The wire carried a base64 audio blob; persisting it writes megabytes of unreadable data and loses the only readable record of the turn
|
|
5
|
+
tags: core, ai-agent
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Thread history records the transcript, not the audio that arrived
|
|
9
|
+
|
|
10
|
+
What goes into thread history is what the model was actually asked. For a typed
|
|
11
|
+
turn that is what arrived over the wire. For a spoken turn it is not: the wire
|
|
12
|
+
carried a base64 audio blob, and `voiceInput` replaced it with a transcript
|
|
13
|
+
before the model ever saw it.
|
|
14
|
+
|
|
15
|
+
Persisting the blob would write megabytes of unreadable data into the history
|
|
16
|
+
*and* discard the only readable record of what was said — the worst of both.
|
|
17
|
+
|
|
18
|
+
Both the streaming and non-streaming paths do this, and both check identity
|
|
19
|
+
rather than assuming: a middleware is free to rewrite the message list into
|
|
20
|
+
something with no relation to this turn, and in that case there is no transcript
|
|
21
|
+
to substitute and the list is persisted as-is.
|
|
22
|
+
|
|
23
|
+
**What this rules out:** persisting the inbound message verbatim on the grounds
|
|
24
|
+
that it is the ground truth. For voice it is the least useful representation
|
|
25
|
+
available, and the substitution is exactly what makes the history readable.
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: decision
|
|
3
|
+
title: pikku validate runs every applicable check, it does not detect a project kind
|
|
4
|
+
description: one `pikku validate` replaces `pikku workspace validate`; checks declare a precondition and run wherever it holds, because the repos that matter are an app and a pile of addons at the same time
|
|
5
|
+
tags: cli, validate, addons
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# pikku validate runs every applicable check, it does not detect a project kind
|
|
9
|
+
|
|
10
|
+
`pikku workspace validate` was named after one of the things a repo can be.
|
|
11
|
+
Adding a second — "is this addon publishable" — invited a second command,
|
|
12
|
+
`pikku addon validate`, and that is where the naming falls apart: the addons
|
|
13
|
+
repo is a workspace *containing* 217 publishable addons. Standing at its root,
|
|
14
|
+
`pikku addon validate` would have to refuse or sweep every package, at which
|
|
15
|
+
point it is doing the workspace thing anyway. The two nouns were never
|
|
16
|
+
alternatives.
|
|
17
|
+
|
|
18
|
+
So there is one command, and it does not detect a kind and dispatch. Each check
|
|
19
|
+
declares the condition under which it means anything, and runs wherever that
|
|
20
|
+
condition holds:
|
|
21
|
+
|
|
22
|
+
- `app-project` — a `pikku.config.json` with no `types/application-types.d.ts`
|
|
23
|
+
beside it. The marker matters: an addon carries a `pikku.config.json` too, and
|
|
24
|
+
the app-shaped checks would report every app convention it has no reason to
|
|
25
|
+
follow, starting with a `packages/functions/` it will never have.
|
|
26
|
+
- `addon-package` — a non-private package that ships generated pikku output.
|
|
27
|
+
Having a `.pikku` directory is not enough on its own: an app's
|
|
28
|
+
`packages/functions` has one, and it is codegen for that app rather than
|
|
29
|
+
something anyone installs. The signal is a `files`/`exports` that carries it
|
|
30
|
+
into the tarball.
|
|
31
|
+
|
|
32
|
+
Detection-as-dispatch fails on the first repo that is two things at once.
|
|
33
|
+
Detection-as-precondition composes: the addons repo plans 217 addon checks and
|
|
34
|
+
no app check; the online-shop template plans one app check and no addon check;
|
|
35
|
+
a repo that is both plans both.
|
|
36
|
+
|
|
37
|
+
## Targets come from walking, not from `workspaces`
|
|
38
|
+
|
|
39
|
+
The field is an array in one repo and `{ packages: [...] }` in the next, spells
|
|
40
|
+
the same layout as `packages/**` or as six explicit globs, and a package that is
|
|
41
|
+
real but unlisted is exactly the kind of thing worth validating. So discovery
|
|
42
|
+
walks the tree for `package.json` files and lets the preconditions decide.
|
|
43
|
+
Over-collecting targets is free; guessing the glob dialect is not.
|
|
44
|
+
|
|
45
|
+
## A run that checked nothing must not print a tick
|
|
46
|
+
|
|
47
|
+
The failure mode of auto-detection is that finding nothing looks exactly like
|
|
48
|
+
finding everything and liking it. When no check applies, the renderer says so
|
|
49
|
+
and names what it looked for, rather than reporting success.
|
|
50
|
+
|
|
51
|
+
## The check the addon case exists for
|
|
52
|
+
|
|
53
|
+
Every relative import in a shipped generated file must resolve to a file that is
|
|
54
|
+
itself shipped — stated as a property, not as "the build script must copy
|
|
55
|
+
`types/`". The build script is not the only way to get this wrong and will not
|
|
56
|
+
always be a `cp`; the generated files already declare what they need.
|
|
57
|
+
|
|
58
|
+
That property was false in all 217 published addons. `cp -r .pikku dist/`
|
|
59
|
+
shipped the generated output but not the `types/application-types.d.ts` it
|
|
60
|
+
imports, and `tsc` never emits a hand-written `.d.ts` to `outDir`, so nothing
|
|
61
|
+
put it in `dist`. Consumers got 8 unresolved-module errors plus 6
|
|
62
|
+
`RequiredServices does not satisfy CoreSecretlessSingletonServices` follow-ons
|
|
63
|
+
inside `node_modules`, for merely depending on an addon. The scaffold template
|
|
64
|
+
had been right the whole time — `templates/function-addon` copies `types/` —
|
|
65
|
+
and the published packages had drifted from it with nothing watching.
|
|
66
|
+
|
|
67
|
+
## The check walks whichever generated directory a package ships
|
|
68
|
+
|
|
69
|
+
`dist/.pikku` is the target shape (below), but the check cannot assume it: the
|
|
70
|
+
shape is what this work moved addons *to*, and a package that has not moved —
|
|
71
|
+
or was published before it did — still carries `.pikku` at the root, listed in
|
|
72
|
+
`files` and mapped by `exports` as `./.pikku/*`. There it is a public entry
|
|
73
|
+
point rather than build input, and its imports climb one level fewer — to
|
|
74
|
+
`<pkg>/src` and `<pkg>/types` — where the copy under `dist` reaches
|
|
75
|
+
`<pkg>/dist/src` and `<pkg>/dist/types`. Two roots, two ways to fall outside
|
|
76
|
+
the tarball, so the check walks whichever ones are actually shipped rather than
|
|
77
|
+
the one it would prefer to find.
|
|
78
|
+
|
|
79
|
+
`exports` and `imports` get the same treatment one level up: a target outside
|
|
80
|
+
the published file set is the same defect, and the one the import walk cannot
|
|
81
|
+
see, because nothing inside `dist/.pikku` mentions it. That is how a `#pikku`
|
|
82
|
+
still pointing at `./.pikku/pikku-types.gen.ts` hides.
|
|
83
|
+
|
|
84
|
+
## An addon's entry points all resolve under dist
|
|
85
|
+
|
|
86
|
+
Everything an installed package reaches for lives under `dist`; the addon's own
|
|
87
|
+
build resolves `#pikku` through tsconfig `paths`, so nothing in `exports` or
|
|
88
|
+
`imports` has to point into the source tree, and `files` is just `["dist"]`.
|
|
89
|
+
The alternative — shipping `src/` and `types/` at the root so the existing paths
|
|
90
|
+
resolve as written — publishes TypeScript source and a second copy of
|
|
91
|
+
everything `dist` already has.
|
|
92
|
+
|
|
93
|
+
Checking only `dist` is what let the root `.pikku` stay broken through the first
|
|
94
|
+
round of fixes: in the published `@pikku/addon-assemblyai@0.1.4` tarball, `.pikku`
|
|
95
|
+
ships `.gen.ts` files importing a `../../src/` and `../types/` that the tarball
|
|
96
|
+
does not contain, and the `pikku-bootstrap.gen.js` that consumers import through
|
|
97
|
+
that subpath exists only under `dist`. Everything resolved locally through the
|
|
98
|
+
workspace link and none of it resolved on install.
|
|
99
|
+
|
|
100
|
+
## Only a package that publishes gets the dist shape
|
|
101
|
+
|
|
102
|
+
The shape describes a tarball, so it means nothing for a `private` package —
|
|
103
|
+
and applying it there actively breaks: `exports` *is* enforced across a
|
|
104
|
+
workspace link, so repointing a private fixture at `dist` makes every consumer
|
|
105
|
+
demand a directory that only a build produces. The three `verifiers/db-schema`
|
|
106
|
+
and `verifiers/addon-registry` fixtures have no build script at all, so `dist`
|
|
107
|
+
never exists for them; the five `e2e/packages` addons build, but their metadata
|
|
108
|
+
is read straight from the source tree before any build has run. Repointing all
|
|
109
|
+
eight left the db-schema verifier unable to resolve
|
|
110
|
+
`dist/.pikku/db/pikku-db-meta.gen.json` and every e2e addon reporting "no
|
|
111
|
+
function metadata".
|
|
112
|
+
|
|
113
|
+
`isAddonPackage` already draws this line — it returns false for `private` — so
|
|
114
|
+
the checks and the shape agree: a package the registry never sees is consumed
|
|
115
|
+
from source.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: decision
|
|
3
|
+
title: A scenario's step order for a virtual user is insertion order, not a graph traversal
|
|
4
|
+
description: The CLI writes nodes in declaration order, so following `next` would buy an ordering that is already true and cost a traversal that has to interpret branches
|
|
5
|
+
tags: core, virtual-user
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Step order comes from insertion order, not a graph traversal
|
|
9
|
+
|
|
10
|
+
When a scenario becomes catalogue entries, its steps are read in the order the
|
|
11
|
+
nodes appear — which is the order the CLI wrote them, which is the order the
|
|
12
|
+
scenario declares them.
|
|
13
|
+
|
|
14
|
+
Following `next` edges instead would produce the same ordering in the ordinary
|
|
15
|
+
case, and in the branching case it would force a decision nobody needs: which
|
|
16
|
+
arm of a branch to present to a reader who will never take one. The traversal
|
|
17
|
+
costs work to reproduce a fact that already holds.
|
|
18
|
+
|
|
19
|
+
**What this rules out:** "correcting" this to a topological walk because a graph
|
|
20
|
+
is present. The graph describes execution; the catalogue describes what the
|
|
21
|
+
scenario *says*, and those are read in declaration order.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: decision
|
|
3
|
+
title: Only an explicit `false` silences a spoken reply
|
|
4
|
+
description: The key being absent means no voice input is wired and nothing reported either way, so those callers keep the behaviour they had before the option existed
|
|
5
|
+
tags: core, ai-agent
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Only an explicit `false` silences a spoken reply
|
|
9
|
+
|
|
10
|
+
`voiceOutput` speaks a reply unless the turn is explicitly marked as not having
|
|
11
|
+
arrived by voice. The distinction is between three states, not two:
|
|
12
|
+
|
|
13
|
+
- `true` — `voiceInput` handled this turn and it carried speech.
|
|
14
|
+
- `false` — `voiceInput` handled this turn and a real user really typed it.
|
|
15
|
+
- absent — no voice input is wired at all, so nothing reported either way.
|
|
16
|
+
|
|
17
|
+
Treating absent as `false` would silence every caller who wired `voiceOutput`
|
|
18
|
+
without `voiceInput`, changing behaviour that worked before the option existed.
|
|
19
|
+
Treating absent as `true` would speak replies to typists on any stack where the
|
|
20
|
+
flag never gets set.
|
|
21
|
+
|
|
22
|
+
**What this rules out:** normalising the flag to a boolean at any point between
|
|
23
|
+
`voiceInput` and `voiceOutput` — that collapses absent into one of the other two
|
|
24
|
+
and picks a wrong answer for somebody.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: decision
|
|
3
|
+
title: Wiring registries erase the generics their wire* functions capture
|
|
4
|
+
description: A registry is typed at unknown so every wiring shares one map; storing a generic config in it needs an assertion, because function parameters are contravariant
|
|
5
|
+
tags: core
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Wiring registries erase the generics their `wire*` functions capture
|
|
9
|
+
|
|
10
|
+
Every `wire*` entry point — `wireTrigger`, `wireTriggerSource`, `wireChannel`,
|
|
11
|
+
`wireQueueWorker`, and the rest — is generic in the shape a caller declares, so
|
|
12
|
+
the caller gets a checked `func`. The registry each one writes to is not:
|
|
13
|
+
`pikkuState(null, 'trigger', 'triggers')` is a
|
|
14
|
+
`Map<string, CoreTriggerSource>` where `CoreTriggerSource` fixes input and
|
|
15
|
+
output at `unknown`, because one map has to hold every trigger in the app.
|
|
16
|
+
|
|
17
|
+
Handing `CoreTriggerSource<TInput, TOutput>` to a slot typed
|
|
18
|
+
`CoreTriggerSource<unknown, unknown>` is not an upcast. `func` takes its input
|
|
19
|
+
as a *parameter*, and parameters are contravariant: a function that accepts
|
|
20
|
+
`TInput` cannot stand in for one that accepts `unknown`, since `unknown` admits
|
|
21
|
+
values `TInput` does not. TypeScript is right to reject it, and no variance
|
|
22
|
+
annotation makes it go away — the registry genuinely holds functions whose input
|
|
23
|
+
types it cannot name.
|
|
24
|
+
|
|
25
|
+
The runtime is nonetheless sound, because the only thing that ever invokes a
|
|
26
|
+
registered `func` is the function runner, which validates the incoming data
|
|
27
|
+
against that function's own generated schema before the call. The type the
|
|
28
|
+
registry lost is re-established at the call boundary by the schema, not by the
|
|
29
|
+
type system.
|
|
30
|
+
|
|
31
|
+
**What this rules out:** deleting these assertions as if they were noise — the
|
|
32
|
+
code does not compile without them. It equally rules out `as any` in their
|
|
33
|
+
place: `as any` discards the target type too, so a genuinely wrong config would
|
|
34
|
+
also slip through. Assert to the registry's own element type and let everything
|
|
35
|
+
except the erased generic stay checked.
|