@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
|
@@ -1,53 +1,23 @@
|
|
|
1
|
-
import { runPikkuFunc
|
|
2
|
-
import {
|
|
3
|
-
pikkuWorkflowWorkerFunc,
|
|
4
|
-
pikkuWorkflowOrchestratorFunc,
|
|
5
|
-
pikkuWorkflowSleeperFunc,
|
|
6
|
-
} from './workflow-queue-workers.js'
|
|
7
|
-
import { wireQueueWorker } from '../queue/queue-runner.js'
|
|
1
|
+
import { runPikkuFunc } from '../../function/function-runner.js'
|
|
8
2
|
import {
|
|
9
3
|
getSingletonServices,
|
|
10
4
|
getCreateWireServices,
|
|
11
5
|
pikkuState,
|
|
12
6
|
} from '../../pikku-state.js'
|
|
13
7
|
import { getDurationInMilliseconds } from '../../time-utils.js'
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
const namespace = name.substring(0, colonIndex)
|
|
26
|
-
const localName = name.substring(colonIndex + 1)
|
|
27
|
-
const addons = pikkuState(null, 'addons', 'packages')
|
|
28
|
-
const pkgConfig = addons?.get(namespace)
|
|
29
|
-
if (pkgConfig) {
|
|
30
|
-
const addonMeta = pikkuState(pkgConfig.package, 'workflows', 'meta')
|
|
31
|
-
if (addonMeta?.[localName]) {
|
|
32
|
-
return {
|
|
33
|
-
meta: addonMeta[localName],
|
|
34
|
-
packageName: pkgConfig.package,
|
|
35
|
-
resolvedName: localName,
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
return null
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
const toKebab = (s: string) =>
|
|
45
|
-
s.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase()
|
|
46
|
-
import type { PikkuWire, SerializedError } from '../../types/core.types.js'
|
|
47
|
-
import type { QueueService } from '../queue/queue.types.js'
|
|
8
|
+
import type {
|
|
9
|
+
CoreUserSession,
|
|
10
|
+
PikkuRawWire,
|
|
11
|
+
SerializedError,
|
|
12
|
+
} from '../../types/core.types.js'
|
|
13
|
+
import type {
|
|
14
|
+
GroupConcurrencyConfig,
|
|
15
|
+
JobGroup,
|
|
16
|
+
JobOptions,
|
|
17
|
+
QueueService,
|
|
18
|
+
} from '../queue/queue.types.js'
|
|
48
19
|
import type {
|
|
49
20
|
ApprovalOutcome,
|
|
50
|
-
CoreWorkflow,
|
|
51
21
|
PikkuWorkflowWire,
|
|
52
22
|
StepState,
|
|
53
23
|
StepStatus,
|
|
@@ -64,6 +34,7 @@ import type {
|
|
|
64
34
|
WorkflowStepOptions,
|
|
65
35
|
} from './workflow.types.js'
|
|
66
36
|
import {
|
|
37
|
+
ChildWorkflowStartedException,
|
|
67
38
|
continueGraph,
|
|
68
39
|
executeGraphStep,
|
|
69
40
|
runWorkflowGraph,
|
|
@@ -71,251 +42,66 @@ import {
|
|
|
71
42
|
} from './graph/graph-runner.js'
|
|
72
43
|
import type { WorkflowService } from '../../services/workflow-service.js'
|
|
73
44
|
import type { ScenarioPersonas } from '../../services/personas-service.js'
|
|
74
|
-
import {
|
|
75
|
-
|
|
76
|
-
addError,
|
|
77
|
-
isExpectedError,
|
|
78
|
-
} from '../../errors/error-handler.js'
|
|
45
|
+
import { isExpectedError } from '../../errors/error-handler.js'
|
|
46
|
+
import { PikkuMissingMetaError } from '../../errors/errors.js'
|
|
79
47
|
import { RPCNotFoundError } from '../rpc/rpc-runner.js'
|
|
80
|
-
import {
|
|
48
|
+
import type { PikkuRPC } from '../rpc/rpc-types.js'
|
|
81
49
|
import { deriveInvocationId } from './workflow-invocation-id.js'
|
|
50
|
+
import { approvalDeciderFrom } from './workflow-approval-policy.js'
|
|
82
51
|
import {
|
|
83
52
|
buildRunTimeline,
|
|
84
53
|
reconstructStateAt,
|
|
85
54
|
type RunTimeline,
|
|
86
55
|
type ReconstructedRunState,
|
|
87
56
|
} from './run-timeline.js'
|
|
57
|
+
import {
|
|
58
|
+
DEFAULT_STEP_RETRIES,
|
|
59
|
+
WORKFLOW_CHILD_POLL_MAX_MS,
|
|
60
|
+
WORKFLOW_END_STATES,
|
|
61
|
+
WORKFLOW_POLL_FACTOR,
|
|
62
|
+
WORKFLOW_POLL_MIN_MS,
|
|
63
|
+
WORKFLOW_TERMINAL_STATES,
|
|
64
|
+
} from './workflow-constants.js'
|
|
65
|
+
import {
|
|
66
|
+
WorkflowAsyncException,
|
|
67
|
+
WorkflowCancelledException,
|
|
68
|
+
WorkflowDispatchException,
|
|
69
|
+
WorkflowNotFoundError,
|
|
70
|
+
WorkflowRunCancelledError,
|
|
71
|
+
WorkflowRunFailedError,
|
|
72
|
+
WorkflowRunNotFoundError,
|
|
73
|
+
WorkflowStepFunctionMismatchError,
|
|
74
|
+
WorkflowStepNameNotString,
|
|
75
|
+
WorkflowSuspendedException,
|
|
76
|
+
} from './workflow-errors.js'
|
|
88
77
|
import type {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
} from '
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
export class WorkflowSuspendedException extends Error {
|
|
118
|
-
constructor(
|
|
119
|
-
public readonly runId: string,
|
|
120
|
-
public readonly reason: string
|
|
121
|
-
) {
|
|
122
|
-
super(reason || 'Workflow suspended')
|
|
123
|
-
this.name = 'WorkflowSuspendedException'
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
export class WorkflowDispatchException extends Error {
|
|
128
|
-
constructor(
|
|
129
|
-
public readonly runId: string,
|
|
130
|
-
public readonly stepName: string,
|
|
131
|
-
options?: { cause?: unknown }
|
|
132
|
-
) {
|
|
133
|
-
super(
|
|
134
|
-
`Failed to dispatch workflow step '${stepName}' (run ${runId})`,
|
|
135
|
-
options
|
|
136
|
-
)
|
|
137
|
-
this.name = 'WorkflowDispatchException'
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
export class WorkflowNotFoundError extends PikkuError {
|
|
142
|
-
constructor(name: string) {
|
|
143
|
-
super(`Workflow not found: ${name}`)
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
addError(WorkflowNotFoundError, {
|
|
147
|
-
status: 404,
|
|
148
|
-
message: 'Workflow not found.',
|
|
149
|
-
})
|
|
150
|
-
|
|
151
|
-
export class WorkflowRunNotFoundError extends PikkuError {
|
|
152
|
-
constructor(runId: string) {
|
|
153
|
-
super(`Workflow run not found: ${runId}`)
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
addError(WorkflowRunNotFoundError, {
|
|
157
|
-
status: 404,
|
|
158
|
-
message: 'Workflow run not found.',
|
|
159
|
-
})
|
|
160
|
-
|
|
161
|
-
export class WorkflowRunFailedError extends PikkuError {
|
|
162
|
-
public payload: { message?: string }
|
|
163
|
-
constructor(message?: string) {
|
|
164
|
-
super(`Workflow run failed: ${message ?? 'unknown'}`)
|
|
165
|
-
this.payload = { message }
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
addError(WorkflowRunFailedError, {
|
|
169
|
-
status: 422,
|
|
170
|
-
message: 'Workflow run failed.',
|
|
171
|
-
})
|
|
172
|
-
|
|
173
|
-
export class WorkflowRunCancelledError extends PikkuError {
|
|
174
|
-
constructor() {
|
|
175
|
-
super('Workflow was cancelled')
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
addError(WorkflowRunCancelledError, {
|
|
179
|
-
status: 409,
|
|
180
|
-
message: 'Workflow was cancelled.',
|
|
181
|
-
})
|
|
182
|
-
|
|
183
|
-
export class WorkflowApprovalResolvedError extends PikkuError {
|
|
184
|
-
public payload: {
|
|
185
|
-
reason: string
|
|
186
|
-
outcome: ApprovalOutcome<unknown>['status']
|
|
187
|
-
}
|
|
188
|
-
constructor(reason: string, outcome: ApprovalOutcome<unknown>['status']) {
|
|
189
|
-
super(`Approval already ${outcome}: ${reason}`)
|
|
190
|
-
this.payload = { reason, outcome }
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
addError(WorkflowApprovalResolvedError, {
|
|
194
|
-
status: 409,
|
|
195
|
-
message: 'Approval has already been resolved.',
|
|
196
|
-
})
|
|
197
|
-
|
|
198
|
-
export class WorkflowServiceNotInitialized extends Error {}
|
|
199
|
-
export class WorkflowStepNameNotString extends Error {
|
|
200
|
-
constructor(stepName: any) {
|
|
201
|
-
super(`Workflow step name must be a string. Received: ${typeof stepName}`)
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
const WORKFLOW_END_STATES: ReadonlySet<string> = new Set([
|
|
206
|
-
'completed',
|
|
207
|
-
'failed',
|
|
208
|
-
'cancelled',
|
|
209
|
-
'suspended',
|
|
210
|
-
])
|
|
211
|
-
|
|
212
|
-
export interface RunLifecycleContext {
|
|
213
|
-
runId: string
|
|
214
|
-
run: WorkflowRun
|
|
215
|
-
workflowMeta: any
|
|
216
|
-
workflow: CoreWorkflow
|
|
217
|
-
wire: PikkuWire
|
|
218
|
-
packageName: string | null
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
export interface WorkflowRunEngine {
|
|
222
|
-
inlineStep(
|
|
223
|
-
runId: string,
|
|
224
|
-
logicalStepName: string,
|
|
225
|
-
fn: Function,
|
|
226
|
-
stepOptions?: WorkflowStepOptions,
|
|
227
|
-
data?: any,
|
|
228
|
-
funcName?: string
|
|
229
|
-
): Promise<any>
|
|
230
|
-
updateRunStatus(
|
|
231
|
-
runId: string,
|
|
232
|
-
status: WorkflowStatus,
|
|
233
|
-
output?: any,
|
|
234
|
-
error?: SerializedError
|
|
235
|
-
): Promise<void>
|
|
236
|
-
onChildWorkflowFailed(run: WorkflowRun, error: unknown): Promise<void>
|
|
237
|
-
verifyStepName(stepName: unknown): void
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
export interface WorkflowRunExtension {
|
|
241
|
-
attachRunContext(
|
|
242
|
-
runId: string,
|
|
243
|
-
workflowMeta: any,
|
|
244
|
-
options?: Record<string, any>
|
|
245
|
-
): Promise<void>
|
|
246
|
-
detachRunContext(runId: string): void
|
|
247
|
-
decorateRunWire(
|
|
248
|
-
wire: PikkuWire,
|
|
249
|
-
context: {
|
|
250
|
-
runId: string
|
|
251
|
-
workflowMeta: any
|
|
252
|
-
workflowWire: PikkuWorkflowWire
|
|
253
|
-
}
|
|
254
|
-
): void
|
|
255
|
-
decorateWorkflowWire(
|
|
256
|
-
workflowWire: PikkuWorkflowWire,
|
|
257
|
-
context: {
|
|
258
|
-
name: string
|
|
259
|
-
runId: string
|
|
260
|
-
rpcService: any
|
|
261
|
-
addonNamespace?: string | null
|
|
262
|
-
}
|
|
263
|
-
): void
|
|
264
|
-
onBeforeRunFunc(context: RunLifecycleContext): Promise<void>
|
|
265
|
-
onAfterRunFunc(
|
|
266
|
-
context: RunLifecycleContext,
|
|
267
|
-
outcome: 'completed' | 'failed' | 'interrupted',
|
|
268
|
-
failure: unknown
|
|
269
|
-
): Promise<void>
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
const WORKFLOW_TERMINAL_STATES: ReadonlySet<string> = new Set([
|
|
273
|
-
'completed',
|
|
274
|
-
'failed',
|
|
275
|
-
'cancelled',
|
|
276
|
-
])
|
|
277
|
-
|
|
278
|
-
/** Idle window before a `running` run with nothing in flight is treated as stalled. */
|
|
279
|
-
const DEFAULT_STALLED_RUN_MS = 5 * 60_000
|
|
280
|
-
|
|
281
|
-
/** Runs re-driven per `recoverStalledRuns` call, so one sweep is bounded. */
|
|
282
|
-
const DEFAULT_STALLED_RUN_LIMIT = 100
|
|
283
|
-
|
|
284
|
-
/**
|
|
285
|
-
* How long a step may sit `pending` before the relay assumes its dispatch was
|
|
286
|
-
* lost. This is a bet that no healthy queue takes this long to move a job from
|
|
287
|
-
* `pending` to `running`; set it above the observed p99 of that latency.
|
|
288
|
-
*/
|
|
289
|
-
const DEFAULT_UNDISPATCHED_STEP_MS = 30_000
|
|
290
|
-
|
|
291
|
-
/** Steps re-driven per `relayUndispatchedSteps` call, so one tick is bounded. */
|
|
292
|
-
const DEFAULT_UNDISPATCHED_STEP_LIMIT = 100
|
|
293
|
-
|
|
294
|
-
/** First wait before a step already re-dispatched once is re-dispatched again. */
|
|
295
|
-
const REDISPATCH_BACKOFF_MS = 30_000
|
|
296
|
-
|
|
297
|
-
/** Ceiling on the doubling backoff, so a permanently stuck step still gets swept. */
|
|
298
|
-
const REDISPATCH_BACKOFF_MAX_MS = 10 * 60_000
|
|
299
|
-
|
|
300
|
-
/** Bound on the in-process backoff map, so a long-lived process cannot grow it without bound. */
|
|
301
|
-
const REDISPATCH_BACKOFF_MAX_ENTRIES = 10_000
|
|
302
|
-
|
|
303
|
-
const WORKFLOW_POLL_MIN_MS = 10
|
|
304
|
-
|
|
305
|
-
const WORKFLOW_POLL_FACTOR = 1.6
|
|
306
|
-
|
|
307
|
-
const WORKFLOW_CHILD_POLL_MAX_MS = 500
|
|
308
|
-
|
|
309
|
-
type RunContext = {
|
|
310
|
-
activeExecutions: number
|
|
311
|
-
inline?: boolean
|
|
312
|
-
ordinals: Map<string, number>
|
|
313
|
-
lastStep?: string
|
|
314
|
-
replay?: {
|
|
315
|
-
steps?: Map<string, StepState>
|
|
316
|
-
run?: WorkflowRun
|
|
317
|
-
}
|
|
318
|
-
}
|
|
78
|
+
RunContext,
|
|
79
|
+
RunLifecycleContext,
|
|
80
|
+
WorkflowRunEngine,
|
|
81
|
+
WorkflowRunExtension,
|
|
82
|
+
} from './workflow-run-engine.types.js'
|
|
83
|
+
import { resolveWorkflowMeta } from './workflow-meta-resolver.js'
|
|
84
|
+
import {
|
|
85
|
+
jobGroupFor,
|
|
86
|
+
orchestratorQueueName,
|
|
87
|
+
resolveWorkflowConfig,
|
|
88
|
+
stepJobOptions,
|
|
89
|
+
stepWorkerQueueName,
|
|
90
|
+
} from './workflow-queue-routing.js'
|
|
91
|
+
import { wireWorkflowQueueWorkers } from './workflow-queue-wiring.js'
|
|
92
|
+
import {
|
|
93
|
+
approvalStepNameFor,
|
|
94
|
+
evaluateApprovalStep,
|
|
95
|
+
recordApprovalDecision,
|
|
96
|
+
type ApprovalStore,
|
|
97
|
+
} from './workflow-approval.js'
|
|
98
|
+
import { auditApprovalDecision } from './workflow-approval-audit.js'
|
|
99
|
+
import { recordSuspension, suspendStepNameFor } from './workflow-suspend.js'
|
|
100
|
+
import {
|
|
101
|
+
RedispatchBackoff,
|
|
102
|
+
sweepStalledRuns,
|
|
103
|
+
sweepUndispatchedSteps,
|
|
104
|
+
} from './workflow-recovery.js'
|
|
319
105
|
|
|
320
106
|
export abstract class PikkuWorkflowService implements WorkflowService {
|
|
321
107
|
private runExtension?: WorkflowRunExtension
|
|
@@ -406,112 +192,12 @@ export abstract class PikkuWorkflowService implements WorkflowService {
|
|
|
406
192
|
}
|
|
407
193
|
|
|
408
194
|
public wireQueueWorkers(): void {
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
sessionless: true,
|
|
415
|
-
functionType: 'helper' as const,
|
|
416
|
-
inputSchemaName: null,
|
|
417
|
-
outputSchemaName: null,
|
|
195
|
+
wireWorkflowQueueWorkers({
|
|
196
|
+
strategy: this.queueStrategy,
|
|
197
|
+
concurrency: this.queueConcurrency,
|
|
198
|
+
groupConcurrency: this.queueGroupConcurrency,
|
|
199
|
+
getLogger: () => this.logger,
|
|
418
200
|
})
|
|
419
|
-
|
|
420
|
-
const queueMeta = pikkuState(null, 'queue', 'meta')
|
|
421
|
-
|
|
422
|
-
const registerWorkflowFunc = (
|
|
423
|
-
funcId: string,
|
|
424
|
-
func: { func: unknown },
|
|
425
|
-
queueName: string,
|
|
426
|
-
config?: PikkuWorkerConfig
|
|
427
|
-
) => {
|
|
428
|
-
if (functions.has(funcId)) return
|
|
429
|
-
addFunction(funcId, func as never)
|
|
430
|
-
if (!queueMeta[queueName]) {
|
|
431
|
-
queueMeta[queueName] = { pikkuFuncId: funcId, name: queueName }
|
|
432
|
-
}
|
|
433
|
-
wireQueueWorker({ name: queueName, func, config } as never)
|
|
434
|
-
if (!functionsMeta[funcId]) {
|
|
435
|
-
functionsMeta[funcId] = mkMeta(funcId)
|
|
436
|
-
}
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
const sharedGroups = this.queueStrategy === 'shared-groups'
|
|
440
|
-
const sharedQueueConfig: PikkuWorkerConfig | undefined = sharedGroups
|
|
441
|
-
? {
|
|
442
|
-
batchSize: this.queueConcurrency,
|
|
443
|
-
groupConcurrency: this.queueGroupConcurrency,
|
|
444
|
-
}
|
|
445
|
-
: undefined
|
|
446
|
-
|
|
447
|
-
registerWorkflowFunc(
|
|
448
|
-
'pikkuWorkflowOrchestrator',
|
|
449
|
-
{ func: pikkuWorkflowOrchestratorFunc },
|
|
450
|
-
'pikku-workflow-orchestrator',
|
|
451
|
-
sharedQueueConfig
|
|
452
|
-
)
|
|
453
|
-
registerWorkflowFunc(
|
|
454
|
-
'pikkuWorkflowStepWorker',
|
|
455
|
-
{ func: pikkuWorkflowWorkerFunc },
|
|
456
|
-
'pikku-workflow-step-worker',
|
|
457
|
-
sharedQueueConfig
|
|
458
|
-
)
|
|
459
|
-
|
|
460
|
-
const registerQueueWorkers = (queueMeta: Record<string, any>) => {
|
|
461
|
-
for (const [queueName, meta] of Object.entries(queueMeta)) {
|
|
462
|
-
if (functions.has(meta.pikkuFuncId)) continue
|
|
463
|
-
if (queueName.startsWith('wf-orchestrator-')) {
|
|
464
|
-
registerWorkflowFunc(
|
|
465
|
-
meta.pikkuFuncId,
|
|
466
|
-
{ func: pikkuWorkflowOrchestratorFunc },
|
|
467
|
-
queueName
|
|
468
|
-
)
|
|
469
|
-
} else if (queueName.startsWith('wf-step-')) {
|
|
470
|
-
registerWorkflowFunc(
|
|
471
|
-
meta.pikkuFuncId,
|
|
472
|
-
{ func: pikkuWorkflowWorkerFunc },
|
|
473
|
-
queueName
|
|
474
|
-
)
|
|
475
|
-
}
|
|
476
|
-
}
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
if (!sharedGroups) {
|
|
480
|
-
registerQueueWorkers(pikkuState(null, 'queue', 'meta'))
|
|
481
|
-
|
|
482
|
-
const addons = pikkuState(null, 'addons', 'packages')
|
|
483
|
-
if (addons) {
|
|
484
|
-
for (const [, addon] of addons) {
|
|
485
|
-
const addonQueueMeta = pikkuState(addon.package, 'queue', 'meta')
|
|
486
|
-
if (addonQueueMeta) {
|
|
487
|
-
registerQueueWorkers(addonQueueMeta)
|
|
488
|
-
}
|
|
489
|
-
}
|
|
490
|
-
}
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
const workflowCount = Object.keys(
|
|
494
|
-
pikkuState(null, 'workflows', 'meta') ?? {}
|
|
495
|
-
).length
|
|
496
|
-
const perWorkflowQueues = Object.keys(queueMeta).filter((name) =>
|
|
497
|
-
name.startsWith('wf-orchestrator-')
|
|
498
|
-
).length
|
|
499
|
-
if (workflowCount > 0 && perWorkflowQueues === 0) {
|
|
500
|
-
this.logger?.warn?.(
|
|
501
|
-
`[pikku] ${workflowCount} workflow(s) registered but no per-workflow orchestrator queues were found in queue meta. ` +
|
|
502
|
-
`All workflows will share a single orchestrator queue, where one slow step blocks every other workflow behind it. ` +
|
|
503
|
-
`Check that the generated bootstrap imports the queue-workers meta (pikku-queue-workers-wirings-meta.gen.js).`
|
|
504
|
-
)
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
if (!functions.has('pikkuWorkflowSleeper')) {
|
|
508
|
-
addFunction('pikkuWorkflowSleeper', {
|
|
509
|
-
func: pikkuWorkflowSleeperFunc,
|
|
510
|
-
})
|
|
511
|
-
}
|
|
512
|
-
if (!functionsMeta.pikkuWorkflowSleeper) {
|
|
513
|
-
functionsMeta.pikkuWorkflowSleeper = mkMeta('pikkuWorkflowSleeper')
|
|
514
|
-
}
|
|
515
201
|
}
|
|
516
202
|
|
|
517
203
|
protected async isInline(runId: string): Promise<boolean> {
|
|
@@ -774,7 +460,7 @@ export abstract class PikkuWorkflowService implements WorkflowService {
|
|
|
774
460
|
const serialized: SerializedError = {
|
|
775
461
|
message: error.message,
|
|
776
462
|
stack: error.stack,
|
|
777
|
-
code: (error as
|
|
463
|
+
code: (error as { code?: string }).code,
|
|
778
464
|
expected: isExpectedError(error),
|
|
779
465
|
}
|
|
780
466
|
return mirror.setStepError(stepId, serialized)
|
|
@@ -796,7 +482,7 @@ export abstract class PikkuWorkflowService implements WorkflowService {
|
|
|
796
482
|
(mirror, newStep) =>
|
|
797
483
|
mirror.createRetryAttempt(failedStepId, {
|
|
798
484
|
...newStep,
|
|
799
|
-
stepName: (newStep as
|
|
485
|
+
stepName: (newStep as { stepName?: string }).stepName ?? '',
|
|
800
486
|
})
|
|
801
487
|
)
|
|
802
488
|
}
|
|
@@ -949,51 +635,6 @@ export abstract class PikkuWorkflowService implements WorkflowService {
|
|
|
949
635
|
return []
|
|
950
636
|
}
|
|
951
637
|
|
|
952
|
-
/**
|
|
953
|
-
* Re-drive runs whose next move was lost, and report which were resumed.
|
|
954
|
-
*
|
|
955
|
-
* Arming a step is two writes to two systems — the step row, then the queue
|
|
956
|
-
* or scheduler job — so a process that dies between them leaves a run that is
|
|
957
|
-
* `running` with nothing in flight. Nothing notices: the run parks on a step
|
|
958
|
-
* that will never complete and never error, so it neither finishes nor fails.
|
|
959
|
-
* (Seen on a `workflow.sleep()`: a deploy restart landed between the sleep
|
|
960
|
-
* step's insert and its timer, parking the run permanently.)
|
|
961
|
-
*
|
|
962
|
-
* Replay is the recovery — `resumeWorkflow` re-orchestrates from persisted
|
|
963
|
-
* step state, and every settled step is memoized, so resuming a run that was
|
|
964
|
-
* not actually stuck costs an orchestration pass and changes nothing. That
|
|
965
|
-
* idempotence is what makes an idle-time heuristic safe here; a run that is
|
|
966
|
-
* legitimately mid-sleep is excluded anyway, since its step is `scheduled`.
|
|
967
|
-
*
|
|
968
|
-
* This is not self-starting. Call it from a scheduled task at whatever
|
|
969
|
-
* interval suits the workload.
|
|
970
|
-
*/
|
|
971
|
-
public async recoverStalledRuns(options?: {
|
|
972
|
-
stalledAfterMs?: number
|
|
973
|
-
limit?: number
|
|
974
|
-
}): Promise<{ resumed: string[] }> {
|
|
975
|
-
const before = new Date(
|
|
976
|
-
Date.now() - (options?.stalledAfterMs ?? DEFAULT_STALLED_RUN_MS)
|
|
977
|
-
)
|
|
978
|
-
const runIds = await this.findStalledRunIds(
|
|
979
|
-
before,
|
|
980
|
-
options?.limit ?? DEFAULT_STALLED_RUN_LIMIT
|
|
981
|
-
)
|
|
982
|
-
const resumed: string[] = []
|
|
983
|
-
for (const runId of runIds) {
|
|
984
|
-
try {
|
|
985
|
-
await this.resumeWorkflow(runId)
|
|
986
|
-
resumed.push(runId)
|
|
987
|
-
} catch (err) {
|
|
988
|
-
// One unresumable run must not stop the sweep from recovering the rest.
|
|
989
|
-
getSingletonServices()?.logger?.error(
|
|
990
|
-
`Failed to resume stalled workflow run ${runId}: ${err instanceof Error ? err.message : String(err)}`
|
|
991
|
-
)
|
|
992
|
-
}
|
|
993
|
-
}
|
|
994
|
-
return { resumed }
|
|
995
|
-
}
|
|
996
|
-
|
|
997
638
|
/**
|
|
998
639
|
* Runs holding a step that has sat `pending` since before `before`, paired
|
|
999
640
|
* with the step that flagged them.
|
|
@@ -1014,110 +655,51 @@ export abstract class PikkuWorkflowService implements WorkflowService {
|
|
|
1014
655
|
return []
|
|
1015
656
|
}
|
|
1016
657
|
|
|
658
|
+
private readonly redispatchBackoff = new RedispatchBackoff()
|
|
659
|
+
|
|
660
|
+
private get sweepDeps() {
|
|
661
|
+
return {
|
|
662
|
+
resume: (runId: string) => this.resumeWorkflow(runId),
|
|
663
|
+
logger: getSingletonServices()?.logger,
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
|
|
1017
667
|
/**
|
|
1018
|
-
* Re-drive
|
|
1019
|
-
*
|
|
1020
|
-
* Arming a step is two writes to two systems: the step row lands `pending`,
|
|
1021
|
-
* then a queue or scheduler job is published. Nothing spans both, so a crash
|
|
1022
|
-
* in between leaves a durable row that nothing will ever pick up — the run
|
|
1023
|
-
* neither finishes nor fails. (Seen on a `workflow.sleep()`: a deploy restart
|
|
1024
|
-
* landed between the sleep step's insert and its timer.)
|
|
1025
|
-
*
|
|
1026
|
-
* The row is the outbox record and this is the relay. Age is the only signal
|
|
1027
|
-
* available — a step `pending` because its dispatch was lost is
|
|
1028
|
-
* indistinguishable from one whose job is merely still queued — so a step
|
|
1029
|
-
* past `undispatchedAfterMs` is re-dispatched regardless, and correctness
|
|
1030
|
-
* rests on the claim rather than on the guess being right. A redundant
|
|
1031
|
-
* dispatch costs one queue message: the loser reads `running` and returns
|
|
1032
|
-
* without invoking anything.
|
|
1033
|
-
*
|
|
1034
|
-
* Re-dispatches back off per step (doubling from 30s, capped at 10m) so a
|
|
1035
|
-
* genuine queue backlog is not amplified by a tick that keeps firing at the
|
|
1036
|
-
* steps the backlog is already delaying. The backoff is per process and
|
|
1037
|
-
* advisory — losing it on restart costs extra dispatches, never correctness.
|
|
1038
|
-
*
|
|
1039
|
-
* This is not self-starting. Call it from a scheduled task; ~30s suits a
|
|
1040
|
-
* queue whose `pending`→`running` latency is well under that.
|
|
668
|
+
* Re-drive runs whose next move was lost. Not self-starting — call it from a
|
|
669
|
+
* scheduled task at whatever interval suits the workload.
|
|
1041
670
|
*/
|
|
1042
|
-
public async
|
|
1043
|
-
|
|
671
|
+
public async recoverStalledRuns(options?: {
|
|
672
|
+
stalledAfterMs?: number
|
|
1044
673
|
limit?: number
|
|
1045
|
-
}): Promise<{
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
const steps = await this.findUndispatchedSteps(
|
|
1051
|
-
before,
|
|
1052
|
-
options?.limit ?? DEFAULT_UNDISPATCHED_STEP_LIMIT
|
|
674
|
+
}): Promise<{ resumed: string[] }> {
|
|
675
|
+
return sweepStalledRuns(
|
|
676
|
+
(before, limit) => this.findStalledRunIds(before, limit),
|
|
677
|
+
options,
|
|
678
|
+
this.sweepDeps
|
|
1053
679
|
)
|
|
1054
|
-
|
|
1055
|
-
// Backoff is keyed by run, not step, because the run is the unit of
|
|
1056
|
-
// re-drive: `resumeWorkflow` replays the whole run and re-dispatches every
|
|
1057
|
-
// step still owed a job. Holding off a single step while resuming its run
|
|
1058
|
-
// would not suppress anything.
|
|
1059
|
-
const now = Date.now()
|
|
1060
|
-
const runIds = new Set<string>()
|
|
1061
|
-
for (const { runId } of steps) {
|
|
1062
|
-
const eligibleAt = this.redispatchBackoff.get(runId)
|
|
1063
|
-
if (eligibleAt !== undefined && eligibleAt > now) {
|
|
1064
|
-
continue
|
|
1065
|
-
}
|
|
1066
|
-
this.noteRedispatch(runId, now)
|
|
1067
|
-
runIds.add(runId)
|
|
1068
|
-
}
|
|
1069
|
-
|
|
1070
|
-
const redispatched: string[] = []
|
|
1071
|
-
for (const runId of runIds) {
|
|
1072
|
-
try {
|
|
1073
|
-
await this.resumeWorkflow(runId)
|
|
1074
|
-
redispatched.push(runId)
|
|
1075
|
-
} catch (err) {
|
|
1076
|
-
// One unresumable run must not stop the tick from relaying the rest.
|
|
1077
|
-
getSingletonServices()?.logger?.error(
|
|
1078
|
-
`Failed to re-dispatch workflow run ${runId}: ${err instanceof Error ? err.message : String(err)}`
|
|
1079
|
-
)
|
|
1080
|
-
}
|
|
1081
|
-
}
|
|
1082
|
-
return { redispatched }
|
|
1083
680
|
}
|
|
1084
681
|
|
|
1085
|
-
/**
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
682
|
+
/**
|
|
683
|
+
* Re-drive steps whose dispatch was lost. Not self-starting — call it from a
|
|
684
|
+
* scheduled task; ~30s suits a queue whose `pending`→`running` latency is
|
|
685
|
+
* well under that.
|
|
686
|
+
*/
|
|
687
|
+
public async relayUndispatchedSteps(options?: {
|
|
688
|
+
undispatchedAfterMs?: number
|
|
689
|
+
limit?: number
|
|
690
|
+
}): Promise<{ redispatched: string[] }> {
|
|
691
|
+
return sweepUndispatchedSteps(
|
|
692
|
+
(before, limit) => this.findUndispatchedSteps(before, limit),
|
|
693
|
+
this.redispatchBackoff,
|
|
694
|
+
options,
|
|
695
|
+
this.sweepDeps
|
|
1095
696
|
)
|
|
1096
|
-
// A run that settles is never returned again, so entries are only evicted by
|
|
1097
|
-
// this bound — oldest first, which is also least recently re-dispatched.
|
|
1098
|
-
if (this.redispatchBackoff.size >= REDISPATCH_BACKOFF_MAX_ENTRIES) {
|
|
1099
|
-
const oldest = this.redispatchBackoff.keys().next()
|
|
1100
|
-
if (!oldest.done) {
|
|
1101
|
-
this.redispatchBackoff.delete(oldest.value)
|
|
1102
|
-
this.redispatchDelays.delete(oldest.value)
|
|
1103
|
-
}
|
|
1104
|
-
}
|
|
1105
|
-
this.redispatchDelays.set(runId, delay)
|
|
1106
|
-
this.redispatchBackoff.set(runId, now + delay)
|
|
1107
697
|
}
|
|
1108
698
|
|
|
1109
699
|
protected resolveStepJobOptions(
|
|
1110
700
|
stepOptions?: WorkflowStepOptions
|
|
1111
701
|
): JobOptions {
|
|
1112
|
-
|
|
1113
|
-
const retryDelay = stepOptions?.retryDelay
|
|
1114
|
-
const backoff =
|
|
1115
|
-
retryDelay !== undefined && retryDelay !== 'exponential'
|
|
1116
|
-
? { type: 'fixed', delay: getDurationInMilliseconds(retryDelay) }
|
|
1117
|
-
: retries > 0 || retryDelay === 'exponential'
|
|
1118
|
-
? 'exponential'
|
|
1119
|
-
: undefined
|
|
1120
|
-
return { attempts: retries + 1, ...(backoff ? { backoff } : {}) }
|
|
702
|
+
return stepJobOptions(stepOptions)
|
|
1121
703
|
}
|
|
1122
704
|
|
|
1123
705
|
public async queueStepWorker(
|
|
@@ -1246,7 +828,7 @@ export abstract class PikkuWorkflowService implements WorkflowService {
|
|
|
1246
828
|
name: string,
|
|
1247
829
|
input: I,
|
|
1248
830
|
wire: WorkflowRunWire,
|
|
1249
|
-
rpcService:
|
|
831
|
+
rpcService: PikkuRPC,
|
|
1250
832
|
options?: {
|
|
1251
833
|
inline?: boolean
|
|
1252
834
|
startNode?: string
|
|
@@ -1263,6 +845,22 @@ export abstract class PikkuWorkflowService implements WorkflowService {
|
|
|
1263
845
|
}
|
|
1264
846
|
|
|
1265
847
|
if (workflowMeta.source === 'graph') {
|
|
848
|
+
// A caller-supplied startNode must be one of the graph's declared entry
|
|
849
|
+
// nodes. startWorkflow is the boundary the public
|
|
850
|
+
// `/workflow/:name/graph/:nodeId` route and triggers enter through, so
|
|
851
|
+
// without this a request could name any dependency-free node — one whose
|
|
852
|
+
// input reads only `trigger` — and fire its RPC directly with
|
|
853
|
+
// attacker-chosen data, skipping every upstream eligibility, validation or
|
|
854
|
+
// approval node. (Internal resume/replay drives runWorkflowGraph directly
|
|
855
|
+
// and is unaffected.)
|
|
856
|
+
if (
|
|
857
|
+
options?.startNode &&
|
|
858
|
+
!(workflowMeta.entryNodeIds ?? []).includes(options.startNode)
|
|
859
|
+
) {
|
|
860
|
+
throw new Error(
|
|
861
|
+
`Workflow graph '${name}': '${options.startNode}' is not a declared entry node`
|
|
862
|
+
)
|
|
863
|
+
}
|
|
1266
864
|
const shouldInline =
|
|
1267
865
|
options?.inline || !getSingletonServices()?.queueService
|
|
1268
866
|
return runWorkflowGraph(
|
|
@@ -1346,7 +944,7 @@ export abstract class PikkuWorkflowService implements WorkflowService {
|
|
|
1346
944
|
public async runToCompletion<I>(
|
|
1347
945
|
name: string,
|
|
1348
946
|
input: I,
|
|
1349
|
-
rpcService:
|
|
947
|
+
rpcService: PikkuRPC,
|
|
1350
948
|
options?: { pollIntervalMs?: number; wire?: WorkflowRunWire }
|
|
1351
949
|
): Promise<any> {
|
|
1352
950
|
const { runId } = await this.startWorkflow(
|
|
@@ -1468,7 +1066,10 @@ export abstract class PikkuWorkflowService implements WorkflowService {
|
|
|
1468
1066
|
return stepName
|
|
1469
1067
|
}
|
|
1470
1068
|
|
|
1471
|
-
public async runWorkflowJob(
|
|
1069
|
+
public async runWorkflowJob(
|
|
1070
|
+
runId: string,
|
|
1071
|
+
rpcService: PikkuRPC
|
|
1072
|
+
): Promise<void> {
|
|
1472
1073
|
await this.beginReplay(runId)
|
|
1473
1074
|
try {
|
|
1474
1075
|
await this.runWorkflowJobInner(runId, rpcService)
|
|
@@ -1479,7 +1080,7 @@ export abstract class PikkuWorkflowService implements WorkflowService {
|
|
|
1479
1080
|
|
|
1480
1081
|
private async runWorkflowJobInner(
|
|
1481
1082
|
runId: string,
|
|
1482
|
-
rpcService:
|
|
1083
|
+
rpcService: PikkuRPC
|
|
1483
1084
|
): Promise<void> {
|
|
1484
1085
|
const run = await this.getRunIdentity(runId)
|
|
1485
1086
|
if (!run) {
|
|
@@ -1517,6 +1118,12 @@ export abstract class PikkuWorkflowService implements WorkflowService {
|
|
|
1517
1118
|
}
|
|
1518
1119
|
|
|
1519
1120
|
const registrations = pikkuState(pkgName, 'workflows', 'registrations')
|
|
1121
|
+
if (!workflowMeta) {
|
|
1122
|
+
throw new PikkuMissingMetaError(
|
|
1123
|
+
`Missing generated metadata for workflow '${run.workflow}'`
|
|
1124
|
+
)
|
|
1125
|
+
}
|
|
1126
|
+
|
|
1520
1127
|
const workflow = registrations.get(resolved?.resolvedName ?? run.workflow)
|
|
1521
1128
|
if (!workflow) {
|
|
1522
1129
|
throw new WorkflowNotFoundError(run.workflow)
|
|
@@ -1533,11 +1140,10 @@ export abstract class PikkuWorkflowService implements WorkflowService {
|
|
|
1533
1140
|
addonNs
|
|
1534
1141
|
)
|
|
1535
1142
|
workflowWire.pikkuUserId = run.wire?.pikkuUserId
|
|
1536
|
-
|
|
1143
|
+
// No `rpc` yet — runPikkuFunc attaches it lazily for the invocation.
|
|
1144
|
+
const wire: PikkuRawWire = {
|
|
1537
1145
|
workflow: workflowWire,
|
|
1538
1146
|
pikkuUserId: run.wire?.pikkuUserId,
|
|
1539
|
-
session: rpcService?.wire?.session,
|
|
1540
|
-
rpc: rpcService?.wire?.rpc,
|
|
1541
1147
|
}
|
|
1542
1148
|
this.runExtension?.decorateRunWire(wire, {
|
|
1543
1149
|
runId,
|
|
@@ -1649,7 +1255,7 @@ export abstract class PikkuWorkflowService implements WorkflowService {
|
|
|
1649
1255
|
private async runVersionMismatchFallback(
|
|
1650
1256
|
run: WorkflowRun,
|
|
1651
1257
|
currentMeta: { source: string },
|
|
1652
|
-
rpcService:
|
|
1258
|
+
rpcService: PikkuRPC
|
|
1653
1259
|
): Promise<void> {
|
|
1654
1260
|
const source = currentMeta.source
|
|
1655
1261
|
|
|
@@ -1680,7 +1286,7 @@ export abstract class PikkuWorkflowService implements WorkflowService {
|
|
|
1680
1286
|
stepName: string,
|
|
1681
1287
|
rpcName: string,
|
|
1682
1288
|
data: any,
|
|
1683
|
-
rpcService:
|
|
1289
|
+
rpcService: PikkuRPC
|
|
1684
1290
|
): Promise<void> {
|
|
1685
1291
|
this.enterExecution(runId)
|
|
1686
1292
|
try {
|
|
@@ -1701,10 +1307,17 @@ export abstract class PikkuWorkflowService implements WorkflowService {
|
|
|
1701
1307
|
stepName: string,
|
|
1702
1308
|
rpcName: string,
|
|
1703
1309
|
data: any,
|
|
1704
|
-
rpcService:
|
|
1310
|
+
rpcService: PikkuRPC
|
|
1705
1311
|
): Promise<void> {
|
|
1706
1312
|
const claimed = await this.withStepLock(runId, stepName, async () => {
|
|
1707
1313
|
const stepState = await this.getStepState(runId, stepName)
|
|
1314
|
+
// knowledge: decisions/security/a-step-runs-the-function-the-workflow-dispatched-it-with.md
|
|
1315
|
+
if (
|
|
1316
|
+
stepState.rpcName !== undefined &&
|
|
1317
|
+
stepState.rpcName !== (rpcName ?? null)
|
|
1318
|
+
) {
|
|
1319
|
+
throw new WorkflowStepFunctionMismatchError(runId, stepName)
|
|
1320
|
+
}
|
|
1708
1321
|
if (stepState.status === 'succeeded' || stepState.status === 'running') {
|
|
1709
1322
|
return null
|
|
1710
1323
|
}
|
|
@@ -1751,7 +1364,7 @@ export abstract class PikkuWorkflowService implements WorkflowService {
|
|
|
1751
1364
|
runId,
|
|
1752
1365
|
stepState.stepId,
|
|
1753
1366
|
graphNodeId,
|
|
1754
|
-
rpcName,
|
|
1367
|
+
workflowMeta!.nodes![graphNodeId].rpcName,
|
|
1755
1368
|
data,
|
|
1756
1369
|
run.workflow
|
|
1757
1370
|
)
|
|
@@ -1763,7 +1376,7 @@ export abstract class PikkuWorkflowService implements WorkflowService {
|
|
|
1763
1376
|
id: rpcName,
|
|
1764
1377
|
parentRunId: runId,
|
|
1765
1378
|
parentStepId: stepState.stepId,
|
|
1766
|
-
pikkuUserId:
|
|
1379
|
+
pikkuUserId: run.wire?.pikkuUserId,
|
|
1767
1380
|
}
|
|
1768
1381
|
const shouldInline = !getSingletonServices()?.queueService
|
|
1769
1382
|
const { runId: childRunId } = await this.startWorkflow(
|
|
@@ -1838,7 +1451,7 @@ export abstract class PikkuWorkflowService implements WorkflowService {
|
|
|
1838
1451
|
|
|
1839
1452
|
public async orchestrateWorkflow(
|
|
1840
1453
|
runId: string,
|
|
1841
|
-
rpcService:
|
|
1454
|
+
rpcService: PikkuRPC
|
|
1842
1455
|
): Promise<void> {
|
|
1843
1456
|
try {
|
|
1844
1457
|
await this.runWorkflowJob(runId, rpcService)
|
|
@@ -1885,7 +1498,7 @@ export abstract class PikkuWorkflowService implements WorkflowService {
|
|
|
1885
1498
|
stepState: StepState,
|
|
1886
1499
|
rpcName: string,
|
|
1887
1500
|
data: any,
|
|
1888
|
-
rpcService:
|
|
1501
|
+
rpcService: PikkuRPC,
|
|
1889
1502
|
knownRun?: WorkflowRun | null
|
|
1890
1503
|
): Promise<any> {
|
|
1891
1504
|
const run = knownRun ?? (await this.getRunIdentity(runId))
|
|
@@ -1943,7 +1556,7 @@ export abstract class PikkuWorkflowService implements WorkflowService {
|
|
|
1943
1556
|
runId: string,
|
|
1944
1557
|
stepName: string,
|
|
1945
1558
|
onErrorRpcName: string,
|
|
1946
|
-
rpcService:
|
|
1559
|
+
rpcService: PikkuRPC,
|
|
1947
1560
|
error: Error
|
|
1948
1561
|
): Promise<void> {
|
|
1949
1562
|
await this.rpcStep(
|
|
@@ -1961,7 +1574,7 @@ export abstract class PikkuWorkflowService implements WorkflowService {
|
|
|
1961
1574
|
logicalStepName: string,
|
|
1962
1575
|
rpcName: string,
|
|
1963
1576
|
data: any,
|
|
1964
|
-
rpcService:
|
|
1577
|
+
rpcService: PikkuRPC,
|
|
1965
1578
|
stepOptions?: WorkflowStepOptions
|
|
1966
1579
|
): Promise<any> {
|
|
1967
1580
|
const fromStepName = this.lastStepName(runId)
|
|
@@ -2043,7 +1656,7 @@ export abstract class PikkuWorkflowService implements WorkflowService {
|
|
|
2043
1656
|
type: 'workflow',
|
|
2044
1657
|
id: rpcName,
|
|
2045
1658
|
parentRunId: runId,
|
|
2046
|
-
pikkuUserId:
|
|
1659
|
+
pikkuUserId: (await this.getRunIdentity(runId))?.wire?.pikkuUserId,
|
|
2047
1660
|
}
|
|
2048
1661
|
const { runId: childRunId } = await this.startWorkflow(
|
|
2049
1662
|
rpcName,
|
|
@@ -2182,59 +1795,18 @@ export abstract class PikkuWorkflowService implements WorkflowService {
|
|
|
2182
1795
|
await this.setStepResult(stepState.stepId, null)
|
|
2183
1796
|
}
|
|
2184
1797
|
|
|
2185
|
-
private getSuspendStepName(reason: string): string {
|
|
2186
|
-
return `__workflow_suspend:${reason}`
|
|
2187
|
-
}
|
|
2188
|
-
|
|
2189
1798
|
private async suspendStep(runId: string, reason: string): Promise<void> {
|
|
2190
1799
|
const fromStepName = this.lastStepName(runId)
|
|
2191
|
-
const suspendStepName = this.nextStepKey(
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
let stepState: StepState
|
|
2197
|
-
try {
|
|
2198
|
-
stepState = await this.getStepState(runId, suspendStepName)
|
|
2199
|
-
} catch {
|
|
2200
|
-
stepState = await this.insertStepState(
|
|
2201
|
-
runId,
|
|
2202
|
-
suspendStepName,
|
|
2203
|
-
'pikkuWorkflowSuspend',
|
|
2204
|
-
{
|
|
2205
|
-
reason,
|
|
2206
|
-
},
|
|
2207
|
-
undefined,
|
|
2208
|
-
fromStepName
|
|
2209
|
-
)
|
|
2210
|
-
}
|
|
2211
|
-
if (!stepState.stepId) {
|
|
2212
|
-
stepState = await this.insertStepState(
|
|
2213
|
-
runId,
|
|
2214
|
-
suspendStepName,
|
|
2215
|
-
'pikkuWorkflowSuspend',
|
|
2216
|
-
{
|
|
2217
|
-
reason,
|
|
2218
|
-
},
|
|
2219
|
-
undefined,
|
|
2220
|
-
fromStepName
|
|
2221
|
-
)
|
|
2222
|
-
}
|
|
2223
|
-
|
|
2224
|
-
if (stepState.status === 'succeeded') {
|
|
2225
|
-
return
|
|
2226
|
-
}
|
|
2227
|
-
|
|
2228
|
-
if (stepState.status === 'pending') {
|
|
2229
|
-
await this.setStepRunning(stepState.stepId)
|
|
2230
|
-
}
|
|
2231
|
-
|
|
2232
|
-
await this.setStepResult(stepState.stepId, {
|
|
1800
|
+
const suspendStepName = this.nextStepKey(runId, suspendStepNameFor(reason))
|
|
1801
|
+
await this.withStepLock(runId, suspendStepName, () =>
|
|
1802
|
+
recordSuspension(
|
|
1803
|
+
this.approvalStore,
|
|
1804
|
+
runId,
|
|
2233
1805
|
reason,
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
1806
|
+
suspendStepName,
|
|
1807
|
+
fromStepName
|
|
1808
|
+
)
|
|
1809
|
+
)
|
|
2238
1810
|
}
|
|
2239
1811
|
|
|
2240
1812
|
private async scheduleRunWake(runId: string, delay: number): Promise<void> {
|
|
@@ -2259,49 +1831,51 @@ export abstract class PikkuWorkflowService implements WorkflowService {
|
|
|
2259
1831
|
}
|
|
2260
1832
|
}
|
|
2261
1833
|
|
|
2262
|
-
private
|
|
2263
|
-
return
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
1834
|
+
private get approvalStore(): ApprovalStore {
|
|
1835
|
+
return {
|
|
1836
|
+
getStepState: (runId, stepName) => this.getStepState(runId, stepName),
|
|
1837
|
+
insertStepState: (
|
|
1838
|
+
runId,
|
|
1839
|
+
stepName,
|
|
1840
|
+
rpcName,
|
|
1841
|
+
input,
|
|
1842
|
+
output,
|
|
1843
|
+
fromStepName
|
|
1844
|
+
) =>
|
|
1845
|
+
this.insertStepState(
|
|
1846
|
+
runId,
|
|
1847
|
+
stepName,
|
|
1848
|
+
rpcName,
|
|
1849
|
+
input,
|
|
1850
|
+
output,
|
|
1851
|
+
fromStepName
|
|
1852
|
+
),
|
|
1853
|
+
setStepRunning: (stepId) => this.setStepRunning(stepId),
|
|
1854
|
+
setStepResult: (stepId, result) => this.setStepResult(stepId, result),
|
|
1855
|
+
getRunState: (runId) => this.getRunState(runId),
|
|
1856
|
+
updateRunState: (runId, key, value) =>
|
|
1857
|
+
this.updateRunState(runId, key, value),
|
|
1858
|
+
resumeWorkflow: (runId) => this.resumeWorkflow(runId),
|
|
1859
|
+
scheduleRunWake: (runId, delay) => this.scheduleRunWake(runId, delay),
|
|
1860
|
+
getRunOwner: async (runId) =>
|
|
1861
|
+
(await this.getRunIdentity(runId))?.wire?.pikkuUserId,
|
|
1862
|
+
auditApproval: (event) => auditApprovalDecision(event),
|
|
2270
1863
|
}
|
|
2271
|
-
return `__approval_${hex}`
|
|
2272
1864
|
}
|
|
2273
1865
|
|
|
2274
1866
|
public async approveStep(
|
|
2275
1867
|
runId: string,
|
|
2276
1868
|
reason: string,
|
|
2277
|
-
decision: unknown
|
|
1869
|
+
decision: unknown,
|
|
1870
|
+
session?: CoreUserSession
|
|
2278
1871
|
): Promise<void> {
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
try {
|
|
2284
|
-
resolved = await this.getStepState(runId, stepName)
|
|
2285
|
-
} catch {
|
|
2286
|
-
// knowledge: decisions/security/workflow-approval-payloads-are-validated-on-replay-inside-the-workflow.md
|
|
2287
|
-
}
|
|
2288
|
-
if (resolved?.stepId && resolved.status === 'succeeded') {
|
|
2289
|
-
const outcome = resolved.result as ApprovalOutcome<unknown> | undefined
|
|
2290
|
-
throw new WorkflowApprovalResolvedError(
|
|
2291
|
-
reason,
|
|
2292
|
-
outcome?.status ?? 'decided'
|
|
2293
|
-
)
|
|
2294
|
-
}
|
|
2295
|
-
|
|
2296
|
-
const state = await this.getRunState(runId)
|
|
2297
|
-
const record = (state[stateKey] ?? {}) as Record<string, unknown>
|
|
2298
|
-
await this.updateRunState(runId, stateKey, {
|
|
2299
|
-
...record,
|
|
1872
|
+
return recordApprovalDecision(
|
|
1873
|
+
this.approvalStore,
|
|
1874
|
+
runId,
|
|
1875
|
+
reason,
|
|
2300
1876
|
decision,
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
})
|
|
2304
|
-
await this.resumeWorkflow(runId)
|
|
1877
|
+
approvalDeciderFrom(session)
|
|
1878
|
+
)
|
|
2305
1879
|
}
|
|
2306
1880
|
|
|
2307
1881
|
private async approvalStep(
|
|
@@ -2312,100 +1886,24 @@ export abstract class PikkuWorkflowService implements WorkflowService {
|
|
|
2312
1886
|
const fromStepName = this.lastStepName(runId)
|
|
2313
1887
|
const approvalStepName = this.nextStepKey(
|
|
2314
1888
|
runId,
|
|
2315
|
-
|
|
1889
|
+
approvalStepNameFor(reason)
|
|
1890
|
+
)
|
|
1891
|
+
return await this.withStepLock(runId, approvalStepName, () =>
|
|
1892
|
+
evaluateApprovalStep(
|
|
1893
|
+
this.approvalStore,
|
|
1894
|
+
runId,
|
|
1895
|
+
reason,
|
|
1896
|
+
approvalStepName,
|
|
1897
|
+
fromStepName,
|
|
1898
|
+
options
|
|
1899
|
+
)
|
|
2316
1900
|
)
|
|
2317
|
-
return await this.withStepLock(runId, approvalStepName, async () => {
|
|
2318
|
-
let stepState: StepState
|
|
2319
|
-
try {
|
|
2320
|
-
stepState = await this.getStepState(runId, approvalStepName)
|
|
2321
|
-
} catch {
|
|
2322
|
-
stepState = await this.insertStepState(
|
|
2323
|
-
runId,
|
|
2324
|
-
approvalStepName,
|
|
2325
|
-
'pikkuWorkflowApproval',
|
|
2326
|
-
{ reason, expiry: options.expiry },
|
|
2327
|
-
undefined,
|
|
2328
|
-
fromStepName
|
|
2329
|
-
)
|
|
2330
|
-
}
|
|
2331
|
-
if (!stepState.stepId) {
|
|
2332
|
-
stepState = await this.insertStepState(
|
|
2333
|
-
runId,
|
|
2334
|
-
approvalStepName,
|
|
2335
|
-
'pikkuWorkflowApproval',
|
|
2336
|
-
{ reason, expiry: options.expiry },
|
|
2337
|
-
undefined,
|
|
2338
|
-
fromStepName
|
|
2339
|
-
)
|
|
2340
|
-
}
|
|
2341
|
-
|
|
2342
|
-
if (stepState.status === 'succeeded') {
|
|
2343
|
-
return stepState.result as ApprovalOutcome<unknown>
|
|
2344
|
-
}
|
|
2345
|
-
|
|
2346
|
-
const stateKey = this.approvalStateKey(approvalStepName)
|
|
2347
|
-
let record = ((await this.getRunState(runId))[stateKey] ?? {}) as {
|
|
2348
|
-
decision?: unknown
|
|
2349
|
-
decidedAt?: string
|
|
2350
|
-
expiresAt?: string
|
|
2351
|
-
error?: unknown
|
|
2352
|
-
}
|
|
2353
|
-
|
|
2354
|
-
if (stepState.status === 'pending') {
|
|
2355
|
-
await this.setStepRunning(stepState.stepId)
|
|
2356
|
-
if (options.expiry !== undefined && !record.expiresAt) {
|
|
2357
|
-
const expiresAt = new Date(
|
|
2358
|
-
Date.now() + getDurationInMilliseconds(options.expiry)
|
|
2359
|
-
).toISOString()
|
|
2360
|
-
record = { ...record, expiresAt }
|
|
2361
|
-
await this.updateRunState(runId, stateKey, record)
|
|
2362
|
-
await this.scheduleRunWake(
|
|
2363
|
-
runId,
|
|
2364
|
-
getDurationInMilliseconds(options.expiry)
|
|
2365
|
-
)
|
|
2366
|
-
}
|
|
2367
|
-
}
|
|
2368
|
-
|
|
2369
|
-
if (record.decision !== undefined) {
|
|
2370
|
-
const validation = await options.schema['~standard'].validate(
|
|
2371
|
-
record.decision
|
|
2372
|
-
)
|
|
2373
|
-
if (validation.issues) {
|
|
2374
|
-
await this.updateRunState(runId, stateKey, {
|
|
2375
|
-
...record,
|
|
2376
|
-
decision: undefined,
|
|
2377
|
-
decidedAt: undefined,
|
|
2378
|
-
error: validation.issues.map((issue) => ({
|
|
2379
|
-
message: issue.message,
|
|
2380
|
-
path: issue.path?.map((segment) =>
|
|
2381
|
-
typeof segment === 'object' ? segment.key : segment
|
|
2382
|
-
),
|
|
2383
|
-
})),
|
|
2384
|
-
})
|
|
2385
|
-
throw new WorkflowSuspendedException(runId, reason)
|
|
2386
|
-
}
|
|
2387
|
-
const outcome: ApprovalOutcome<unknown> = {
|
|
2388
|
-
status: 'decided',
|
|
2389
|
-
data: validation.value,
|
|
2390
|
-
}
|
|
2391
|
-
await this.setStepResult(stepState.stepId, outcome)
|
|
2392
|
-
return outcome
|
|
2393
|
-
}
|
|
2394
|
-
|
|
2395
|
-
if (record.expiresAt && Date.now() >= Date.parse(record.expiresAt)) {
|
|
2396
|
-
const outcome: ApprovalOutcome<unknown> = { status: 'expired' }
|
|
2397
|
-
await this.setStepResult(stepState.stepId, outcome)
|
|
2398
|
-
return outcome
|
|
2399
|
-
}
|
|
2400
|
-
|
|
2401
|
-
throw new WorkflowSuspendedException(runId, reason)
|
|
2402
|
-
})
|
|
2403
1901
|
}
|
|
2404
1902
|
|
|
2405
1903
|
public createWorkflowWire(
|
|
2406
1904
|
name: string,
|
|
2407
1905
|
runId: string,
|
|
2408
|
-
rpcService:
|
|
1906
|
+
rpcService: PikkuRPC,
|
|
2409
1907
|
addonNamespace?: string | null
|
|
2410
1908
|
): PikkuWorkflowWire {
|
|
2411
1909
|
const workflowWire: PikkuWorkflowWire = {
|
|
@@ -2478,45 +1976,18 @@ export abstract class PikkuWorkflowService implements WorkflowService {
|
|
|
2478
1976
|
}
|
|
2479
1977
|
|
|
2480
1978
|
private getConfig(): WorkflowServiceConfig {
|
|
2481
|
-
|
|
2482
|
-
const workflow = singletonServices.config?.workflow
|
|
2483
|
-
return {
|
|
2484
|
-
retries: workflow?.retries ?? DEFAULT_STEP_RETRIES,
|
|
2485
|
-
retryDelay: workflow?.retryDelay ?? 0,
|
|
2486
|
-
orchestratorQueueName:
|
|
2487
|
-
workflow?.orchestratorQueueName ?? 'pikku-workflow-orchestrator',
|
|
2488
|
-
stepWorkerQueueName:
|
|
2489
|
-
workflow?.stepWorkerQueueName ?? 'pikku-workflow-step-worker',
|
|
2490
|
-
sleeperRPCName: workflow?.sleeperRPCName ?? 'pikkuWorkflowSleeper',
|
|
2491
|
-
}
|
|
1979
|
+
return resolveWorkflowConfig()
|
|
2492
1980
|
}
|
|
2493
1981
|
|
|
2494
1982
|
protected getOrchestratorQueueName(workflowName?: string): string {
|
|
2495
|
-
|
|
2496
|
-
const perWorkflow = `wf-orchestrator-${toKebab(workflowName)}`
|
|
2497
|
-
const meta = pikkuState(null, 'queue', 'meta')
|
|
2498
|
-
if (meta[perWorkflow]) {
|
|
2499
|
-
return perWorkflow
|
|
2500
|
-
}
|
|
2501
|
-
}
|
|
2502
|
-
return this.getConfig().orchestratorQueueName
|
|
1983
|
+
return orchestratorQueueName(this.queueStrategy, workflowName)
|
|
2503
1984
|
}
|
|
2504
1985
|
|
|
2505
1986
|
protected getStepWorkerQueueName(rpcName?: string): string {
|
|
2506
|
-
|
|
2507
|
-
const perStep = `wf-step-${toKebab(rpcName)}`
|
|
2508
|
-
const meta = pikkuState(null, 'queue', 'meta')
|
|
2509
|
-
if (meta[perStep]) {
|
|
2510
|
-
return perStep
|
|
2511
|
-
}
|
|
2512
|
-
}
|
|
2513
|
-
return this.getConfig().stepWorkerQueueName
|
|
1987
|
+
return stepWorkerQueueName(this.queueStrategy, rpcName)
|
|
2514
1988
|
}
|
|
2515
1989
|
|
|
2516
1990
|
protected getJobGroup(id?: string): JobGroup | undefined {
|
|
2517
|
-
|
|
2518
|
-
return undefined
|
|
2519
|
-
}
|
|
2520
|
-
return { id, tier: id }
|
|
1991
|
+
return jobGroupFor(this.queueStrategy, id)
|
|
2521
1992
|
}
|
|
2522
1993
|
}
|