@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
|
@@ -4,6 +4,12 @@ import { pikkuState } from '../../pikku-state.js';
|
|
|
4
4
|
import { PikkuError, addError } from '../../errors/error-handler.js';
|
|
5
5
|
import { parseVersionedId } from '../../version.js';
|
|
6
6
|
import { resolveRemoteAddonToken } from './remote-addon-auth.js';
|
|
7
|
+
import { createAgentRPC } from '../ai-agent/agent-rpc.js';
|
|
8
|
+
/**
|
|
9
|
+
* The session for a wire: read through `getSession` when a runner attached one,
|
|
10
|
+
* otherwise whatever was placed on the wire directly.
|
|
11
|
+
*/
|
|
12
|
+
const resolveWireSession = async (wire) => typeof wire.getSession === 'function' ? await wire.getSession() : wire.session;
|
|
7
13
|
export class RPCNotFoundError extends PikkuError {
|
|
8
14
|
rpcName;
|
|
9
15
|
constructor(rpcName) {
|
|
@@ -36,9 +42,6 @@ addError(RemoteAddonRequestError, {
|
|
|
36
42
|
status: 502,
|
|
37
43
|
message: 'Remote addon request failed.',
|
|
38
44
|
});
|
|
39
|
-
import { runAIAgent, resumeAIAgentSync } from '../ai-agent/ai-agent-runner.js';
|
|
40
|
-
import { streamAIAgent, resumeAIAgent, interruptAIAgent, } from '../ai-agent/ai-agent-stream.js';
|
|
41
|
-
import { wrapChannelWithAGUI } from '../ai-agent/ai-agent-agui.js';
|
|
42
45
|
export const resolveNamespace = (namespacedFunction) => {
|
|
43
46
|
const colonIndex = namespacedFunction.indexOf(':');
|
|
44
47
|
if (colonIndex === -1) {
|
|
@@ -142,9 +145,7 @@ export class ContextAwareRPCService {
|
|
|
142
145
|
catch (e) {
|
|
143
146
|
if (e instanceof RPCNotFoundError) {
|
|
144
147
|
if (this.services.deploymentService) {
|
|
145
|
-
const session =
|
|
146
|
-
? await this.wire.getSession()
|
|
147
|
-
: this.wire.session;
|
|
148
|
+
const session = await resolveWireSession(this.wire);
|
|
148
149
|
return this.services.deploymentService.invoke(funcName, data, session, this.wire.traceId);
|
|
149
150
|
}
|
|
150
151
|
}
|
|
@@ -276,9 +277,7 @@ export class ContextAwareRPCService {
|
|
|
276
277
|
}
|
|
277
278
|
catch (e) {
|
|
278
279
|
if (e instanceof RPCNotFoundError && this.services.deploymentService) {
|
|
279
|
-
const session =
|
|
280
|
-
? await this.wire.getSession()
|
|
281
|
-
: this.wire.session;
|
|
280
|
+
const session = await resolveWireSession(this.wire);
|
|
282
281
|
return this.services.deploymentService.invoke(rpcName, data, session, this.wire.traceId);
|
|
283
282
|
}
|
|
284
283
|
throw e;
|
|
@@ -308,78 +307,22 @@ export class ContextAwareRPCService {
|
|
|
308
307
|
};
|
|
309
308
|
return this.services.workflowService.startWorkflow(workflowName, input, wire, this, options);
|
|
310
309
|
}
|
|
310
|
+
/**
|
|
311
|
+
* The agent facade, built on access.
|
|
312
|
+
*
|
|
313
|
+
* The implementation lives in `ai-agent/agent-rpc.ts` so the agent surface is
|
|
314
|
+
* one file rather than a wing of this one; a getter rather than a field so a
|
|
315
|
+
* request that never touches an agent never builds it.
|
|
316
|
+
*/
|
|
311
317
|
get agent() {
|
|
312
|
-
return
|
|
313
|
-
run: async (agentName, input) => {
|
|
314
|
-
const result = await runAIAgent(agentName, input, {
|
|
315
|
-
sessionService: this.options.sessionService,
|
|
316
|
-
getCredential: this.wire.getCredential?.bind(this.wire),
|
|
317
|
-
});
|
|
318
|
-
return {
|
|
319
|
-
runId: result.runId,
|
|
320
|
-
result: result.object ?? result.text,
|
|
321
|
-
usage: result.usage,
|
|
322
|
-
...(result.status === 'suspended' && {
|
|
323
|
-
status: 'suspended',
|
|
324
|
-
pendingApprovals: result.pendingApprovals,
|
|
325
|
-
}),
|
|
326
|
-
};
|
|
327
|
-
},
|
|
328
|
-
stream: async (agentName, input, options) => {
|
|
329
|
-
const channel = this.wire.channel;
|
|
330
|
-
if (!channel)
|
|
331
|
-
throw new Error('No channel available for streaming');
|
|
332
|
-
let currentRunId;
|
|
333
|
-
await streamAIAgent(agentName, input, wrapChannelWithAGUI(channel, {
|
|
334
|
-
threadId: input.threadId,
|
|
335
|
-
getRunId: () => currentRunId,
|
|
336
|
-
}), {
|
|
337
|
-
sessionService: this.options.sessionService,
|
|
338
|
-
getCredential: this.wire.getCredential?.bind(this.wire),
|
|
339
|
-
}, undefined, {
|
|
340
|
-
...options,
|
|
341
|
-
onRunCreated: (runId) => {
|
|
342
|
-
currentRunId = runId;
|
|
343
|
-
options?.onRunCreated?.(runId);
|
|
344
|
-
},
|
|
345
|
-
});
|
|
346
|
-
},
|
|
347
|
-
resume: async (runId, input, options) => {
|
|
348
|
-
const channel = this.wire.channel;
|
|
349
|
-
if (!channel)
|
|
350
|
-
throw new Error('No channel available for streaming');
|
|
351
|
-
await resumeAIAgent({ runId, ...input }, wrapChannelWithAGUI(channel, { runId }), {
|
|
352
|
-
sessionService: this.options.sessionService,
|
|
353
|
-
getCredential: this.wire.getCredential?.bind(this.wire),
|
|
354
|
-
}, options);
|
|
355
|
-
},
|
|
356
|
-
interrupt: async (runId, reason) => {
|
|
357
|
-
return interruptAIAgent({ runId, ...(reason ? { reason } : {}) }, { sessionService: this.options.sessionService });
|
|
358
|
-
},
|
|
359
|
-
approve: async (runId, approvals, expectedAgentName) => {
|
|
360
|
-
const result = await resumeAIAgentSync(runId, approvals, {
|
|
361
|
-
sessionService: this.options.sessionService,
|
|
362
|
-
}, expectedAgentName);
|
|
363
|
-
return {
|
|
364
|
-
runId: result.runId,
|
|
365
|
-
result: result.object ?? result.text,
|
|
366
|
-
usage: result.usage,
|
|
367
|
-
...(result.status === 'suspended' && {
|
|
368
|
-
status: 'suspended',
|
|
369
|
-
pendingApprovals: result.pendingApprovals,
|
|
370
|
-
}),
|
|
371
|
-
};
|
|
372
|
-
},
|
|
373
|
-
};
|
|
318
|
+
return createAgentRPC(this.wire, this.options);
|
|
374
319
|
}
|
|
375
320
|
async remote(funcName, data) {
|
|
376
321
|
if (!this.services.deploymentService) {
|
|
377
322
|
throw new Error(`No DeploymentService configured for remote RPC: ${funcName}. ` +
|
|
378
323
|
`Set up a DeploymentService to enable remote function calls.`);
|
|
379
324
|
}
|
|
380
|
-
const session =
|
|
381
|
-
? await this.wire.getSession()
|
|
382
|
-
: this.wire.session;
|
|
325
|
+
const session = await resolveWireSession(this.wire);
|
|
383
326
|
return this.services.deploymentService.invoke(funcName, data, session, this.wire.traceId);
|
|
384
327
|
}
|
|
385
328
|
}
|
|
@@ -390,18 +333,42 @@ export class PikkuRPCService {
|
|
|
390
333
|
? requiresAuthOrOptions
|
|
391
334
|
: { requiresAuth: requiresAuthOrOptions };
|
|
392
335
|
const serviceRPC = new ContextAwareRPCService(services, wire, options, packageName);
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
336
|
+
// `TypedRPC` is named by the caller from its generated RPC map. Nothing
|
|
337
|
+
// concrete can satisfy a type the caller has not chosen yet.
|
|
338
|
+
return new ContextRPCView(serviceRPC, depth);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
/**
|
|
342
|
+
* The per-invocation `wire.rpc`.
|
|
343
|
+
*
|
|
344
|
+
* A class rather than an object literal because `agent` has to stay lazy — most
|
|
345
|
+
* requests never touch it — and an accessor declared on an object literal is
|
|
346
|
+
* defined per instance, which drops the literal off V8's fast construction
|
|
347
|
+
* path. Measured at ~1.15µs per request as a literal against ~0.47µs here, on
|
|
348
|
+
* the hot path every request takes.
|
|
349
|
+
*
|
|
350
|
+
* knowledge: decisions/internals/the-per-invocation-rpc-view-is-a-class.md
|
|
351
|
+
*/
|
|
352
|
+
class ContextRPCView {
|
|
353
|
+
serviceRPC;
|
|
354
|
+
depth;
|
|
355
|
+
global = false;
|
|
356
|
+
invoke;
|
|
357
|
+
remote;
|
|
358
|
+
exposed;
|
|
359
|
+
startWorkflow;
|
|
360
|
+
rpcWithWire;
|
|
361
|
+
constructor(serviceRPC, depth) {
|
|
362
|
+
this.serviceRPC = serviceRPC;
|
|
363
|
+
this.depth = depth;
|
|
364
|
+
this.invoke = serviceRPC.rpc.bind(serviceRPC);
|
|
365
|
+
this.remote = serviceRPC.remote.bind(serviceRPC);
|
|
366
|
+
this.exposed = serviceRPC.rpcExposed.bind(serviceRPC);
|
|
367
|
+
this.startWorkflow = serviceRPC.startWorkflow.bind(serviceRPC);
|
|
368
|
+
this.rpcWithWire = serviceRPC.rpcWithWire.bind(serviceRPC);
|
|
369
|
+
}
|
|
370
|
+
get agent() {
|
|
371
|
+
return this.serviceRPC.agent;
|
|
405
372
|
}
|
|
406
373
|
}
|
|
407
374
|
export const rpcService = new PikkuRPCService();
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { PikkuRawWire } from '../../types/core.types.js';
|
|
1
2
|
import type { AgentInterruptResult } from '../ai-agent/ai-agent-interrupt.js';
|
|
2
3
|
export type PikkuRPC<Invoke extends (...args: any[]) => any = (...args: any[]) => any, Remote extends (...args: any[]) => any = (...args: any[]) => any, startWorkflow extends (...args: any[]) => any = (...args: any[]) => any, AgentRun extends (...args: any[]) => any = (...args: any[]) => any, AgentStream extends (...args: any[]) => any = (...args: any[]) => any> = {
|
|
3
4
|
depth: number;
|
|
@@ -5,6 +6,8 @@ export type PikkuRPC<Invoke extends (...args: any[]) => any = (...args: any[]) =
|
|
|
5
6
|
invoke: Invoke;
|
|
6
7
|
remote: Remote;
|
|
7
8
|
exposed: (name: string, data: any) => Promise<any>;
|
|
9
|
+
/** Invoke an RPC with explicit wire fields merged over the caller's. */
|
|
10
|
+
rpcWithWire: <In = any, Out = any>(rpcName: string, data: In, wire: PikkuRawWire) => Promise<Out>;
|
|
8
11
|
startWorkflow: startWorkflow;
|
|
9
12
|
agent: {
|
|
10
13
|
run: AgentRun;
|
|
@@ -34,6 +34,7 @@ export function validateAndBuildSecretDefinitionsMeta(definitions, schemaLookup)
|
|
|
34
34
|
schema: def.schema,
|
|
35
35
|
oauth2: def.oauth2,
|
|
36
36
|
rotationPeriod: def.rotationPeriod,
|
|
37
|
+
allowedHosts: def.allowedHosts,
|
|
37
38
|
docsUrl: def.docsUrl,
|
|
38
39
|
sourceFile: def.sourceFile,
|
|
39
40
|
};
|
|
@@ -50,6 +51,7 @@ export function validateAndBuildSecretDefinitionsMeta(definitions, schemaLookup)
|
|
|
50
51
|
schema: def.schema,
|
|
51
52
|
oauth2: def.oauth2,
|
|
52
53
|
rotationPeriod: def.rotationPeriod,
|
|
54
|
+
allowedHosts: def.allowedHosts,
|
|
53
55
|
docsUrl: def.docsUrl,
|
|
54
56
|
sourceFile: def.sourceFile,
|
|
55
57
|
};
|
|
@@ -5,10 +5,6 @@ export type TriggerTarget = {
|
|
|
5
5
|
targetName: string;
|
|
6
6
|
startNode?: string;
|
|
7
7
|
};
|
|
8
|
-
export type TriggerSourceInfo = {
|
|
9
|
-
name: string;
|
|
10
|
-
input: unknown;
|
|
11
|
-
};
|
|
12
8
|
export declare abstract class PikkuTriggerService implements TriggerService {
|
|
13
9
|
protected activeTriggers: Map<string, TriggerInstance>;
|
|
14
10
|
abstract start(): Promise<void>;
|
|
@@ -24,6 +24,7 @@ export const wireTriggerSource = (source) => {
|
|
|
24
24
|
if (triggerSources.has(source.name)) {
|
|
25
25
|
throw new Error(`Trigger source already exists: ${source.name}`);
|
|
26
26
|
}
|
|
27
|
+
// knowledge: decisions/internals/wiring-registries-erase-the-generics-their-wire-functions-capture.md
|
|
27
28
|
triggerSources.set(source.name, source);
|
|
28
29
|
addFunction(triggerSourceMeta.pikkuFuncId, source.func, triggerSourceMeta.packageName);
|
|
29
30
|
};
|
|
@@ -97,7 +97,9 @@ export const rememberIds = (body, into, depth = 0) => {
|
|
|
97
97
|
return into;
|
|
98
98
|
}
|
|
99
99
|
for (const [key, value] of Object.entries(body)) {
|
|
100
|
-
if (typeof value === 'string' &&
|
|
100
|
+
if (typeof value === 'string' &&
|
|
101
|
+
MEMORABLE.test(key) &&
|
|
102
|
+
value.length < 200) {
|
|
101
103
|
into[key] = value;
|
|
102
104
|
}
|
|
103
105
|
else if (typeof value === 'object') {
|
|
@@ -203,7 +205,8 @@ export const runVirtualUser = async (params) => {
|
|
|
203
205
|
stoppedBy = 'budget-duration';
|
|
204
206
|
break;
|
|
205
207
|
}
|
|
206
|
-
if (budget?.mutations !== undefined &&
|
|
208
|
+
if (budget?.mutations !== undefined &&
|
|
209
|
+
tally.mutations >= budget.mutations) {
|
|
207
210
|
stoppedBy = 'budget-mutations';
|
|
208
211
|
break;
|
|
209
212
|
}
|
|
@@ -243,10 +246,7 @@ export const runVirtualUser = async (params) => {
|
|
|
243
246
|
if (tick.move === 'resume' || tick.move === 'suspend') {
|
|
244
247
|
messages.push(msg('user', `You were pulled away and have come back to this. Things may have changed since.`));
|
|
245
248
|
}
|
|
246
|
-
//
|
|
247
|
-
// `stale` almost always trusts them, a newcomer has none to trust, and an
|
|
248
|
-
// auditor checks nearly everything — which is the whole difference between
|
|
249
|
-
// those runs, expressed as one roll rather than as prose.
|
|
249
|
+
// knowledge: decisions/internals/a-virtual-user-decides-whether-to-trust-memory-once-per-turn.md
|
|
250
250
|
if (Object.keys(memory).length && !rng.chance(profile.reReadRate)) {
|
|
251
251
|
messages.push(msg('user', `Things you have noted down: ${Object.entries(memory)
|
|
252
252
|
.slice(-25)
|
|
@@ -365,10 +365,7 @@ export const runVirtualUser = async (params) => {
|
|
|
365
365
|
steps.push(record);
|
|
366
366
|
continue;
|
|
367
367
|
}
|
|
368
|
-
//
|
|
369
|
-
// read-only disposition is never offered a mutation and an approval-gated
|
|
370
|
-
// endpoint is never offered at all, so `index` is the gate and there is no
|
|
371
|
-
// second one here to drift out of step with it.
|
|
368
|
+
// knowledge: decisions/internals/the-virtual-user-catalogue-is-the-only-gate-on-what-may-be-called.md
|
|
372
369
|
const mutating = !isReadOnly(entry);
|
|
373
370
|
const args = readArgs(action.args);
|
|
374
371
|
const repeats = profile.repeatRate > 0 && rng.chance(profile.repeatRate) ? 2 : 1;
|
|
@@ -379,7 +376,10 @@ export const runVirtualUser = async (params) => {
|
|
|
379
376
|
response = await target.call(entry.name, args);
|
|
380
377
|
}
|
|
381
378
|
catch (error) {
|
|
382
|
-
record.findingKinds = [
|
|
379
|
+
record.findingKinds = [
|
|
380
|
+
...(record.findingKinds ?? []),
|
|
381
|
+
'transport-error',
|
|
382
|
+
];
|
|
383
383
|
addFinding({
|
|
384
384
|
kind: 'transport-error',
|
|
385
385
|
detail: `${entry.name} threw: ${error.message}`,
|
|
@@ -18,24 +18,10 @@ const topLevelKeys = (schema) => {
|
|
|
18
18
|
export const deriveCatalogue = (functions, schemas = {}) => {
|
|
19
19
|
const entries = [];
|
|
20
20
|
for (const [name, meta] of Object.entries(functions ?? {})) {
|
|
21
|
-
//
|
|
22
|
-
// held out of every deployed unit — so offering one would only ever waste
|
|
23
|
-
// a turn on a 404.
|
|
24
|
-
//
|
|
25
|
-
// For a platform or addon step this is not an efficiency argument but the
|
|
26
|
-
// oracle: a virtual user's findings are worth something only because it
|
|
27
|
-
// cannot manufacture the outcomes it is meant to discover. One that could
|
|
28
|
-
// invoke "Stripe's webhook arrives" would forge its own payment success,
|
|
29
|
-
// and every finding downstream of that is worthless. Same class of argument
|
|
30
|
-
// as `allowApprovalRequired` defaulting to false — and enforced here, at
|
|
31
|
-
// derivation, rather than by convention.
|
|
21
|
+
// knowledge: decisions/security/a-virtual-user-is-never-offered-a-step-that-would-forge-its-own-oracle.md
|
|
32
22
|
if (meta.scenario || meta.scenarioStep || meta.scenarioStepKind)
|
|
33
23
|
continue;
|
|
34
|
-
//
|
|
35
|
-
// what the shipped target calls over. Absent is not permissive: an unexposed
|
|
36
|
-
// function 404s, and offering one spends a step to learn nothing about the
|
|
37
|
-
// product. Measured on the e2e app, 34 of its 72 functions are in exactly
|
|
38
|
-
// that state — nearly half a catalogue that cannot be called.
|
|
24
|
+
// knowledge: decisions/internals/only-exposed-functions-enter-a-virtual-user-catalogue.md
|
|
39
25
|
if (meta.expose !== true)
|
|
40
26
|
continue;
|
|
41
27
|
const inputSchema = meta.inputSchemaName
|
|
@@ -76,10 +62,7 @@ const scenarioSteps = (meta) => {
|
|
|
76
62
|
? [{ stepFunc: step.stepFunc, phase: step.phase, actor: step.actor }]
|
|
77
63
|
: []);
|
|
78
64
|
}
|
|
79
|
-
//
|
|
80
|
-
// the scenario declares them. Following `next` instead would buy an ordering
|
|
81
|
-
// that is already true and cost a traversal that has to decide what a branch
|
|
82
|
-
// means to a reader who will never take one.
|
|
65
|
+
// knowledge: decisions/internals/virtual-user-step-order-comes-from-insertion-order.md
|
|
83
66
|
return Object.values(meta.nodes ?? {}).flatMap((node) => {
|
|
84
67
|
const { rpcName, scenarioStepPhase, actor } = (node ?? {});
|
|
85
68
|
if (!rpcName || !scenarioStepPhase)
|
|
@@ -115,11 +98,7 @@ export const deriveIntents = (workflows, functions = {}) => {
|
|
|
115
98
|
continue;
|
|
116
99
|
steps.push(composeStepProse({
|
|
117
100
|
phase: step.phase,
|
|
118
|
-
//
|
|
119
|
-
// placeholders from a run that happened; there is no run yet, and
|
|
120
|
-
// "invites {email}" tells a user to pick someone where the filled-in
|
|
121
|
-
// form would have told it whom — which is the scenario's answer, not
|
|
122
|
-
// the user's own.
|
|
101
|
+
// knowledge: decisions/internals/a-scenario-step-template-is-offered-unfilled.md
|
|
123
102
|
description: template ?? description ?? '',
|
|
124
103
|
actor: step.actor,
|
|
125
104
|
}));
|
|
@@ -78,10 +78,7 @@ export const DISPOSITIONS = {
|
|
|
78
78
|
reReadRate: 0.2,
|
|
79
79
|
invertedOracle: true,
|
|
80
80
|
},
|
|
81
|
-
//
|
|
82
|
-
// it abandons rarely, runs cool, and is told to stop and say so rather than
|
|
83
|
-
// guess — the failure mode here is a wrong action nobody asked for, not a
|
|
84
|
-
// missed bug.
|
|
81
|
+
// knowledge: decisions/internals/the-worker-disposition-is-the-one-that-is-not-testing.md
|
|
85
82
|
accountable: {
|
|
86
83
|
...REALISTIC,
|
|
87
84
|
instructions: [
|
|
@@ -288,7 +288,7 @@ export interface ReturnStepMeta {
|
|
|
288
288
|
spread?: string[];
|
|
289
289
|
}
|
|
290
290
|
/**
|
|
291
|
-
* Inline step metadata
|
|
291
|
+
* Inline step metadata
|
|
292
292
|
*/
|
|
293
293
|
export interface InlineStepMeta {
|
|
294
294
|
/** Inline form - local execution */
|
|
@@ -473,7 +473,34 @@ export interface PikkuWorkflowWire {
|
|
|
473
473
|
/** Suspend workflow until a human records a decision against this gate */
|
|
474
474
|
approval: WorkflowWireApproval;
|
|
475
475
|
}
|
|
476
|
-
|
|
476
|
+
/**
|
|
477
|
+
* What a scenario has accumulated so far, shared by its body and its hooks.
|
|
478
|
+
*
|
|
479
|
+
* Typed as a partial of the scenario's own output because a scenario can fail
|
|
480
|
+
* at any step: the shape describes what a *completed* run produces, and the
|
|
481
|
+
* context holds however much of it this run reached. A scenario declaring no
|
|
482
|
+
* object output gets an open record rather than `Partial<never>`.
|
|
483
|
+
*
|
|
484
|
+
* `any` collapses to `any`, not to the union of both branches. `PikkuWire`'s
|
|
485
|
+
* default depends on that: the wire type is also used as a generic *constraint*,
|
|
486
|
+
* and a constraint carrying a concrete context would reject every wire whose
|
|
487
|
+
* scenario output differs from it.
|
|
488
|
+
*/
|
|
489
|
+
export type ScenarioContext<Out = unknown> = 0 extends 1 & Out ? any : Out extends object ? Partial<Out> : Record<string, unknown>;
|
|
490
|
+
export interface PikkuScenarioWire<Out = unknown> extends PikkuWorkflowWire {
|
|
491
|
+
/**
|
|
492
|
+
* Scratch the body writes and the `before`/`after` hooks read.
|
|
493
|
+
*
|
|
494
|
+
* A hook is a separate function, so it cannot see the body's locals — which
|
|
495
|
+
* is why teardown had nowhere to learn the ids the body minted. Assigning
|
|
496
|
+
* them here (`scenario.context.projectId = project.projectId`) hands them to
|
|
497
|
+
* `after`, which runs in a `finally` and so cleans up on a failed run too.
|
|
498
|
+
*
|
|
499
|
+
* Deliberately *not* a world: it is scoped to one run, and scenario steps
|
|
500
|
+
* cannot reach it. Steps stay pure functions of their declared inputs, so the
|
|
501
|
+
* ladder in the report remains a complete account of what a step saw.
|
|
502
|
+
*/
|
|
503
|
+
context: ScenarioContext<Out>;
|
|
477
504
|
/**
|
|
478
505
|
* Durable polling step: invoke `rpcName` (as an actor when `options.actor`
|
|
479
506
|
* is set) until `predicate` passes or `options.within` elapses.
|
|
@@ -13,8 +13,10 @@ export const resolveFeatureScenarios = (features, registrations) => {
|
|
|
13
13
|
const scenarios = feature.scenarios ?? [];
|
|
14
14
|
for (let index = 0; index < scenarios.length; index++) {
|
|
15
15
|
const entry = scenarios[index];
|
|
16
|
-
const
|
|
17
|
-
|
|
16
|
+
const pairing = typeof entry === 'object' && entry !== null && 'scenario' in entry
|
|
17
|
+
? entry
|
|
18
|
+
: undefined;
|
|
19
|
+
const config = pairing ? pairing.scenario : entry;
|
|
18
20
|
const scenarioName = nameByConfig.get(config);
|
|
19
21
|
if (!scenarioName) {
|
|
20
22
|
unresolved.push({ featureId, index });
|
|
@@ -24,10 +26,11 @@ export const resolveFeatureScenarios = (features, registrations) => {
|
|
|
24
26
|
featureId,
|
|
25
27
|
featureName: feature.name ?? featureId,
|
|
26
28
|
scenarioName,
|
|
27
|
-
data:
|
|
29
|
+
data: pairing?.data,
|
|
28
30
|
tags: [
|
|
29
31
|
...new Set([
|
|
30
|
-
...(registrations.get(scenarioName)?.func
|
|
32
|
+
...(registrations.get(scenarioName)?.func
|
|
33
|
+
?.tags ?? []),
|
|
31
34
|
...(feature.tags ?? []),
|
|
32
35
|
]),
|
|
33
36
|
],
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { PikkuWorkflowService } from '../pikku-workflow-service.js';
|
|
2
2
|
import type { WorkflowRuntimeMeta, WorkflowRunWire } from '../workflow.types.js';
|
|
3
3
|
export declare class ChildWorkflowStartedException extends Error {
|
|
4
4
|
parentRunId: string;
|
|
@@ -9,7 +9,6 @@ export declare class ChildWorkflowStartedException extends Error {
|
|
|
9
9
|
}
|
|
10
10
|
export declare function continueGraph(workflowService: PikkuWorkflowService, runId: string, graphName: string, overrideMeta?: WorkflowRuntimeMeta): Promise<void>;
|
|
11
11
|
export declare function executeGraphStep(workflowService: PikkuWorkflowService, rpcService: any, runId: string, stepId: string, nodeId: string, rpcName: string, data: any, graphName: string): Promise<any>;
|
|
12
|
-
export declare function onGraphNodeComplete(workflowService: PikkuWorkflowService, runId: string, graphName: string): Promise<void>;
|
|
13
12
|
export declare function runFromMeta(workflowService: PikkuWorkflowService, runId: string, meta: WorkflowRuntimeMeta, _rpcService: any): Promise<void>;
|
|
14
13
|
export declare function runWorkflowGraph(workflowService: PikkuWorkflowService, graphName: string, triggerInput: any, rpcService?: any, inline?: boolean, startNode?: string, wire?: WorkflowRunWire, overrideMeta?: WorkflowRuntimeMeta): Promise<{
|
|
15
14
|
runId: string;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { WorkflowAsyncException, WorkflowSuspendedException,
|
|
1
|
+
import { WorkflowAsyncException, WorkflowSuspendedException, } from '../workflow-errors.js';
|
|
2
|
+
import { DEFAULT_STEP_RETRIES } from '../workflow-constants.js';
|
|
2
3
|
import { pikkuState, getSingletonServices } from '../../../pikku-state.js';
|
|
3
4
|
import { RPCNotFoundError } from '../../rpc/rpc-runner.js';
|
|
4
5
|
export class ChildWorkflowStartedException extends Error {
|
|
@@ -491,9 +492,6 @@ export async function executeGraphStep(workflowService, rpcService, runId, stepI
|
|
|
491
492
|
throw error;
|
|
492
493
|
}
|
|
493
494
|
}
|
|
494
|
-
export async function onGraphNodeComplete(workflowService, runId, graphName) {
|
|
495
|
-
await continueGraph(workflowService, runId, graphName);
|
|
496
|
-
}
|
|
497
495
|
export async function runFromMeta(workflowService, runId, meta, _rpcService) {
|
|
498
496
|
await continueGraph(workflowService, runId, meta.name, meta);
|
|
499
497
|
}
|
|
@@ -610,9 +608,12 @@ export async function runWorkflowGraph(workflowService, graphName, triggerInput,
|
|
|
610
608
|
throw new Error(`Workflow graph '${graphName}' not found`);
|
|
611
609
|
}
|
|
612
610
|
const nodes = meta.nodes;
|
|
613
|
-
const
|
|
614
|
-
|
|
615
|
-
|
|
611
|
+
const declaredEntryNodes = meta.entryNodeIds ?? [];
|
|
612
|
+
// knowledge: decisions/security/a-graph-run-starts-at-an-entry-node-the-graph-declared.md
|
|
613
|
+
if (startNode && !declaredEntryNodes.includes(startNode)) {
|
|
614
|
+
throw new Error(`Workflow graph '${graphName}': '${startNode}' is not an entry node`);
|
|
615
|
+
}
|
|
616
|
+
const entryNodes = startNode ? [startNode] : declaredEntryNodes;
|
|
616
617
|
validateGraphReferences(graphName, nodes, entryNodes);
|
|
617
618
|
if (entryNodes.length === 0) {
|
|
618
619
|
throw new Error(`Workflow graph '${graphName}': no entry nodes found in meta or startNode`);
|
|
@@ -5,10 +5,6 @@ export interface RefValue {
|
|
|
5
5
|
}
|
|
6
6
|
export declare const createRef: (nodeId: string, path?: string) => RefValue;
|
|
7
7
|
export declare const isRef: (value: unknown) => value is RefValue;
|
|
8
|
-
export interface InputRef {
|
|
9
|
-
nodeId: string;
|
|
10
|
-
path?: string;
|
|
11
|
-
}
|
|
12
8
|
export type RefFn<NodeIds extends string = string> = (nodeId: NodeIds, path?: string) => RefValue;
|
|
13
9
|
export type NextConfig<NodeIds extends string = string> = NodeIds | NodeIds[] | Record<string, NodeIds | NodeIds[]>;
|
|
14
10
|
export interface GraphNodeConfig<NodeIds extends string = string> {
|
|
@@ -1,6 +1,11 @@
|
|
|
1
|
-
export { PikkuWorkflowService
|
|
2
|
-
export
|
|
1
|
+
export { PikkuWorkflowService } from './pikku-workflow-service.js';
|
|
2
|
+
export { WorkflowCancelledException, WorkflowSuspendedException, WorkflowDispatchException, WorkflowNotFoundError, WorkflowRunNotFoundError, WorkflowApprovalResolvedError, WorkflowStepFunctionMismatchError, } from './workflow-errors.js';
|
|
3
|
+
export { DEFAULT_STEP_RETRIES } from './workflow-constants.js';
|
|
4
|
+
export { assertWorkflowRunOwner, WorkflowRunForbiddenError, } from './workflow-run-ownership.js';
|
|
5
|
+
export type { RunLifecycleContext, WorkflowRunEngine, WorkflowRunExtension, } from './workflow-run-engine.types.js';
|
|
3
6
|
export { deriveInvocationId, uuidv5 } from './workflow-invocation-id.js';
|
|
7
|
+
export { isRef } from './graph/workflow-graph.types.js';
|
|
8
|
+
export type { RefValue } from './graph/workflow-graph.types.js';
|
|
4
9
|
export { buildRunTimeline, reconstructStateAt, reconstructFinalState, } from './run-timeline.js';
|
|
5
10
|
export type { RunTimeline, RunTimelineEvent, ReconstructedRunState, ReconstructedStep, RunPhase, } from './run-timeline.js';
|
|
6
11
|
export { addWorkflow } from './dsl/workflow-runner.js';
|
|
@@ -1,5 +1,9 @@
|
|
|
1
|
-
export { PikkuWorkflowService
|
|
1
|
+
export { PikkuWorkflowService } from './pikku-workflow-service.js';
|
|
2
|
+
export { WorkflowCancelledException, WorkflowSuspendedException, WorkflowDispatchException, WorkflowNotFoundError, WorkflowRunNotFoundError, WorkflowApprovalResolvedError, WorkflowStepFunctionMismatchError, } from './workflow-errors.js';
|
|
3
|
+
export { DEFAULT_STEP_RETRIES } from './workflow-constants.js';
|
|
4
|
+
export { assertWorkflowRunOwner, WorkflowRunForbiddenError, } from './workflow-run-ownership.js';
|
|
2
5
|
export { deriveInvocationId, uuidv5 } from './workflow-invocation-id.js';
|
|
6
|
+
export { isRef } from './graph/workflow-graph.types.js';
|
|
3
7
|
export { buildRunTimeline, reconstructStateAt, reconstructFinalState, } from './run-timeline.js';
|
|
4
8
|
export { addWorkflow } from './dsl/workflow-runner.js';
|
|
5
9
|
export { addFeature, resolveFeatureScenarios } from './feature.js';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { PikkuError } from '../../errors/error-handler.js';
|
|
2
2
|
import { InMemoryWorkflowService } from '../../services/in-memory-workflow-service.js';
|
|
3
|
-
import {
|
|
4
|
-
import type {
|
|
3
|
+
import type { RunLifecycleContext, WorkflowRunEngine, WorkflowRunExtension } from './workflow-run-engine.types.js';
|
|
4
|
+
import type { PikkuRawWire } from '../../types/core.types.js';
|
|
5
5
|
import type { ScenarioPersonas } from '../../services/personas-service.js';
|
|
6
6
|
import type { ScenarioBrowserProvider, ScenarioEnvironment, ScenarioSurface } from './scenario-step.types.js';
|
|
7
7
|
import type { PikkuWorkflowWire, WorkflowQueueOptions } from './workflow.types.js';
|
|
@@ -126,6 +126,7 @@ export declare class ScenarioNoWitness extends PikkuError {
|
|
|
126
126
|
export declare class PikkuScenarioService implements WorkflowRunExtension {
|
|
127
127
|
private readonly engine;
|
|
128
128
|
private runActors;
|
|
129
|
+
private runContexts;
|
|
129
130
|
private scenarioBrowserProvider?;
|
|
130
131
|
private scenarioEnvironment?;
|
|
131
132
|
private runSurface;
|
|
@@ -154,7 +155,20 @@ export declare class PikkuScenarioService implements WorkflowRunExtension {
|
|
|
154
155
|
actors?: ScenarioPersonas;
|
|
155
156
|
}): Promise<void>;
|
|
156
157
|
detachRunContext(runId: string): void;
|
|
157
|
-
|
|
158
|
+
/**
|
|
159
|
+
* What the run has accumulated, for a reporter that wants to say what a
|
|
160
|
+
* failed scenario left behind. Undefined for a plain workflow.
|
|
161
|
+
*/
|
|
162
|
+
getRunContext(runId: string): Record<string, unknown> | undefined;
|
|
163
|
+
/**
|
|
164
|
+
* The run's context, created on demand.
|
|
165
|
+
*
|
|
166
|
+
* `attachRunContext` seeds it for an ordinary scenario run, but the wire is
|
|
167
|
+
* also built on paths that skip attach; without this the body would be
|
|
168
|
+
* writing to an `undefined` and teardown would read nothing.
|
|
169
|
+
*/
|
|
170
|
+
private contextForRun;
|
|
171
|
+
decorateRunWire(wire: PikkuRawWire, context: {
|
|
158
172
|
runId: string;
|
|
159
173
|
workflowMeta: any;
|
|
160
174
|
workflowWire: PikkuWorkflowWire;
|