@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,76 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
PikkuAIScorer,
|
|
3
|
+
ScorerInput,
|
|
4
|
+
ScorerOutput,
|
|
5
|
+
} from './ai-scorer.types.js'
|
|
6
|
+
|
|
7
|
+
const assertSampleRate = (name: string, sampleRate: number | undefined) => {
|
|
8
|
+
if (sampleRate === undefined) return 1
|
|
9
|
+
if (sampleRate < 0 || sampleRate > 1) {
|
|
10
|
+
throw new Error(
|
|
11
|
+
`Scorer '${name}' has a sampleRate of ${sampleRate} — it is a fraction of live runs to grade, so it must be between 0 and 1`
|
|
12
|
+
)
|
|
13
|
+
}
|
|
14
|
+
return sampleRate
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* A heuristic scorer: pure code over the finished run, no model call, so it
|
|
19
|
+
* grades on the fast lane.
|
|
20
|
+
*/
|
|
21
|
+
export const pikkuAIScorer = <Services = any>(config: {
|
|
22
|
+
name: string
|
|
23
|
+
description: string
|
|
24
|
+
/** 0..1 fraction of live runs to grade. Defaults to all of them. */
|
|
25
|
+
sampleRate?: number
|
|
26
|
+
/**
|
|
27
|
+
* Grades against a known-correct answer. Such a scorer is test-only — live
|
|
28
|
+
* traffic has no answer key, so the runtime never samples it.
|
|
29
|
+
*/
|
|
30
|
+
requiresReference?: boolean
|
|
31
|
+
score: (
|
|
32
|
+
input: ScorerInput,
|
|
33
|
+
services: Services
|
|
34
|
+
) => ScorerOutput | Promise<ScorerOutput>
|
|
35
|
+
}): PikkuAIScorer<Services> => ({
|
|
36
|
+
name: config.name,
|
|
37
|
+
description: config.description,
|
|
38
|
+
lane: 'fast',
|
|
39
|
+
sampleRate: assertSampleRate(config.name, config.sampleRate),
|
|
40
|
+
requiresReference: config.requiresReference ?? false,
|
|
41
|
+
score: config.score,
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* An LLM-judge scorer: the runtime makes the model call and forces a structured
|
|
46
|
+
* `{ score, reason }`, so a judge is a rubric rather than a prompt to parse.
|
|
47
|
+
*
|
|
48
|
+
* The rubric field is `goal`, matching `pikkuAIAgent`'s prompt vocabulary — a
|
|
49
|
+
* judge is a degenerate agent, and should use the same word for the same thing.
|
|
50
|
+
* `prompt` is the escape hatch for non-standard framing.
|
|
51
|
+
*/
|
|
52
|
+
export const pikkuAIJudge = <Services = any>(config: {
|
|
53
|
+
name: string
|
|
54
|
+
description: string
|
|
55
|
+
/** 0..1 fraction of live runs to grade. Defaults to all of them. */
|
|
56
|
+
sampleRate?: number
|
|
57
|
+
/**
|
|
58
|
+
* Grades against a known-correct answer. Such a judge is test-only — live
|
|
59
|
+
* traffic has no answer key, so the runtime never samples it.
|
|
60
|
+
*/
|
|
61
|
+
requiresReference?: boolean
|
|
62
|
+
model: string
|
|
63
|
+
goal: string
|
|
64
|
+
prompt?: (input: ScorerInput) => string
|
|
65
|
+
}): PikkuAIScorer<Services> => ({
|
|
66
|
+
name: config.name,
|
|
67
|
+
description: config.description,
|
|
68
|
+
lane: 'slow',
|
|
69
|
+
sampleRate: assertSampleRate(config.name, config.sampleRate),
|
|
70
|
+
requiresReference: config.requiresReference ?? false,
|
|
71
|
+
judge: {
|
|
72
|
+
model: config.model,
|
|
73
|
+
goal: config.goal,
|
|
74
|
+
...(config.prompt ? { prompt: config.prompt } : {}),
|
|
75
|
+
},
|
|
76
|
+
})
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a scorer is shown about a finished run.
|
|
3
|
+
*
|
|
4
|
+
* One snapshot, taken after the output middleware has resolved, so a scorer and
|
|
5
|
+
* the persisted run record see the same thing — including the redactions.
|
|
6
|
+
*/
|
|
7
|
+
export interface ScorerInput {
|
|
8
|
+
runId: string
|
|
9
|
+
agentName: string
|
|
10
|
+
threadId?: string
|
|
11
|
+
resourceId?: string
|
|
12
|
+
/** The user prompt the run answered. */
|
|
13
|
+
input: string
|
|
14
|
+
/** The agent's final text answer. */
|
|
15
|
+
output: string
|
|
16
|
+
/**
|
|
17
|
+
* The known-correct answer. Supplied by a scenario for a reference-based
|
|
18
|
+
* judge; never set on live traffic, which has no answer key.
|
|
19
|
+
*/
|
|
20
|
+
reference?: string
|
|
21
|
+
toolCalls: {
|
|
22
|
+
name: string
|
|
23
|
+
args: unknown
|
|
24
|
+
result?: unknown
|
|
25
|
+
error?: string
|
|
26
|
+
}[]
|
|
27
|
+
usage: {
|
|
28
|
+
inputTokens: number
|
|
29
|
+
outputTokens: number
|
|
30
|
+
model?: string
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface ScorerOutput {
|
|
35
|
+
/** 0..1, so grades are comparable across scorers. */
|
|
36
|
+
score: number
|
|
37
|
+
reason?: string
|
|
38
|
+
metadata?: Record<string, unknown>
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* The queue a scorer's jobs go to. Two lanes exist only so a flood of slow
|
|
43
|
+
* LLM-judge jobs cannot starve the cheap heuristic ones; within a lane it is
|
|
44
|
+
* plain FIFO.
|
|
45
|
+
*/
|
|
46
|
+
export type ScorerLane = 'fast' | 'slow'
|
|
47
|
+
|
|
48
|
+
export const SCORER_LANE_QUEUES: Record<ScorerLane, string> = {
|
|
49
|
+
fast: 'ai-score-fast',
|
|
50
|
+
slow: 'ai-score-slow',
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* A judge's model call. Present only on a scorer built with `pikkuAIJudge`;
|
|
55
|
+
* `score` is present only on one built with `pikkuAIScorer`. Exactly one of the
|
|
56
|
+
* two is set, which is what the two constructors exist to guarantee.
|
|
57
|
+
*/
|
|
58
|
+
export type ScorerJudgeConfig = {
|
|
59
|
+
model: string
|
|
60
|
+
goal: string
|
|
61
|
+
prompt?: (input: ScorerInput) => string
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export type PikkuAIScorer<Services = any> = {
|
|
65
|
+
name: string
|
|
66
|
+
description: string
|
|
67
|
+
lane: ScorerLane
|
|
68
|
+
/** 0..1 fraction of live runs to grade. */
|
|
69
|
+
sampleRate: number
|
|
70
|
+
/**
|
|
71
|
+
* Grades against a known-correct answer, so it is test-only: live traffic has
|
|
72
|
+
* no answer key and the runtime never samples it.
|
|
73
|
+
*/
|
|
74
|
+
requiresReference: boolean
|
|
75
|
+
score?: (
|
|
76
|
+
input: ScorerInput,
|
|
77
|
+
services: Services
|
|
78
|
+
) => ScorerOutput | Promise<ScorerOutput>
|
|
79
|
+
judge?: ScorerJudgeConfig
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export type ScorerMeta = Record<
|
|
83
|
+
string,
|
|
84
|
+
{
|
|
85
|
+
name: string
|
|
86
|
+
description: string
|
|
87
|
+
lane: ScorerLane
|
|
88
|
+
sampleRate: number
|
|
89
|
+
requiresReference: boolean
|
|
90
|
+
sourceFile?: string
|
|
91
|
+
exportedName?: string
|
|
92
|
+
}
|
|
93
|
+
>
|
|
94
|
+
|
|
95
|
+
/** A single scorer's job on a lane queue. */
|
|
96
|
+
export type ScoreJob = ScorerInput & {
|
|
97
|
+
scorerName: string
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export type AIRunScore = {
|
|
101
|
+
runId: string
|
|
102
|
+
scorerName: string
|
|
103
|
+
score: number
|
|
104
|
+
reason?: string
|
|
105
|
+
metadata?: Record<string, unknown>
|
|
106
|
+
createdAt: Date
|
|
107
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export { pikkuAIScorer, pikkuAIJudge } from './ai-scorer.js'
|
|
2
|
+
export {
|
|
3
|
+
addAIScorer,
|
|
4
|
+
getAIScorers,
|
|
5
|
+
getAIScorersMeta,
|
|
6
|
+
} from './ai-scorer-registry.js'
|
|
7
|
+
export { gradeRun } from './ai-scorer-grade.js'
|
|
8
|
+
export {
|
|
9
|
+
enableScoreSnapshots,
|
|
10
|
+
getScoreSnapshot,
|
|
11
|
+
} from './ai-scorer-snapshots.js'
|
|
12
|
+
export {
|
|
13
|
+
wireAIScorerQueueWorkers,
|
|
14
|
+
} from './ai-scorer-worker.js'
|
|
15
|
+
export type {
|
|
16
|
+
AIRunScore,
|
|
17
|
+
PikkuAIScorer,
|
|
18
|
+
ScoreJob,
|
|
19
|
+
ScorerInput,
|
|
20
|
+
ScorerJudgeConfig,
|
|
21
|
+
ScorerLane,
|
|
22
|
+
ScorerMeta,
|
|
23
|
+
ScorerOutput,
|
|
24
|
+
} from './ai-scorer.types.js'
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { CorePikkuChannelMiddleware } from './channel.types.js'
|
|
1
2
|
import type {
|
|
2
3
|
CoreSingletonServices,
|
|
3
4
|
CorePikkuMiddleware,
|
|
@@ -61,7 +62,9 @@ export const runChannelLifecycleWithMiddleware = async ({
|
|
|
61
62
|
`${channelConfig.name}:${lifecycleType}:cm`,
|
|
62
63
|
{
|
|
63
64
|
wireInheritedChannelMiddleware: channelMiddlewareMeta,
|
|
64
|
-
|
|
65
|
+
// knowledge: questions/channel-middleware-accepts-bare-factories-that-nothing-resolves.md
|
|
66
|
+
wireChannelMiddleware:
|
|
67
|
+
channelConfig.channelMiddleware as CorePikkuChannelMiddleware[],
|
|
65
68
|
}
|
|
66
69
|
)
|
|
67
70
|
|
|
@@ -76,7 +79,7 @@ export const runChannelLifecycleWithMiddleware = async ({
|
|
|
76
79
|
const runLifecycle = async () => {
|
|
77
80
|
return await runPikkuFunc('channel', channelConfig.name, meta.pikkuFuncId, {
|
|
78
81
|
singletonServices: services,
|
|
79
|
-
data: () => data
|
|
82
|
+
data: () => data,
|
|
80
83
|
wire,
|
|
81
84
|
sessionService: userSession,
|
|
82
85
|
tags: meta.tags ?? [],
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { describe, test } from 'node:test'
|
|
2
|
+
import assert from 'node:assert/strict'
|
|
3
|
+
|
|
4
|
+
import { pikkuState, resetPikkuState } from '../../pikku-state.js'
|
|
5
|
+
import { wireChannel } from './channel-runner.js'
|
|
6
|
+
|
|
7
|
+
const registeredFunc = (funcId: string) =>
|
|
8
|
+
pikkuState(null, 'function', 'functions').get(funcId)
|
|
9
|
+
|
|
10
|
+
const wire = (channel: any) => {
|
|
11
|
+
resetPikkuState()
|
|
12
|
+
pikkuState(null, 'channel', 'meta', {
|
|
13
|
+
shapes: {
|
|
14
|
+
name: 'shapes',
|
|
15
|
+
route: '/shapes',
|
|
16
|
+
input: null,
|
|
17
|
+
connect: { pikkuFuncId: 'shapes__connect' },
|
|
18
|
+
disconnect: { pikkuFuncId: 'shapes__disconnect' },
|
|
19
|
+
message: { pikkuFuncId: 'shapes__message' },
|
|
20
|
+
messageWirings: {},
|
|
21
|
+
},
|
|
22
|
+
} as any)
|
|
23
|
+
wireChannel({ name: 'shapes', route: '/shapes', ...channel })
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* knowledge: decisions/internals/channel-message-handlers-accept-three-config-shapes.md
|
|
28
|
+
*/
|
|
29
|
+
describe('every channel handler shape registers a callable function config', () => {
|
|
30
|
+
const handler = async () => undefined
|
|
31
|
+
|
|
32
|
+
test('a direct function config registers as-is', () => {
|
|
33
|
+
wire({ onConnect: { func: handler } })
|
|
34
|
+
|
|
35
|
+
assert.equal(typeof registeredFunc('shapes__connect')?.func, 'function')
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
test('a wrapper around a function config registers the inner config', () => {
|
|
39
|
+
wire({ onConnect: { func: { func: handler }, middleware: [] } })
|
|
40
|
+
|
|
41
|
+
// Registering the wrapper itself leaves `.func` an object, and the function
|
|
42
|
+
// runner calls `.func(...)` — that is a TypeError at connect time.
|
|
43
|
+
assert.equal(
|
|
44
|
+
typeof registeredFunc('shapes__connect')?.func,
|
|
45
|
+
'function',
|
|
46
|
+
'the wrapper was registered instead of the config it wraps'
|
|
47
|
+
)
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
test('onDisconnect unwraps the same way', () => {
|
|
51
|
+
wire({ onDisconnect: { func: { func: handler }, middleware: [] } })
|
|
52
|
+
|
|
53
|
+
assert.equal(typeof registeredFunc('shapes__disconnect')?.func, 'function')
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
test('onMessage unwraps the same way', () => {
|
|
57
|
+
wire({ onMessage: { func: { func: handler } } })
|
|
58
|
+
|
|
59
|
+
assert.equal(typeof registeredFunc('shapes__message')?.func, 'function')
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
test('a simple wrapper — a function plus sibling middleware — stays whole', () => {
|
|
63
|
+
wire({ onConnect: { func: handler, middleware: [] } })
|
|
64
|
+
|
|
65
|
+
const registered = registeredFunc('shapes__connect')
|
|
66
|
+
assert.equal(typeof registered?.func, 'function')
|
|
67
|
+
assert.ok(
|
|
68
|
+
'middleware' in (registered as object),
|
|
69
|
+
'the sibling middleware must survive registration'
|
|
70
|
+
)
|
|
71
|
+
})
|
|
72
|
+
})
|
|
@@ -4,6 +4,7 @@ import type {
|
|
|
4
4
|
CoreUserSession,
|
|
5
5
|
} from '../../types/core.types.js'
|
|
6
6
|
import type {
|
|
7
|
+
CorePikkuChannelMiddleware,
|
|
7
8
|
BinaryData,
|
|
8
9
|
ChannelMessageMeta,
|
|
9
10
|
CoreChannel,
|
|
@@ -94,7 +95,7 @@ export const processMessageHandlers = (
|
|
|
94
95
|
logger.error(
|
|
95
96
|
`Channel ${channelConfig.name} with id ${channelHandler.getChannel().channelId} requires a session for ${routeMessage}. No session is attached to this websocket connection. Ensure auth middleware establishes a session during websocket upgrade, and configure sessionStore when the runtime needs persisted sessions.`
|
|
96
97
|
)
|
|
97
|
-
//
|
|
98
|
+
// knowledge: questions/unauthorized-channel-replies-escape-the-declared-out-type.md
|
|
98
99
|
channelHandler.getChannel().send(`Unauthorized for ${routeMessage}`)
|
|
99
100
|
return
|
|
100
101
|
}
|
|
@@ -111,10 +112,7 @@ export const processMessageHandlers = (
|
|
|
111
112
|
// Get wire middleware: channel-level middleware + message-specific middleware
|
|
112
113
|
const channelWireMiddleware = channelConfig.middleware || []
|
|
113
114
|
|
|
114
|
-
//
|
|
115
|
-
// - Direct config: onMessage.func is a plain Function
|
|
116
|
-
// - Wrapper: onMessage.func is a CorePikkuFunctionConfig (has onMessage.func.func)
|
|
117
|
-
// - Simple wrapper: onMessage has both func (plain Function) and middleware properties
|
|
115
|
+
// knowledge: decisions/internals/channel-message-handlers-accept-three-config-shapes.md
|
|
118
116
|
const isWrapper =
|
|
119
117
|
onMessage &&
|
|
120
118
|
typeof onMessage === 'object' &&
|
|
@@ -145,7 +143,9 @@ export const processMessageHandlers = (
|
|
|
145
143
|
inheritedMiddleware,
|
|
146
144
|
wireMiddleware,
|
|
147
145
|
inheritedChannelMiddleware: channelMeta?.channelMiddleware,
|
|
148
|
-
|
|
146
|
+
// knowledge: questions/channel-middleware-accepts-bare-factories-that-nothing-resolves.md
|
|
147
|
+
wireChannelMiddleware:
|
|
148
|
+
wireChannelMiddleware as CorePikkuChannelMiddleware[],
|
|
149
149
|
coerceDataFromSchema: true,
|
|
150
150
|
tags: channelConfig.tags,
|
|
151
151
|
sessionService: userSession,
|
|
@@ -191,7 +191,7 @@ export const processMessageHandlers = (
|
|
|
191
191
|
|
|
192
192
|
processed = true
|
|
193
193
|
const routeResult = await processMessage(
|
|
194
|
-
data
|
|
194
|
+
data,
|
|
195
195
|
routes[routerValue],
|
|
196
196
|
routingProperty,
|
|
197
197
|
routerValue
|
|
@@ -10,20 +10,6 @@ import {
|
|
|
10
10
|
type ChannelRPCValidator,
|
|
11
11
|
} from './channel-rpc.types.js'
|
|
12
12
|
|
|
13
|
-
/**
|
|
14
|
-
* `remote` for channels that only ever flow one way — a server-sent stream, an
|
|
15
|
-
* agent's output, a local CLI. Nothing is listening for a request on them, so
|
|
16
|
-
* the call is refused rather than waiting out a timeout.
|
|
17
|
-
*/
|
|
18
|
-
export const unsupportedChannelRemote = async (
|
|
19
|
-
funcName: string
|
|
20
|
-
): Promise<never> => {
|
|
21
|
-
throw new ChannelRPCError(
|
|
22
|
-
`Cannot call "${funcName}" remotely: this channel has no peer that answers`,
|
|
23
|
-
'unsupported'
|
|
24
|
-
)
|
|
25
|
-
}
|
|
26
|
-
|
|
27
13
|
/**
|
|
28
14
|
* A `DeploymentService` whose transport is an open channel rather than an
|
|
29
15
|
* address, so a server-side function can call back into a peer that has no
|
|
@@ -109,21 +109,42 @@ describe('ChannelRPCRegistry', () => {
|
|
|
109
109
|
})
|
|
110
110
|
})
|
|
111
111
|
|
|
112
|
-
|
|
112
|
+
/**
|
|
113
|
+
* The registry unrefs its timeout timer so an in-flight call can never hold
|
|
114
|
+
* a process open. That makes the timeout unawaitable in a test: when the
|
|
115
|
+
* pending call is the only thing left, the loop drains before the timer
|
|
116
|
+
* fires, and node:test cancels the file — taking every later test with it.
|
|
117
|
+
*
|
|
118
|
+
* Driving the clock by hand removes the dependency on real time entirely.
|
|
119
|
+
* `t.mock.timers` is scoped to the test and resets itself, so the tests that
|
|
120
|
+
* do want a real clock are unaffected.
|
|
121
|
+
*
|
|
122
|
+
* knowledge: decisions/internals/an-unref-d-timer-cannot-be-awaited-under-node-test.md
|
|
123
|
+
*/
|
|
124
|
+
test('times out instead of hanging forever', async (t) => {
|
|
125
|
+
t.mock.timers.enable({ apis: ['setTimeout'] })
|
|
126
|
+
|
|
113
127
|
const registry = new ChannelRPCRegistry(10)
|
|
114
128
|
const call = registry.register()
|
|
115
129
|
|
|
116
|
-
|
|
130
|
+
const rejected = assert.rejects(call.promise, (e: ChannelRPCError) => {
|
|
117
131
|
assert.equal(e.reason, 'timeout')
|
|
118
132
|
return true
|
|
119
133
|
})
|
|
134
|
+
t.mock.timers.tick(10)
|
|
135
|
+
await rejected
|
|
136
|
+
|
|
120
137
|
assert.equal(registry.inFlight, 0, 'a timed out call is not leaked')
|
|
121
138
|
})
|
|
122
139
|
|
|
123
|
-
test('drops a late response for an already-timed-out call', async () => {
|
|
140
|
+
test('drops a late response for an already-timed-out call', async (t) => {
|
|
141
|
+
t.mock.timers.enable({ apis: ['setTimeout'] })
|
|
142
|
+
|
|
124
143
|
const registry = new ChannelRPCRegistry(10)
|
|
125
144
|
const call = registry.register()
|
|
126
|
-
|
|
145
|
+
const rejected = assert.rejects(call.promise)
|
|
146
|
+
t.mock.timers.tick(10)
|
|
147
|
+
await rejected
|
|
127
148
|
|
|
128
149
|
const settled = registry.settle({
|
|
129
150
|
action: CHANNEL_RPC_RESPONSE,
|
|
@@ -148,3 +148,17 @@ export type ChannelRPCValidator = (
|
|
|
148
148
|
funcName: string,
|
|
149
149
|
value: unknown
|
|
150
150
|
) => Promise<void> | void
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* `remote` for wires with no peer that answers — a server-sent stream, an
|
|
154
|
+
* agent's output, a local CLI. Nothing is listening for a request on them, so
|
|
155
|
+
* the call is refused rather than waiting out a timeout.
|
|
156
|
+
*/
|
|
157
|
+
export const unsupportedChannelRemote = async (
|
|
158
|
+
funcName: string
|
|
159
|
+
): Promise<never> => {
|
|
160
|
+
throw new ChannelRPCError(
|
|
161
|
+
`Cannot call "${funcName}" remotely: this channel has no peer that answers`,
|
|
162
|
+
'unsupported'
|
|
163
|
+
)
|
|
164
|
+
}
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import { NotFoundError } from '../../errors/errors.js'
|
|
2
2
|
import { addFunction } from '../../function/function-runner.js'
|
|
3
|
-
import type {
|
|
3
|
+
import type { CorePikkuFunctionConfig } from '../../function/functions.types.js'
|
|
4
4
|
import { pikkuState, getSingletonServices } from '../../pikku-state.js'
|
|
5
5
|
import { coerceTopLevelDataFromSchema, validateSchema } from '../../schema.js'
|
|
6
6
|
import type { SessionService } from '../../services/user-session-service.js'
|
|
7
|
-
import type { CorePikkuMiddleware } from '../../types/core.types.js'
|
|
8
7
|
import { httpRouter } from '../http/routers/http-router.js'
|
|
9
8
|
import type {
|
|
10
9
|
ChannelMeta,
|
|
@@ -13,19 +12,34 @@ import type {
|
|
|
13
12
|
RunChannelParams,
|
|
14
13
|
} from './channel.types.js'
|
|
15
14
|
|
|
15
|
+
type HandlerConfig = CorePikkuFunctionConfig<any, any>
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* The config to register for a channel handler: the handler itself when it is a
|
|
19
|
+
* direct function config, or its inner `func` when it is a wrapper.
|
|
20
|
+
*
|
|
21
|
+
* knowledge: decisions/internals/channel-message-handlers-accept-three-config-shapes.md
|
|
22
|
+
*/
|
|
23
|
+
const registeredHandler = (handler: unknown): HandlerConfig => {
|
|
24
|
+
const candidate = handler as { func?: unknown }
|
|
25
|
+
return typeof candidate.func === 'function'
|
|
26
|
+
? (handler as HandlerConfig)
|
|
27
|
+
: (candidate.func as HandlerConfig)
|
|
28
|
+
}
|
|
29
|
+
|
|
16
30
|
export const wireChannel = <
|
|
17
31
|
In,
|
|
18
32
|
Channel extends string,
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
33
|
+
ChannelConnect,
|
|
34
|
+
ChannelDisconnect,
|
|
35
|
+
ChannelFunctionMessage,
|
|
22
36
|
>(
|
|
23
37
|
channel: CoreChannel<
|
|
24
38
|
In,
|
|
25
39
|
Channel,
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
40
|
+
ChannelConnect,
|
|
41
|
+
ChannelDisconnect,
|
|
42
|
+
ChannelFunctionMessage
|
|
29
43
|
>
|
|
30
44
|
) => {
|
|
31
45
|
const channelsMeta = pikkuState(null, 'channel', 'meta')
|
|
@@ -38,19 +52,23 @@ export const wireChannel = <
|
|
|
38
52
|
}
|
|
39
53
|
|
|
40
54
|
if (channel.onConnect && channelMeta.connect) {
|
|
41
|
-
addFunction(
|
|
55
|
+
addFunction(
|
|
56
|
+
channelMeta.connect.pikkuFuncId,
|
|
57
|
+
registeredHandler(channel.onConnect)
|
|
58
|
+
)
|
|
42
59
|
}
|
|
43
60
|
|
|
44
61
|
if (channel.onDisconnect && channelMeta.disconnect) {
|
|
45
|
-
addFunction(
|
|
62
|
+
addFunction(
|
|
63
|
+
channelMeta.disconnect.pikkuFuncId,
|
|
64
|
+
registeredHandler(channel.onDisconnect)
|
|
65
|
+
)
|
|
46
66
|
}
|
|
47
67
|
|
|
48
68
|
if (channel.onMessage && channelMeta.message?.pikkuFuncId) {
|
|
49
69
|
addFunction(
|
|
50
70
|
channelMeta.message.pikkuFuncId,
|
|
51
|
-
(channel.onMessage
|
|
52
|
-
? channel.onMessage
|
|
53
|
-
: (channel.onMessage as any).func
|
|
71
|
+
registeredHandler(channel.onMessage)
|
|
54
72
|
)
|
|
55
73
|
}
|
|
56
74
|
|
|
@@ -63,17 +81,12 @@ export const wireChannel = <
|
|
|
63
81
|
const wiringMeta = channelWirings[wiringKey]
|
|
64
82
|
if (!wiringMeta) return
|
|
65
83
|
|
|
66
|
-
addFunction(
|
|
67
|
-
wiringMeta.pikkuFuncId,
|
|
68
|
-
(handler as any).func instanceof Function
|
|
69
|
-
? handler
|
|
70
|
-
: (handler as any).func
|
|
71
|
-
)
|
|
84
|
+
addFunction(wiringMeta.pikkuFuncId, registeredHandler(handler))
|
|
72
85
|
})
|
|
73
86
|
})
|
|
74
87
|
}
|
|
75
88
|
|
|
76
|
-
pikkuState(null, 'channel', 'channels').set(channel.name, channel
|
|
89
|
+
pikkuState(null, 'channel', 'channels').set(channel.name, channel)
|
|
77
90
|
}
|
|
78
91
|
|
|
79
92
|
const getMatchingChannelConfig = (path: string) => {
|
|
@@ -64,6 +64,8 @@ export interface ChannelMeta {
|
|
|
64
64
|
message: ChannelMessageMeta | null
|
|
65
65
|
messageWirings: Record<string, Record<string, ChannelMessageMeta>>
|
|
66
66
|
binary?: boolean | null
|
|
67
|
+
/** Set when the channel was wired by a gateway rather than declared directly. */
|
|
68
|
+
gateway?: boolean
|
|
67
69
|
summary?: string
|
|
68
70
|
description?: string
|
|
69
71
|
errors?: string[]
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
export { wireChannel
|
|
1
|
+
export { wireChannel } from './channel-runner.js'
|
|
2
2
|
export {
|
|
3
3
|
addChannelMiddleware,
|
|
4
4
|
combineChannelMiddleware,
|
|
5
|
-
wrapChannelWithMiddleware,
|
|
6
5
|
} from './channel-middleware-runner.js'
|
|
7
6
|
export { logChannels } from './log-channels.js'
|
|
8
7
|
export { PikkuAbstractChannelHandler } from './pikku-abstract-channel-handler.js'
|
|
@@ -18,25 +17,14 @@ export type {
|
|
|
18
17
|
CorePikkuChannelMiddlewareFactory,
|
|
19
18
|
ChannelMessageMeta,
|
|
20
19
|
ChannelMeta,
|
|
21
|
-
ChannelRemote,
|
|
22
20
|
PikkuChannel,
|
|
23
21
|
PikkuChannelHandlerFactory,
|
|
24
22
|
} from './channel.types.js'
|
|
25
23
|
export { defineChannelRoutes } from './define-channel-routes.js'
|
|
26
24
|
export {
|
|
27
|
-
CHANNEL_RPC_PENDING,
|
|
28
|
-
CHANNEL_RPC_REQUEST,
|
|
29
|
-
CHANNEL_RPC_RESPONSE,
|
|
30
25
|
ChannelDeploymentService,
|
|
31
26
|
ChannelRPCError,
|
|
32
27
|
ChannelRPCRegistry,
|
|
33
|
-
createChannelRPCResponder,
|
|
34
|
-
createChannelRPCInputValidator,
|
|
35
|
-
createChannelRPCResultValidator,
|
|
36
|
-
isChannelRPCPending,
|
|
37
|
-
isChannelRPCRequest,
|
|
38
|
-
isChannelRPCResponse,
|
|
39
|
-
resolveCapability,
|
|
40
28
|
unsupportedChannelRemote,
|
|
41
29
|
} from './channel-rpc.js'
|
|
42
30
|
export type {
|
|
@@ -44,16 +32,9 @@ export type {
|
|
|
44
32
|
ApprovalRequester,
|
|
45
33
|
Capabilities,
|
|
46
34
|
Capability,
|
|
47
|
-
CapabilityDef,
|
|
48
35
|
CapabilityHandler,
|
|
49
36
|
ChannelRPCPending,
|
|
50
37
|
ChannelRPCRequest,
|
|
51
38
|
ChannelRPCResponse,
|
|
52
39
|
ChannelRPCValidator,
|
|
53
40
|
} from './channel-rpc.js'
|
|
54
|
-
export {
|
|
55
|
-
channelRemote,
|
|
56
|
-
getChannelHostRPC,
|
|
57
|
-
handleChannelRPCResponse,
|
|
58
|
-
releaseChannelHostRPC,
|
|
59
|
-
} from './channel-host-rpc.js'
|
|
@@ -3,6 +3,7 @@ import * as assert from 'node:assert/strict'
|
|
|
3
3
|
import { runLocalChannel } from './local-channel-runner.js'
|
|
4
4
|
import { pikkuState, resetPikkuState } from '../../../pikku-state.js'
|
|
5
5
|
import { wireChannel } from '../channel-runner.js'
|
|
6
|
+
import { addFunction } from '../../../function/function-runner.js'
|
|
6
7
|
import { addHTTPMiddleware } from '../../http/http-runner.js'
|
|
7
8
|
import type {
|
|
8
9
|
HTTPMethod,
|
|
@@ -270,3 +271,70 @@ test('runChannel should run HTTP middleware on websocket upgrade and establish s
|
|
|
270
271
|
await result.message(JSON.stringify({ action: 'ping' }))
|
|
271
272
|
assert.deepEqual(sent, { action: 'ping' })
|
|
272
273
|
})
|
|
274
|
+
|
|
275
|
+
test('a message handler that returns nothing does not attempt to send', async () => {
|
|
276
|
+
// The connect path already guards this; the message path did not, so a handler
|
|
277
|
+
// with nothing to say produced `send requires a non-empty message` on every
|
|
278
|
+
// message. Gateway websockets hit it every time — their generated message
|
|
279
|
+
// handler returns undefined by design — which is why a chat gateway could
|
|
280
|
+
// accept a connection and never deliver anything to its handler.
|
|
281
|
+
resetPikkuState()
|
|
282
|
+
pikkuState(null, 'package', 'singletonServices', mockSingletonServices as any)
|
|
283
|
+
pikkuState(null, 'package', 'factories', {
|
|
284
|
+
createWireServices: mockCreateWireServices,
|
|
285
|
+
} as any)
|
|
286
|
+
|
|
287
|
+
const sent: unknown[] = []
|
|
288
|
+
let handled = 0
|
|
289
|
+
|
|
290
|
+
pikkuState(null, 'channel', 'meta', {
|
|
291
|
+
quiet: {
|
|
292
|
+
name: 'quiet',
|
|
293
|
+
route: '/quiet-channel',
|
|
294
|
+
message: { pikkuFuncId: 'quietMessage' },
|
|
295
|
+
messageWirings: {},
|
|
296
|
+
},
|
|
297
|
+
} as any)
|
|
298
|
+
pikkuState(null, 'function', 'meta')['quietMessage'] = {
|
|
299
|
+
pikkuFuncId: 'quietMessage',
|
|
300
|
+
inputSchemaName: null,
|
|
301
|
+
outputSchemaName: null,
|
|
302
|
+
sessionless: true,
|
|
303
|
+
} as any
|
|
304
|
+
const quietMessage = {
|
|
305
|
+
auth: false,
|
|
306
|
+
func: async () => {
|
|
307
|
+
handled++
|
|
308
|
+
return undefined
|
|
309
|
+
},
|
|
310
|
+
}
|
|
311
|
+
wireChannel({
|
|
312
|
+
name: 'quiet',
|
|
313
|
+
route: '/quiet-channel',
|
|
314
|
+
auth: false,
|
|
315
|
+
onMessage: quietMessage as any,
|
|
316
|
+
} as any)
|
|
317
|
+
|
|
318
|
+
httpRouter.initialize()
|
|
319
|
+
|
|
320
|
+
const handler = await runLocalChannel({
|
|
321
|
+
channelId: 'quiet-channel-id',
|
|
322
|
+
request: new PikkuMockRequest('/quiet-channel', 'get'),
|
|
323
|
+
response: new PikkuMockResponse(),
|
|
324
|
+
route: '/quiet-channel',
|
|
325
|
+
})
|
|
326
|
+
assert.ok(handler)
|
|
327
|
+
handler.registerOnSend(async (message: unknown) => {
|
|
328
|
+
sent.push(message)
|
|
329
|
+
})
|
|
330
|
+
handler.open()
|
|
331
|
+
|
|
332
|
+
await handler.message(JSON.stringify({ text: 'hello' }))
|
|
333
|
+
|
|
334
|
+
assert.equal(handled, 1, 'the handler should have run')
|
|
335
|
+
assert.deepEqual(
|
|
336
|
+
sent,
|
|
337
|
+
[],
|
|
338
|
+
'nothing should have been sent for an empty result'
|
|
339
|
+
)
|
|
340
|
+
})
|