@pikku/core 0.12.78 → 0.12.80
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 +234 -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/function/function-runner.js +20 -42
- package/dist/function/functions.types.d.ts +9 -5
- package/dist/index.d.ts +4 -9
- package/dist/index.js +3 -8
- 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 +12 -5
- package/dist/schema.js +35 -1
- package/dist/services/ai-run-state-service.d.ts +7 -1
- package/dist/services/in-memory-ai-run-state-service.d.ts +1 -1
- package/dist/services/in-memory-ai-run-state-service.js +2 -1
- package/dist/services/in-memory-workflow-service.d.ts +10 -0
- package/dist/services/in-memory-workflow-service.js +25 -0
- 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/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 +226 -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 -1
- package/dist/types/state.types.d.ts +1 -1
- package/dist/utils/node-host-resolver.d.ts +12 -0
- package/dist/utils/node-host-resolver.js +16 -0
- package/dist/utils/safe-fetch.d.ts +18 -0
- package/dist/utils/safe-fetch.js +167 -29
- 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-memory.js +3 -1
- package/dist/wirings/ai-agent/ai-agent-prepare.js +3 -9
- package/dist/wirings/ai-agent/ai-agent-runner.js +28 -107
- package/dist/wirings/ai-agent/ai-agent-stream.js +20 -61
- package/dist/wirings/ai-agent/ai-agent-turn.d.ts +56 -0
- package/dist/wirings/ai-agent/ai-agent-turn.js +81 -0
- package/dist/wirings/ai-agent/ai-agent.types.d.ts +1 -1
- package/dist/wirings/ai-agent/voice-input.js +1 -6
- package/dist/wirings/ai-agent/voice-output.js +2 -12
- 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/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/cli-runner.js +4 -2
- package/dist/wirings/cli/cli.types.d.ts +0 -8
- package/dist/wirings/cli/command-parser.js +13 -0
- package/dist/wirings/gateway/gateway-runner.js +41 -5
- 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 +6 -13
- package/dist/wirings/http/http.types.d.ts +0 -10
- package/dist/wirings/http/index.d.ts +1 -1
- package/dist/wirings/http/index.js +1 -1
- package/dist/wirings/mcp/mcp-runner.d.ts +0 -7
- package/dist/wirings/mcp/mcp-runner.js +0 -6
- package/dist/wirings/rpc/rpc-runner.d.ts +8 -51
- package/dist/wirings/rpc/rpc-runner.js +53 -86
- package/dist/wirings/rpc/rpc-types.d.ts +3 -0
- package/dist/wirings/secret/validate-secret-definitions.js +2 -0
- 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/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 +29 -2
- 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 +7 -2
- package/dist/wirings/workflow/index.js +5 -1
- package/dist/wirings/workflow/pikku-scenario-service.d.ts +17 -3
- package/dist/wirings/workflow/pikku-scenario-service.js +38 -10
- package/dist/wirings/workflow/pikku-workflow-service.d.ts +44 -111
- package/dist/wirings/workflow/pikku-workflow-service.js +86 -404
- package/dist/wirings/workflow/workflow-approval.d.ts +39 -0
- package/dist/wirings/workflow/workflow-approval.js +114 -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-engine.types.js +1 -0
- package/dist/wirings/workflow/workflow-run-ownership.d.ts +16 -0
- package/dist/wirings/workflow/workflow-run-ownership.js +29 -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 +7 -0
- 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/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-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 +38 -26
- 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/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-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 +34 -0
- package/knowledge/decisions/security/an-agent-approval-is-claimed-before-the-tool-runs.md +33 -0
- package/knowledge/decisions/security/an-upload-is-counted-as-it-arrives-not-buffered-then-measured.md +24 -0
- package/knowledge/decisions/security/gateway-handlers-run-through-the-function-runner-gate.md +13 -3
- package/knowledge/decisions/security/index.md +7 -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 +14 -3
- package/scripts/generate-api-report.d.mts +1 -0
- package/scripts/generate-api-report.mjs +89 -0
- package/scripts/generate-api-report.mts +218 -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/function/function-runner.test.ts +1 -1
- package/src/function/function-runner.ts +28 -39
- package/src/function/functions.types.ts +15 -9
- package/src/handle-error.ts +1 -1
- package/src/index.ts +3 -22
- 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 +17 -6
- package/src/public-surface.json +578 -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-run-state-service.ts +7 -1
- package/src/services/audit-service.ts +2 -2
- package/src/services/in-memory-ai-run-state-service.ts +3 -2
- package/src/services/in-memory-workflow-service.ts +25 -0
- package/src/services/index.ts +1 -4
- 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/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 +286 -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 +15 -1
- package/src/types/state.types.ts +1 -1
- package/src/utils/node-host-resolver.ts +20 -0
- package/src/utils/safe-fetch.test.ts +143 -1
- package/src/utils/safe-fetch.ts +200 -25
- 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-interrupt.test.ts +2 -1
- package/src/wirings/ai-agent/ai-agent-memory.ts +8 -1
- package/src/wirings/ai-agent/ai-agent-prepare.ts +10 -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 +49 -120
- package/src/wirings/ai-agent/ai-agent-stream.test.ts +2 -0
- package/src/wirings/ai-agent/ai-agent-stream.ts +36 -63
- package/src/wirings/ai-agent/ai-agent-thread-ownership.test.ts +1 -1
- package/src/wirings/ai-agent/ai-agent-turn.ts +121 -0
- package/src/wirings/ai-agent/ai-agent.types.ts +1 -1
- package/src/wirings/ai-agent/voice-input.ts +1 -6
- package/src/wirings/ai-agent/voice-output.ts +2 -12
- 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/local/local-channel-runner.ts +1 -1
- 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-client-runner.ts +4 -1
- package/src/wirings/cli/cli-runner.ts +7 -4
- 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/gateway/gateway-authorization.test.ts +131 -0
- package/src/wirings/gateway/gateway-channel-meta.test.ts +44 -0
- package/src/wirings/gateway/gateway-runner.ts +62 -22
- package/src/wirings/http/http-routes.ts +4 -1
- package/src/wirings/http/http-runner.ts +11 -26
- package/src/wirings/http/http.types.ts +0 -15
- package/src/wirings/http/index.ts +1 -7
- package/src/wirings/http/pikku-fetch-http-request.ts +2 -2
- package/src/wirings/http/web-request.ts +1 -1
- package/src/wirings/mcp/mcp-runner.ts +2 -16
- 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/queue-runner.ts +1 -1
- package/src/wirings/rpc/addon-auth-tags.test.ts +1 -5
- package/src/wirings/rpc/rpc-runner.ts +58 -136
- package/src/wirings/rpc/rpc-types.ts +7 -0
- package/src/wirings/rpc/wire-addon.ts +3 -1
- package/src/wirings/secret/validate-secret-definitions.test.ts +69 -0
- package/src/wirings/secret/validate-secret-definitions.ts +2 -0
- package/src/wirings/trigger/pikku-trigger-service.ts +0 -5
- package/src/wirings/trigger/trigger-runner.ts +7 -5
- 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 +35 -2
- 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 +4 -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 +12 -4
- package/src/wirings/workflow/pikku-scenario-service.ts +54 -25
- package/src/wirings/workflow/pikku-workflow-service.test.ts +2 -2
- package/src/wirings/workflow/pikku-workflow-service.ts +208 -630
- package/src/wirings/workflow/scenario-hooks.test.ts +51 -0
- package/src/wirings/workflow/workflow-approval.ts +185 -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-dispatch-relay.test.ts +128 -0
- 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 +215 -0
- package/src/wirings/workflow/workflow-run-engine.types.ts +91 -0
- package/src/wirings/workflow/workflow-run-ownership.ts +36 -0
- package/src/wirings/workflow/workflow-suspend.ts +61 -0
- package/src/wirings/workflow/workflow.types.ts +7 -0
- package/src/wirings-stay-decoupled.test.ts +123 -0
- package/tsconfig.json +1 -1
- package/tsconfig.tsbuildinfo +1 -1
- package/src/internal.ts +0 -10
|
@@ -41,7 +41,11 @@ const CATALOGUE: ApiCatalogueEntry[] = [
|
|
|
41
41
|
]
|
|
42
42
|
|
|
43
43
|
const INTENTS: IntentSource[] = [
|
|
44
|
-
{
|
|
44
|
+
{
|
|
45
|
+
id: 'onboard',
|
|
46
|
+
title: 'set up your first project',
|
|
47
|
+
steps: ['sign in', 'make a project'],
|
|
48
|
+
},
|
|
45
49
|
]
|
|
46
50
|
|
|
47
51
|
const PERSONA = {
|
|
@@ -84,10 +88,7 @@ const scripted = (
|
|
|
84
88
|
return { llm, turns: () => turn }
|
|
85
89
|
}
|
|
86
90
|
|
|
87
|
-
const respond = <T>(
|
|
88
|
-
status: number,
|
|
89
|
-
body: T
|
|
90
|
-
): ScenarioHttpResponse<T> => ({
|
|
91
|
+
const respond = <T>(status: number, body: T): ScenarioHttpResponse<T> => ({
|
|
91
92
|
status,
|
|
92
93
|
ok: status >= 200 && status < 300,
|
|
93
94
|
body,
|
|
@@ -126,7 +127,12 @@ describe('running a virtual user', () => {
|
|
|
126
127
|
{ kind: 'complete', summary: 'done' },
|
|
127
128
|
])
|
|
128
129
|
|
|
129
|
-
const result = await runVirtualUser({
|
|
130
|
+
const result = await runVirtualUser({
|
|
131
|
+
...base,
|
|
132
|
+
target,
|
|
133
|
+
llm,
|
|
134
|
+
budget: { steps: 3 },
|
|
135
|
+
})
|
|
130
136
|
|
|
131
137
|
// The first attempt cost a step and bought the schema; only the second
|
|
132
138
|
// reached the server.
|
|
@@ -355,7 +361,9 @@ describe('what a virtual user reports', () => {
|
|
|
355
361
|
})
|
|
356
362
|
|
|
357
363
|
test('an app-specific oracle can add what the engine cannot know', async () => {
|
|
358
|
-
const target = recordingTarget(() =>
|
|
364
|
+
const target = recordingTarget(() =>
|
|
365
|
+
respond(200, { orgId: 'someone-else' })
|
|
366
|
+
)
|
|
359
367
|
const { llm } = scripted(walkTo('listProjects'))
|
|
360
368
|
|
|
361
369
|
const result = await runVirtualUser({
|
|
@@ -595,8 +603,9 @@ describe('goals and persona', () => {
|
|
|
595
603
|
test('goals in the user’s own words become intents alongside the derived ones', async () => {
|
|
596
604
|
const seen: string[] = []
|
|
597
605
|
const target = recordingTarget(() => respond(200, {}))
|
|
598
|
-
const { llm } = scripted(
|
|
599
|
-
|
|
606
|
+
const { llm } = scripted(
|
|
607
|
+
[{ kind: 'complete' }, { kind: 'complete' }],
|
|
608
|
+
(_i, m) => seen.push(...m)
|
|
600
609
|
)
|
|
601
610
|
|
|
602
611
|
const result = await runVirtualUser({
|
|
@@ -143,8 +143,12 @@ const msg = (role: AIMessage['role'], content: string): AIMessage => ({
|
|
|
143
143
|
})
|
|
144
144
|
|
|
145
145
|
/** Read a structured result, falling back to parsing JSON out of the text. */
|
|
146
|
-
const readObject = <T>(result: {
|
|
147
|
-
|
|
146
|
+
const readObject = <T>(result: {
|
|
147
|
+
object?: unknown
|
|
148
|
+
text?: string
|
|
149
|
+
}): T | null => {
|
|
150
|
+
if (result.object && typeof result.object === 'object')
|
|
151
|
+
return result.object as T
|
|
148
152
|
if (result.text) {
|
|
149
153
|
try {
|
|
150
154
|
return JSON.parse(result.text) as T
|
|
@@ -201,7 +205,11 @@ export const rememberIds = (
|
|
|
201
205
|
return into
|
|
202
206
|
}
|
|
203
207
|
for (const [key, value] of Object.entries(body)) {
|
|
204
|
-
if (
|
|
208
|
+
if (
|
|
209
|
+
typeof value === 'string' &&
|
|
210
|
+
MEMORABLE.test(key) &&
|
|
211
|
+
value.length < 200
|
|
212
|
+
) {
|
|
205
213
|
into[key] = value
|
|
206
214
|
} else if (typeof value === 'object') {
|
|
207
215
|
rememberIds(value, into, depth + 1)
|
|
@@ -237,7 +245,10 @@ const personaInstructions = (
|
|
|
237
245
|
'',
|
|
238
246
|
agents.length
|
|
239
247
|
? `Assistants you can talk to:\n${agents
|
|
240
|
-
.map(
|
|
248
|
+
.map(
|
|
249
|
+
(agent) =>
|
|
250
|
+
`- ${agent.name}${agent.description ? `: ${agent.description}` : ''}`
|
|
251
|
+
)
|
|
241
252
|
.join('\n')}`
|
|
242
253
|
: '',
|
|
243
254
|
fixtures.length ? `Files you have: ${fixtures.join(', ')}` : '',
|
|
@@ -351,7 +362,10 @@ export const runVirtualUser = async (
|
|
|
351
362
|
stoppedBy = 'budget-duration'
|
|
352
363
|
break
|
|
353
364
|
}
|
|
354
|
-
if (
|
|
365
|
+
if (
|
|
366
|
+
budget?.mutations !== undefined &&
|
|
367
|
+
tally.mutations >= budget.mutations
|
|
368
|
+
) {
|
|
355
369
|
stoppedBy = 'budget-mutations'
|
|
356
370
|
break
|
|
357
371
|
}
|
|
@@ -402,10 +416,7 @@ export const runVirtualUser = async (
|
|
|
402
416
|
)
|
|
403
417
|
)
|
|
404
418
|
}
|
|
405
|
-
//
|
|
406
|
-
// `stale` almost always trusts them, a newcomer has none to trust, and an
|
|
407
|
-
// auditor checks nearly everything — which is the whole difference between
|
|
408
|
-
// those runs, expressed as one roll rather than as prose.
|
|
419
|
+
// knowledge: decisions/internals/a-virtual-user-decides-whether-to-trust-memory-once-per-turn.md
|
|
409
420
|
if (Object.keys(memory).length && !rng.chance(profile.reReadRate)) {
|
|
410
421
|
messages.push(
|
|
411
422
|
msg(
|
|
@@ -448,7 +459,9 @@ export const runVirtualUser = async (
|
|
|
448
459
|
|
|
449
460
|
if (!raw || typeof raw.kind !== 'string') {
|
|
450
461
|
steps.push(record)
|
|
451
|
-
messages.push(
|
|
462
|
+
messages.push(
|
|
463
|
+
msg('user', 'That was not a valid action. Choose one action.')
|
|
464
|
+
)
|
|
452
465
|
continue
|
|
453
466
|
}
|
|
454
467
|
|
|
@@ -550,10 +563,7 @@ export const runVirtualUser = async (
|
|
|
550
563
|
continue
|
|
551
564
|
}
|
|
552
565
|
|
|
553
|
-
//
|
|
554
|
-
// read-only disposition is never offered a mutation and an approval-gated
|
|
555
|
-
// endpoint is never offered at all, so `index` is the gate and there is no
|
|
556
|
-
// second one here to drift out of step with it.
|
|
566
|
+
// knowledge: decisions/internals/the-virtual-user-catalogue-is-the-only-gate-on-what-may-be-called.md
|
|
557
567
|
const mutating = !isReadOnly(entry)
|
|
558
568
|
|
|
559
569
|
const args = readArgs((action as { args?: unknown }).args)
|
|
@@ -566,7 +576,10 @@ export const runVirtualUser = async (
|
|
|
566
576
|
try {
|
|
567
577
|
response = await target.call(entry.name, args)
|
|
568
578
|
} catch (error) {
|
|
569
|
-
record.findingKinds = [
|
|
579
|
+
record.findingKinds = [
|
|
580
|
+
...(record.findingKinds ?? []),
|
|
581
|
+
'transport-error',
|
|
582
|
+
]
|
|
570
583
|
addFinding({
|
|
571
584
|
kind: 'transport-error',
|
|
572
585
|
detail: `${entry.name} threw: ${(error as Error).message}`,
|
|
@@ -45,7 +45,10 @@ describe('reachableAgents', () => {
|
|
|
45
45
|
|
|
46
46
|
test('carries the description through, which is what the model chooses on', () => {
|
|
47
47
|
const [router] = reachableAgents(AGENTS, ['content:write'])
|
|
48
|
-
assert.equal(
|
|
48
|
+
assert.equal(
|
|
49
|
+
router!.description,
|
|
50
|
+
'Routes requests to the right domain agent'
|
|
51
|
+
)
|
|
49
52
|
})
|
|
50
53
|
|
|
51
54
|
test('omits description rather than passing undefined', () => {
|
|
@@ -31,23 +31,9 @@ export const deriveCatalogue = (
|
|
|
31
31
|
): ApiCatalogueEntry[] => {
|
|
32
32
|
const entries: ApiCatalogueEntry[] = []
|
|
33
33
|
for (const [name, meta] of Object.entries(functions ?? {})) {
|
|
34
|
-
//
|
|
35
|
-
// held out of every deployed unit — so offering one would only ever waste
|
|
36
|
-
// a turn on a 404.
|
|
37
|
-
//
|
|
38
|
-
// For a platform or addon step this is not an efficiency argument but the
|
|
39
|
-
// oracle: a virtual user's findings are worth something only because it
|
|
40
|
-
// cannot manufacture the outcomes it is meant to discover. One that could
|
|
41
|
-
// invoke "Stripe's webhook arrives" would forge its own payment success,
|
|
42
|
-
// and every finding downstream of that is worthless. Same class of argument
|
|
43
|
-
// as `allowApprovalRequired` defaulting to false — and enforced here, at
|
|
44
|
-
// derivation, rather than by convention.
|
|
34
|
+
// knowledge: decisions/security/a-virtual-user-is-never-offered-a-step-that-would-forge-its-own-oracle.md
|
|
45
35
|
if (meta.scenario || meta.scenarioStep || meta.scenarioStepKind) continue
|
|
46
|
-
//
|
|
47
|
-
// what the shipped target calls over. Absent is not permissive: an unexposed
|
|
48
|
-
// function 404s, and offering one spends a step to learn nothing about the
|
|
49
|
-
// product. Measured on the e2e app, 34 of its 72 functions are in exactly
|
|
50
|
-
// that state — nearly half a catalogue that cannot be called.
|
|
36
|
+
// knowledge: decisions/internals/only-exposed-functions-enter-a-virtual-user-catalogue.md
|
|
51
37
|
if (meta.expose !== true) continue
|
|
52
38
|
|
|
53
39
|
const inputSchema = meta.inputSchemaName
|
|
@@ -103,10 +89,7 @@ const scenarioSteps = (meta: {
|
|
|
103
89
|
)
|
|
104
90
|
}
|
|
105
91
|
|
|
106
|
-
//
|
|
107
|
-
// the scenario declares them. Following `next` instead would buy an ordering
|
|
108
|
-
// that is already true and cost a traversal that has to decide what a branch
|
|
109
|
-
// means to a reader who will never take one.
|
|
92
|
+
// knowledge: decisions/internals/virtual-user-step-order-comes-from-insertion-order.md
|
|
110
93
|
return Object.values(meta.nodes ?? {}).flatMap((node) => {
|
|
111
94
|
const { rpcName, scenarioStepPhase, actor } = (node ?? {}) as {
|
|
112
95
|
rpcName?: string
|
|
@@ -149,11 +132,7 @@ export const deriveIntents = (
|
|
|
149
132
|
steps.push(
|
|
150
133
|
composeStepProse({
|
|
151
134
|
phase: step.phase,
|
|
152
|
-
//
|
|
153
|
-
// placeholders from a run that happened; there is no run yet, and
|
|
154
|
-
// "invites {email}" tells a user to pick someone where the filled-in
|
|
155
|
-
// form would have told it whom — which is the scenario's answer, not
|
|
156
|
-
// the user's own.
|
|
135
|
+
// knowledge: decisions/internals/a-scenario-step-template-is-offered-unfilled.md
|
|
157
136
|
description: template ?? description ?? '',
|
|
158
137
|
actor: step.actor,
|
|
159
138
|
})
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { strict as assert } from 'assert'
|
|
2
2
|
import { describe, test } from 'node:test'
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
DISPOSITIONS,
|
|
5
|
+
dispositionProfile,
|
|
6
|
+
} from './virtual-user-dispositions.js'
|
|
4
7
|
|
|
5
8
|
describe('resolving a disposition profile', () => {
|
|
6
9
|
test('an unknown disposition falls back to realistic rather than throwing', () => {
|
|
@@ -46,7 +49,9 @@ describe('resolving a disposition profile', () => {
|
|
|
46
49
|
const profile = dispositionProfile('careless', {
|
|
47
50
|
instructions: 'Paste ids from other tabs.',
|
|
48
51
|
})
|
|
49
|
-
assert.ok(
|
|
52
|
+
assert.ok(
|
|
53
|
+
profile.instructions.startsWith(DISPOSITIONS.careless.instructions)
|
|
54
|
+
)
|
|
50
55
|
assert.ok(profile.instructions.endsWith('Paste ids from other tabs.'))
|
|
51
56
|
})
|
|
52
57
|
|
|
@@ -154,10 +154,7 @@ export const DISPOSITIONS: Readonly<
|
|
|
154
154
|
invertedOracle: true,
|
|
155
155
|
},
|
|
156
156
|
|
|
157
|
-
//
|
|
158
|
-
// it abandons rarely, runs cool, and is told to stop and say so rather than
|
|
159
|
-
// guess — the failure mode here is a wrong action nobody asked for, not a
|
|
160
|
-
// missed bug.
|
|
157
|
+
// knowledge: decisions/internals/the-worker-disposition-is-the-one-that-is-not-testing.md
|
|
161
158
|
accountable: {
|
|
162
159
|
...REALISTIC,
|
|
163
160
|
instructions: [
|
|
@@ -3,7 +3,10 @@ import assert from 'node:assert/strict'
|
|
|
3
3
|
|
|
4
4
|
import { IntentStack, intentsForPersona } from './virtual-user-intents.js'
|
|
5
5
|
import { createRng } from './virtual-user-rng.js'
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
DISPOSITIONS,
|
|
8
|
+
type DispositionProfile,
|
|
9
|
+
} from './virtual-user-dispositions.js'
|
|
7
10
|
import type { IntentSource } from './virtual-user.types.js'
|
|
8
11
|
|
|
9
12
|
const profileWith = (
|
|
@@ -182,7 +185,11 @@ describe('intents for an actor', () => {
|
|
|
182
185
|
const catalogue: IntentSource[] = [
|
|
183
186
|
{ id: 'a', title: 'invite a teammate', personas: ['orgAdmin'] },
|
|
184
187
|
{ id: 'b', title: 'read the dashboard' },
|
|
185
|
-
{
|
|
188
|
+
{
|
|
189
|
+
id: 'c',
|
|
190
|
+
title: 'deploy a stage',
|
|
191
|
+
personas: ['platformAdmin', 'orgAdmin'],
|
|
192
|
+
},
|
|
186
193
|
]
|
|
187
194
|
|
|
188
195
|
test('an actor gets what names it, plus anything that names nobody', () => {
|
|
@@ -201,7 +208,10 @@ describe('intents for an actor', () => {
|
|
|
201
208
|
|
|
202
209
|
test('an empty actor list means everyone', () => {
|
|
203
210
|
assert.deepEqual(
|
|
204
|
-
intentsForPersona(
|
|
211
|
+
intentsForPersona(
|
|
212
|
+
[{ id: 'x', title: 'anything', personas: [] }],
|
|
213
|
+
'nobody'
|
|
214
|
+
),
|
|
205
215
|
[{ id: 'x', title: 'anything', personas: [] }]
|
|
206
216
|
)
|
|
207
217
|
})
|
|
@@ -311,7 +311,7 @@ export interface ReturnStepMeta {
|
|
|
311
311
|
}
|
|
312
312
|
|
|
313
313
|
/**
|
|
314
|
-
* Inline step metadata
|
|
314
|
+
* Inline step metadata
|
|
315
315
|
*/
|
|
316
316
|
export interface InlineStepMeta {
|
|
317
317
|
/** Inline form - local execution */
|
|
@@ -528,7 +528,40 @@ export interface PikkuWorkflowWire {
|
|
|
528
528
|
approval: WorkflowWireApproval
|
|
529
529
|
}
|
|
530
530
|
|
|
531
|
-
|
|
531
|
+
/**
|
|
532
|
+
* What a scenario has accumulated so far, shared by its body and its hooks.
|
|
533
|
+
*
|
|
534
|
+
* Typed as a partial of the scenario's own output because a scenario can fail
|
|
535
|
+
* at any step: the shape describes what a *completed* run produces, and the
|
|
536
|
+
* context holds however much of it this run reached. A scenario declaring no
|
|
537
|
+
* object output gets an open record rather than `Partial<never>`.
|
|
538
|
+
*
|
|
539
|
+
* `any` collapses to `any`, not to the union of both branches. `PikkuWire`'s
|
|
540
|
+
* default depends on that: the wire type is also used as a generic *constraint*,
|
|
541
|
+
* and a constraint carrying a concrete context would reject every wire whose
|
|
542
|
+
* scenario output differs from it.
|
|
543
|
+
*/
|
|
544
|
+
export type ScenarioContext<Out = unknown> = 0 extends 1 & Out
|
|
545
|
+
? any
|
|
546
|
+
: Out extends object
|
|
547
|
+
? Partial<Out>
|
|
548
|
+
: Record<string, unknown>
|
|
549
|
+
|
|
550
|
+
export interface PikkuScenarioWire<Out = unknown> extends PikkuWorkflowWire {
|
|
551
|
+
/**
|
|
552
|
+
* Scratch the body writes and the `before`/`after` hooks read.
|
|
553
|
+
*
|
|
554
|
+
* A hook is a separate function, so it cannot see the body's locals — which
|
|
555
|
+
* is why teardown had nowhere to learn the ids the body minted. Assigning
|
|
556
|
+
* them here (`scenario.context.projectId = project.projectId`) hands them to
|
|
557
|
+
* `after`, which runs in a `finally` and so cleans up on a failed run too.
|
|
558
|
+
*
|
|
559
|
+
* Deliberately *not* a world: it is scoped to one run, and scenario steps
|
|
560
|
+
* cannot reach it. Steps stay pure functions of their declared inputs, so the
|
|
561
|
+
* ladder in the report remains a complete account of what a step saw.
|
|
562
|
+
*/
|
|
563
|
+
context: ScenarioContext<Out>
|
|
564
|
+
|
|
532
565
|
/**
|
|
533
566
|
* Durable polling step: invoke `rpcName` (as an actor when `options.actor`
|
|
534
567
|
* is set) until `predicate` passes or `options.within` elapses.
|
|
@@ -32,9 +32,11 @@ export const resolveFeatureScenarios = (
|
|
|
32
32
|
const scenarios = feature.scenarios ?? []
|
|
33
33
|
for (let index = 0; index < scenarios.length; index++) {
|
|
34
34
|
const entry = scenarios[index]!
|
|
35
|
-
const
|
|
35
|
+
const pairing =
|
|
36
36
|
typeof entry === 'object' && entry !== null && 'scenario' in entry
|
|
37
|
-
|
|
37
|
+
? (entry as { scenario: unknown; data?: unknown })
|
|
38
|
+
: undefined
|
|
39
|
+
const config = pairing ? pairing.scenario : entry
|
|
38
40
|
const scenarioName = nameByConfig.get(config)
|
|
39
41
|
if (!scenarioName) {
|
|
40
42
|
unresolved.push({ featureId, index })
|
|
@@ -44,10 +46,11 @@ export const resolveFeatureScenarios = (
|
|
|
44
46
|
featureId,
|
|
45
47
|
featureName: feature.name ?? featureId,
|
|
46
48
|
scenarioName,
|
|
47
|
-
data:
|
|
49
|
+
data: pairing?.data,
|
|
48
50
|
tags: [
|
|
49
51
|
...new Set([
|
|
50
|
-
...((registrations.get(scenarioName)?.func as
|
|
52
|
+
...((registrations.get(scenarioName)?.func as { tags?: string[] })
|
|
53
|
+
?.tags ?? []),
|
|
51
54
|
...(feature.tags ?? []),
|
|
52
55
|
]),
|
|
53
56
|
],
|
|
@@ -65,7 +65,7 @@ export function createGraph<RPCMap extends Record<string, RPCHandler>>() {
|
|
|
65
65
|
for (const [nodeId, def] of Object.entries(nodes) as [string, any][]) {
|
|
66
66
|
result[nodeId] = {
|
|
67
67
|
func: funcMap[nodeId] as string,
|
|
68
|
-
input: def?.input
|
|
68
|
+
input: def?.input,
|
|
69
69
|
next: def?.next,
|
|
70
70
|
onError: def?.onError,
|
|
71
71
|
retries: def?.retries,
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
import type { WorkflowRuntimeMeta } from '../workflow.types.js'
|
|
11
11
|
import { pikkuState } from '../../../pikku-state.js'
|
|
12
12
|
import { RPCNotFoundError } from '../../rpc/rpc-runner.js'
|
|
13
|
-
import { DEFAULT_STEP_RETRIES } from '../
|
|
13
|
+
import { DEFAULT_STEP_RETRIES } from '../workflow-constants.js'
|
|
14
14
|
|
|
15
15
|
describe('graph-runner bugs', () => {
|
|
16
16
|
test('continueGraph should NOT mark workflow completed while nodes are still running', async () => {
|
|
@@ -828,7 +828,9 @@ describe('graph-runner bugs', () => {
|
|
|
828
828
|
name: 'testRetries',
|
|
829
829
|
pikkuFuncId: 'testRetries',
|
|
830
830
|
source: 'graph',
|
|
831
|
-
|
|
831
|
+
// All three are dependency-free, so all three are entry nodes — a
|
|
832
|
+
// `startNode` may only name one the graph declared.
|
|
833
|
+
entryNodeIds: ['a', 'b', 'c'],
|
|
832
834
|
graphHash: 'retries-hash',
|
|
833
835
|
nodes: {
|
|
834
836
|
a: {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
+
import type { PikkuWorkflowService } from '../pikku-workflow-service.js'
|
|
1
2
|
import {
|
|
2
|
-
type PikkuWorkflowService,
|
|
3
3
|
WorkflowAsyncException,
|
|
4
4
|
WorkflowSuspendedException,
|
|
5
|
-
|
|
6
|
-
} from '../
|
|
5
|
+
} from '../workflow-errors.js'
|
|
6
|
+
import { DEFAULT_STEP_RETRIES } from '../workflow-constants.js'
|
|
7
7
|
import type { GraphWireState, PikkuGraphWire } from './workflow-graph.types.js'
|
|
8
8
|
import { pikkuState, getSingletonServices } from '../../../pikku-state.js'
|
|
9
9
|
import type { WorkflowRuntimeMeta, WorkflowRunWire } from '../workflow.types.js'
|
|
@@ -211,7 +211,7 @@ function isDataRef(value: unknown): value is { $ref: string; path?: string } {
|
|
|
211
211
|
typeof value === 'object' &&
|
|
212
212
|
value !== null &&
|
|
213
213
|
'$ref' in value &&
|
|
214
|
-
typeof (value as
|
|
214
|
+
typeof (value as { $ref?: unknown }).$ref === 'string'
|
|
215
215
|
)
|
|
216
216
|
}
|
|
217
217
|
|
|
@@ -227,7 +227,7 @@ function isTemplate(value: unknown): value is TemplateValue {
|
|
|
227
227
|
typeof value === 'object' &&
|
|
228
228
|
value !== null &&
|
|
229
229
|
'$template' in value &&
|
|
230
|
-
typeof (value as
|
|
230
|
+
typeof (value as { $template?: unknown }).$template === 'object'
|
|
231
231
|
)
|
|
232
232
|
}
|
|
233
233
|
|
|
@@ -695,14 +695,6 @@ export async function executeGraphStep(
|
|
|
695
695
|
}
|
|
696
696
|
}
|
|
697
697
|
|
|
698
|
-
export async function onGraphNodeComplete(
|
|
699
|
-
workflowService: PikkuWorkflowService,
|
|
700
|
-
runId: string,
|
|
701
|
-
graphName: string
|
|
702
|
-
): Promise<void> {
|
|
703
|
-
await continueGraph(workflowService, runId, graphName)
|
|
704
|
-
}
|
|
705
|
-
|
|
706
698
|
export async function runFromMeta(
|
|
707
699
|
workflowService: PikkuWorkflowService,
|
|
708
700
|
runId: string,
|
|
@@ -928,9 +920,14 @@ export async function runWorkflowGraph(
|
|
|
928
920
|
}
|
|
929
921
|
|
|
930
922
|
const nodes = meta.nodes
|
|
931
|
-
const
|
|
932
|
-
|
|
933
|
-
|
|
923
|
+
const declaredEntryNodes = meta.entryNodeIds ?? []
|
|
924
|
+
// knowledge: decisions/security/a-graph-run-starts-at-an-entry-node-the-graph-declared.md
|
|
925
|
+
if (startNode && !declaredEntryNodes.includes(startNode)) {
|
|
926
|
+
throw new Error(
|
|
927
|
+
`Workflow graph '${graphName}': '${startNode}' is not an entry node`
|
|
928
|
+
)
|
|
929
|
+
}
|
|
930
|
+
const entryNodes: string[] = startNode ? [startNode] : declaredEntryNodes
|
|
934
931
|
validateGraphReferences(graphName, nodes, entryNodes)
|
|
935
932
|
|
|
936
933
|
if (entryNodes.length === 0) {
|
|
@@ -16,11 +16,6 @@ export const isRef = (value: unknown): value is RefValue =>
|
|
|
16
16
|
'__isRef' in value &&
|
|
17
17
|
(value as RefValue).__isRef === true
|
|
18
18
|
|
|
19
|
-
export interface InputRef {
|
|
20
|
-
nodeId: string
|
|
21
|
-
path?: string
|
|
22
|
-
}
|
|
23
|
-
|
|
24
19
|
export type RefFn<NodeIds extends string = string> = (
|
|
25
20
|
nodeId: NodeIds,
|
|
26
21
|
path?: string
|
|
@@ -1,20 +1,28 @@
|
|
|
1
|
+
export { PikkuWorkflowService } from './pikku-workflow-service.js'
|
|
1
2
|
export {
|
|
2
|
-
PikkuWorkflowService,
|
|
3
3
|
WorkflowCancelledException,
|
|
4
4
|
WorkflowSuspendedException,
|
|
5
5
|
WorkflowDispatchException,
|
|
6
6
|
WorkflowNotFoundError,
|
|
7
7
|
WorkflowRunNotFoundError,
|
|
8
8
|
WorkflowApprovalResolvedError,
|
|
9
|
-
|
|
10
|
-
} from './
|
|
9
|
+
WorkflowStepFunctionMismatchError,
|
|
10
|
+
} from './workflow-errors.js'
|
|
11
|
+
export { DEFAULT_STEP_RETRIES } from './workflow-constants.js'
|
|
12
|
+
export {
|
|
13
|
+
assertWorkflowRunOwner,
|
|
14
|
+
WorkflowRunForbiddenError,
|
|
15
|
+
} from './workflow-run-ownership.js'
|
|
11
16
|
export type {
|
|
12
17
|
RunLifecycleContext,
|
|
13
18
|
WorkflowRunEngine,
|
|
14
19
|
WorkflowRunExtension,
|
|
15
|
-
} from './
|
|
20
|
+
} from './workflow-run-engine.types.js'
|
|
16
21
|
export { deriveInvocationId, uuidv5 } from './workflow-invocation-id.js'
|
|
17
22
|
|
|
23
|
+
export { isRef } from './graph/workflow-graph.types.js'
|
|
24
|
+
export type { RefValue } from './graph/workflow-graph.types.js'
|
|
25
|
+
|
|
18
26
|
export {
|
|
19
27
|
buildRunTimeline,
|
|
20
28
|
reconstructStateAt,
|