@pikku/core 0.12.84 → 0.12.86
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 +424 -0
- package/dist/bootstrap-compat/root.d.ts +20 -0
- package/dist/classification/column-form.d.ts +32 -0
- package/dist/classification/column-form.js +42 -0
- package/dist/classification/data-classification.d.ts +64 -0
- package/dist/classification/index.d.ts +13 -0
- package/dist/classification/index.js +2 -0
- package/dist/classification/secret-value.d.ts +56 -0
- package/dist/classification/secret-value.js +46 -0
- package/dist/crypto-utils.d.ts +1 -1
- package/dist/ecosystem/agent-scorer.d.ts +1 -1
- package/dist/ecosystem/agent.d.ts +4 -1
- package/dist/ecosystem/channel/local.d.ts +2 -0
- package/dist/ecosystem/channel/local.js +2 -0
- package/dist/ecosystem/channel/serverless.d.ts +9 -0
- package/dist/ecosystem/channel/serverless.js +1 -0
- package/dist/ecosystem/channel.d.ts +3 -0
- package/dist/ecosystem/channel.js +1 -0
- package/dist/ecosystem/cli.d.ts +1 -1
- package/dist/ecosystem/cli.js +1 -1
- package/dist/ecosystem/credential.d.ts +1 -0
- package/dist/ecosystem/crypto-utils.d.ts +8 -0
- package/dist/ecosystem/crypto-utils.js +1 -0
- package/dist/ecosystem/dev.d.ts +1 -0
- package/dist/ecosystem/dev.js +1 -0
- package/{src/ecosystem/testing.ts → dist/ecosystem/errors.d.ts} +3 -3
- package/dist/ecosystem/errors.js +2 -0
- package/dist/ecosystem/function.d.ts +1 -1
- package/dist/ecosystem/function.js +1 -1
- package/dist/ecosystem/http.d.ts +3 -1
- package/dist/ecosystem/http.js +3 -1
- package/dist/ecosystem/mcp.d.ts +1 -1
- package/dist/ecosystem/mcp.js +1 -1
- package/dist/ecosystem/middleware.d.ts +2 -1
- package/dist/ecosystem/middleware.js +2 -1
- package/dist/ecosystem/persona.d.ts +4 -2
- package/dist/ecosystem/persona.js +2 -1
- package/dist/ecosystem/queue.d.ts +4 -2
- package/dist/ecosystem/queue.js +3 -1
- package/dist/ecosystem/role.d.ts +1 -0
- package/dist/ecosystem/rpc.d.ts +2 -0
- package/dist/ecosystem/rpc.js +1 -0
- package/dist/ecosystem/scenario.d.ts +8 -2
- package/dist/ecosystem/scenario.js +5 -1
- package/dist/ecosystem/scheduler.d.ts +2 -1
- package/dist/ecosystem/scheduler.js +2 -1
- package/dist/ecosystem/schema.d.ts +2 -1
- package/dist/ecosystem/schema.js +1 -1
- package/dist/ecosystem/scope.d.ts +2 -0
- package/dist/ecosystem/scope.js +1 -0
- package/dist/ecosystem/secret.d.ts +2 -0
- package/dist/ecosystem/secret.js +1 -0
- package/dist/ecosystem/services/local-content.d.ts +2 -1
- package/dist/ecosystem/services/local-content.js +1 -1
- package/dist/ecosystem/services/local-meta.d.ts +1 -0
- package/dist/ecosystem/services/local-meta.js +1 -0
- package/dist/ecosystem/services.d.ts +11 -3
- package/dist/ecosystem/services.js +7 -1
- package/dist/ecosystem/trigger.d.ts +1 -0
- package/dist/ecosystem/trigger.js +1 -1
- package/dist/ecosystem/types.d.ts +6 -3
- package/dist/ecosystem/types.js +3 -2
- package/dist/ecosystem/variable.d.ts +1 -0
- package/dist/ecosystem/virtual-user.d.ts +3 -0
- package/dist/ecosystem/virtual-user.js +3 -0
- package/dist/ecosystem/workflow.d.ts +2 -0
- package/dist/ecosystem/workflow.js +2 -0
- package/dist/errors/index.d.ts +8 -0
- package/dist/errors/index.js +1 -0
- package/dist/errors/serialized-error.d.ts +7 -0
- package/dist/function/function-meta.types.d.ts +139 -0
- package/dist/function/function-runner.d.ts +3 -2
- package/dist/function/function-runner.js +31 -11
- package/dist/function/functions.types.d.ts +10 -2
- package/dist/function/index.d.ts +8 -2
- package/dist/function/index.js +4 -1
- package/dist/function/pikku-request.d.ts +8 -0
- package/dist/function/pikku-request.js +15 -0
- package/dist/middleware/auth-apikey.d.ts +2 -10
- package/dist/middleware/auth-apikey.js +1 -1
- package/dist/middleware/auth-bearer.d.ts +2 -10
- package/dist/middleware/auth-bearer.js +1 -1
- package/dist/middleware/auth-cookie.d.ts +2 -10
- package/dist/middleware/auth-cookie.js +1 -1
- package/dist/middleware/cors.d.ts +5 -11
- package/dist/middleware/cors.js +13 -6
- package/dist/middleware/index.d.ts +3 -0
- package/dist/middleware/index.js +1 -0
- package/dist/middleware/middleware-factories.d.ts +9 -0
- package/dist/middleware/middleware-factories.js +19 -0
- package/dist/middleware/middleware.types.d.ts +27 -0
- package/dist/middleware/middleware.types.js +1 -0
- package/dist/middleware/remote-auth.d.ts +2 -2
- package/dist/middleware/remote-auth.js +1 -1
- package/dist/middleware/telemetry.d.ts +4 -20
- package/dist/middleware/telemetry.js +1 -1
- package/dist/middleware-runner.d.ts +2 -1
- package/dist/pikku-state.d.ts +2 -1
- package/dist/services/audit-service.d.ts +1 -1
- package/dist/services/credential-wire-service.js +1 -1
- package/dist/services/email-service.d.ts +1 -1
- package/dist/services/file-scenario-run-store.d.ts +58 -0
- package/dist/services/file-scenario-run-store.js +214 -0
- package/dist/services/in-memory-workflow-service.d.ts +1 -1
- package/dist/services/index.d.ts +5 -1
- package/dist/services/index.js +3 -0
- package/dist/services/local-secrets.d.ts +1 -1
- package/dist/services/local-secrets.js +1 -1
- package/dist/services/logger.d.ts +7 -7
- package/dist/services/meta-service.d.ts +2 -1
- package/dist/services/scoped-secret-service.d.ts +1 -1
- package/dist/services/secret-service.d.ts +1 -1
- package/dist/services/typed-secret-service.d.ts +16 -2
- package/dist/services/typed-secret-service.js +5 -0
- package/dist/services/webhook-service.d.ts +1 -1
- package/dist/services/workflow-service.d.ts +2 -1
- package/dist/testing/index.d.ts +1 -0
- package/dist/testing/index.js +1 -0
- package/dist/types/core.types.d.ts +30 -180
- package/dist/types/core.types.js +1 -19
- package/dist/types/index.d.ts +2 -0
- package/dist/types/index.js +1 -0
- package/dist/types/state.types.d.ts +3 -1
- package/dist/utils.d.ts +13 -0
- package/dist/utils.js +2 -0
- package/dist/wirings/addon/addon-runner.d.ts +17 -0
- package/dist/wirings/addon/addon-runner.js +150 -0
- package/dist/wirings/addon/index.d.ts +6 -0
- package/dist/wirings/addon/index.js +3 -0
- package/dist/wirings/addon/remote-addon-auth.d.ts +16 -0
- package/dist/wirings/addon/remote-addon-auth.js +39 -0
- package/dist/wirings/addon/wire-addon.d.ts +80 -0
- package/dist/wirings/addon/wire-addon.js +120 -0
- package/dist/wirings/addon/wire-remote-addon.d.ts +22 -0
- package/dist/wirings/addon/wire-remote-addon.js +11 -0
- package/dist/wirings/agent/agent-prepare.d.ts +3 -3
- package/dist/wirings/agent/agent-prepare.js +1 -1
- package/dist/wirings/agent/agent-runner.js +2 -3
- package/dist/wirings/agent/agent-stream.js +2 -2
- package/dist/wirings/agent/agent-utils.d.ts +13 -0
- package/dist/wirings/agent/agent-utils.js +16 -0
- package/dist/wirings/agent/agent.types.d.ts +3 -2
- package/dist/wirings/agent/index.d.ts +1 -1
- package/dist/wirings/agent/voice-input.js +1 -1
- package/dist/wirings/agent/voice-output.js +1 -1
- package/dist/wirings/agent-scorer/agent-scorer-judge.d.ts +13 -0
- package/dist/wirings/agent-scorer/agent-scorer-judge.js +52 -0
- package/dist/wirings/agent-scorer/agent-scorer.d.ts +7 -1
- package/dist/wirings/agent-scorer/agent-scorer.js +1 -0
- package/dist/wirings/agent-scorer/agent-scorer.types.d.ts +20 -0
- package/dist/wirings/agent-scorer/index.d.ts +1 -1
- package/dist/wirings/channel/channel-common.d.ts +2 -1
- package/dist/wirings/channel/channel-middleware-runner.d.ts +2 -1
- package/dist/wirings/channel/channel.types.d.ts +1 -1
- package/dist/wirings/channel/serverless/index.d.ts +1 -0
- package/dist/wirings/cli/channel/index.d.ts +1 -1
- package/dist/wirings/cli/cli-runner.d.ts +2 -1
- package/dist/wirings/cli/cli-runner.js +4 -1
- package/dist/wirings/cli/cli.types.d.ts +21 -2
- package/dist/wirings/cli/command-parser.js +157 -23
- package/dist/wirings/gateway/gateway.types.d.ts +2 -1
- package/dist/wirings/http/http-runner.d.ts +1 -1
- package/dist/wirings/http/http-runner.js +1 -1
- package/dist/wirings/http/http.types.d.ts +2 -2
- package/dist/wirings/http/index.d.ts +1 -0
- package/dist/wirings/http/index.js +1 -0
- package/dist/wirings/http/pikku-fetch-http-request.js +1 -1
- package/dist/wirings/http/pikku-fetch-http-response.js +2 -2
- package/dist/wirings/mcp/mcp.types.d.ts +1 -1
- package/dist/wirings/persona/index.d.ts +1 -1
- package/dist/wirings/persona/index.js +1 -1
- package/dist/wirings/persona/persona.types.d.ts +14 -0
- package/dist/wirings/queue/queue.types.d.ts +1 -1
- package/dist/wirings/queue/signed-queue-service.d.ts +1 -1
- package/dist/wirings/rpc/index.d.ts +0 -6
- package/dist/wirings/rpc/index.js +0 -3
- package/dist/wirings/rpc/remote-addon-auth.d.ts +1 -1
- package/dist/wirings/rpc/remote-addon-auth.js +1 -1
- package/dist/wirings/rpc/rpc-runner.js +2 -2
- package/dist/wirings/rpc/wire-addon.d.ts +1 -1
- package/dist/wirings/scheduler/scheduler.types.d.ts +2 -1
- package/dist/wirings/scope/index.d.ts +1 -0
- package/dist/wirings/scope/index.js +1 -0
- package/dist/wirings/secret/secret.types.d.ts +10 -0
- package/dist/wirings/secret/validate-secret-definitions.js +2 -0
- package/dist/wirings/variable/validate-variable-definitions.js +2 -0
- package/dist/wirings/variable/variable.types.d.ts +9 -0
- package/dist/wirings/virtual-user/index.d.ts +1 -1
- package/dist/wirings/virtual-user/index.js +1 -1
- package/dist/wirings/virtual-user/prepare-virtual-user-run.d.ts +1 -1
- package/dist/wirings/virtual-user/virtual-user-derive.d.ts +1 -1
- package/dist/wirings/workflow/graph/graph-node.d.ts +9 -2
- package/dist/wirings/workflow/graph/graph-node.js +2 -0
- package/dist/wirings/workflow/graph/graph-runner.d.ts +1 -0
- package/dist/wirings/workflow/graph/graph-runner.js +283 -31
- package/dist/wirings/workflow/graph/workflow-graph.types.d.ts +16 -1
- package/dist/wirings/workflow/index.d.ts +2 -0
- package/dist/wirings/workflow/index.js +1 -0
- package/dist/wirings/workflow/pikku-scenario-service.d.ts +14 -4
- package/dist/wirings/workflow/pikku-scenario-service.js +44 -16
- package/dist/wirings/workflow/pikku-workflow-service.d.ts +2 -1
- package/dist/wirings/workflow/pikku-workflow-service.js +3 -4
- package/dist/wirings/workflow/run-timeline.d.ts +1 -1
- package/dist/wirings/workflow/scenario-run.types.d.ts +171 -0
- package/dist/wirings/workflow/scenario-run.types.js +1 -0
- package/dist/wirings/workflow/scenario-step-guards.d.ts +1 -2
- package/dist/wirings/workflow/scenario-step-guards.js +0 -8
- package/dist/wirings/workflow/scenario-step.types.d.ts +27 -12
- package/dist/wirings/workflow/workflow-approval.js +2 -1
- package/dist/wirings/workflow/workflow-run-engine.types.d.ts +2 -1
- package/dist/wirings/workflow/workflow.types.d.ts +2 -1
- package/knowledge/decisions/index.md +2 -0
- package/knowledge/decisions/internals/a-scenario-step-template-is-offered-unfilled.md +1 -1
- package/knowledge/decisions/internals/a-secret-that-fails-to-decrypt-fails-the-whole-read.md +1 -1
- package/knowledge/decisions/internals/a-virtual-user-run-is-not-a-workflow-and-not-a-queued-job.md +2 -2
- package/knowledge/decisions/internals/a-wall-clock-threshold-is-a-load-test-in-disguise.md +6 -6
- package/knowledge/decisions/internals/a-workflow-wire-is-built-from-the-run-not-from-the-rpc-service.md +1 -1
- package/knowledge/decisions/internals/addon-singleton-services-are-cached-per-namespace-not-per-package.md +1 -1
- package/knowledge/decisions/internals/addon-workflow-names-are-prefixed-with-the-consumer-namespace.md +1 -1
- package/knowledge/decisions/internals/an-addon-scope-root-loses-to-a-root-the-host-already-declares.md +1 -1
- package/knowledge/decisions/internals/channel-message-handlers-accept-three-config-shapes.md +1 -1
- package/knowledge/decisions/internals/channel-middleware-caches-only-statically-resolved-middleware.md +1 -1
- package/knowledge/decisions/internals/channel-user-id-is-persisted-after-onconnect-middleware-runs.md +1 -1
- package/knowledge/decisions/internals/cli-option-names-are-camelcase-in-state-and-kebab-on-the-command-line.md +1 -1
- package/knowledge/decisions/internals/cli-parse-errors-are-routed-by-message-prefix.md +1 -1
- package/knowledge/decisions/internals/cli-unknown-long-options-warn-instead-of-failing.md +1 -1
- package/knowledge/decisions/internals/core-column-form-is-an-axis-of-its-own.md +4 -4
- package/knowledge/decisions/internals/core-data-classification-brand-is-an-optional-property.md +1 -1
- package/knowledge/decisions/internals/core-function-runner-restores-the-wire-fields-it-overwrites.md +3 -3
- package/knowledge/decisions/internals/core-hot-reload-merges-generated-meta-never-replaces-it.md +2 -2
- package/knowledge/decisions/internals/core-hot-reload-owns-its-module-registry.md +2 -2
- package/knowledge/decisions/internals/core-middleware-order-is-scope-then-priority.md +1 -1
- package/knowledge/decisions/internals/core-schema-defaults-apply-on-every-transport.md +1 -1
- package/knowledge/decisions/internals/core-scopes-are-an-and-gate-separate-from-permissions.md +3 -3
- package/knowledge/decisions/internals/gateway-adapters-resolve-lazily-and-are-promise-cached.md +1 -1
- package/knowledge/decisions/internals/gateway-listener-middleware-runs-without-an-rpc-on-the-wire.md +4 -4
- package/knowledge/decisions/internals/gateway-wiring-is-a-meta-wiring-over-http-and-channels.md +1 -1
- package/knowledge/decisions/internals/http-router-matches-normalized-paths-but-returns-registered-ones.md +1 -1
- package/knowledge/decisions/internals/in-a-scenario-a-4xx-is-data-not-an-exception.md +1 -1
- package/knowledge/decisions/internals/in-memory-workflow-history-aliases-the-live-step-object.md +1 -1
- package/knowledge/decisions/internals/index.md +4 -2
- package/knowledge/decisions/internals/istanbul-statement-counts-attach-to-the-start-line-only.md +1 -1
- package/knowledge/decisions/internals/one-project-shape-check-two-validators.md +1 -1
- package/knowledge/decisions/internals/queue-jobs-always-carry-an-explicit-attempts-count.md +1 -1
- package/knowledge/decisions/internals/remote-addons-dispatch-over-http-instead-of-local-meta.md +1 -1
- package/knowledge/decisions/internals/rpc-names-resolve-through-package-scope-before-root.md +1 -1
- package/knowledge/decisions/internals/scenario-given-and-when-are-sugar-but-then-is-not.md +2 -2
- package/knowledge/decisions/internals/scenarios-live-in-files-named-for-them.md +5 -5
- package/knowledge/decisions/internals/the-actor-prompt-says-json-because-of-json-object-mode.md +1 -1
- package/knowledge/decisions/internals/the-api-report-pins-members-not-just-names.md +1 -1
- package/knowledge/decisions/internals/the-ecosystem-entry-point-carries-the-adapter-surface.md +43 -19
- package/knowledge/decisions/internals/the-persona-runtime-is-exported-from-the-persona-entry-point.md +1 -1
- package/knowledge/decisions/internals/the-worker-disposition-is-the-one-that-is-not-testing.md +1 -1
- package/knowledge/decisions/internals/validate-runs-checks-by-precondition.md +3 -3
- package/knowledge/decisions/internals/virtual-user-step-order-comes-from-insertion-order.md +1 -1
- package/knowledge/decisions/internals/webhook-delivery-history-records-every-attempt-best-effort.md +1 -1
- package/knowledge/decisions/internals/wiring-registries-erase-the-generics-their-wire-functions-capture.md +1 -1
- package/knowledge/decisions/internals/workflow-approval-expiry-is-decided-from-a-recorded-deadline.md +1 -1
- package/knowledge/decisions/internals/workflow-core-never-imports-a-browser-driver.md +1 -1
- package/knowledge/decisions/internals/workflow-inline-runs-report-their-run-id-before-they-can-fail.md +1 -1
- package/knowledge/decisions/internals/workflow-invocation-id-is-the-dedupe-key-not-step-id.md +1 -1
- package/knowledge/decisions/internals/workflow-run-capabilities-are-extensions-not-subclasses.md +1 -1
- package/knowledge/decisions/internals/workflow-run-timeline-is-a-pure-fold-over-durable-history.md +1 -1
- package/knowledge/decisions/internals/workflow-scenario-assertions-never-retry-and-record-one-step.md +9 -5
- package/knowledge/decisions/internals/workflow-scenario-hooks-are-a-scenario-only-affordance.md +1 -1
- package/knowledge/decisions/internals/workflow-step-rpc-name-is-provenance-only.md +2 -2
- package/knowledge/decisions/internals/workflow-suspend-and-approval-reasons-are-durable-step-identities.md +1 -1
- package/knowledge/decisions/security/a-function-never-receives-the-secret-service.md +1 -1
- package/knowledge/decisions/security/a-step-runs-the-function-the-workflow-dispatched-it-with.md +1 -1
- package/knowledge/decisions/security/a-virtual-user-is-never-offered-a-step-that-would-forge-its-own-oracle.md +2 -3
- package/knowledge/decisions/security/actor-sign-in-is-proven-by-set-cookie-not-a-non-empty-jar.md +1 -1
- package/knowledge/decisions/security/addon-auth-and-tags-only-tighten.md +1 -1
- package/knowledge/decisions/security/agent-gate-requires-a-session-only-when-auth-is-true.md +1 -1
- package/knowledge/decisions/security/an-approval-answer-outlives-the-run-it-answered.md +3 -3
- package/knowledge/decisions/security/console-addon-privileged-functions-gate-themselves.md +4 -3
- package/knowledge/decisions/security/core-safe-fetch-blocks-ssrf-by-host-literal-not-dns.md +1 -1
- package/knowledge/decisions/security/gateway-middleware-sessions-must-be-bridged-onto-the-wire.md +2 -2
- package/knowledge/decisions/security/global-permissions-and-function-permissions-are-independent-gates.md +1 -1
- package/knowledge/decisions/security/http-request-bodies-are-bounded-before-they-are-buffered.md +2 -2
- package/knowledge/decisions/security/index.md +2 -0
- package/knowledge/decisions/security/mcp-internal-error-details-are-double-gated-on-production.md +1 -1
- package/knowledge/decisions/security/queue-jobs-carry-the-producers-pikku-user-id.md +3 -3
- package/knowledge/decisions/security/remote-addon-tokens-are-client-credentials-not-mesh-trust.md +1 -1
- package/knowledge/decisions/security/scaffold-features-are-authenticated-unless-opted-out.md +2 -2
- package/knowledge/decisions/security/self-authentication-is-declared-not-detected.md +1 -1
- package/knowledge/decisions/security/signed-content-urls-bind-the-request-path.md +2 -2
- package/knowledge/index.md +3 -1
- package/knowledge/questions/channel-middleware-accepts-bare-factories-that-nothing-resolves.md +1 -1
- package/knowledge/questions/index.md +2 -0
- package/package.json +14 -13
- package/run-tests.sh +0 -0
- package/scripts/generate-api-report.d.mts +1 -1
- package/scripts/generate-api-report.mjs +67 -60
- package/scripts/generate-api-report.mts +11 -45
- package/src/api-report.test.ts +70 -1
- package/src/app-alias-surface.test.ts +179 -0
- package/src/app-leaf-surface.test.ts +217 -0
- package/src/bootstrap-compat/root.ts +20 -0
- package/src/{column-form.test.ts → classification/column-form.test.ts} +2 -2
- package/src/{data-classification.ts → classification/data-classification.ts} +1 -5
- package/src/classification/index.ts +38 -0
- package/src/{secret-value.test.ts → classification/secret-value.test.ts} +2 -2
- package/src/{secret-value.ts → classification/secret-value.ts} +1 -6
- package/src/crypto-utils.ts +1 -1
- package/src/dev/reload-meta.test.ts +1 -1
- package/src/errors/index.ts +9 -0
- package/src/errors/serialized-error.ts +9 -0
- package/src/factory-functions.test.ts +4 -2
- package/src/function/function-meta.types.ts +151 -0
- package/src/function/function-runner.test.ts +4 -2
- package/src/function/function-runner.ts +47 -14
- package/src/function/functions.types.ts +11 -7
- package/src/function/index.ts +24 -5
- package/src/function/list.types.ts +1 -3
- package/src/middleware/auth-apikey.ts +4 -2
- package/src/middleware/auth-bearer.test.ts +1 -1
- package/src/middleware/auth-bearer.ts +4 -2
- package/src/middleware/auth-cookie.ts +4 -1
- package/src/middleware/cors.test.ts +126 -5
- package/src/middleware/cors.ts +19 -8
- package/src/middleware/index.ts +16 -0
- package/src/middleware/middleware-factories.ts +62 -0
- package/src/middleware/middleware.types.ts +61 -0
- package/src/middleware/remote-auth.test.ts +1 -1
- package/src/middleware/remote-auth.ts +1 -1
- package/src/middleware/telemetry.ts +4 -2
- package/src/middleware-runner.test.ts +2 -3
- package/src/middleware-runner.ts +2 -2
- package/src/no-root-barrel.test.ts +114 -0
- package/src/permissions.ts +2 -4
- package/src/pikku-state.ts +3 -1
- package/src/public-surface.json +56 -241
- package/src/public-surface.json.README +3 -3
- package/src/public-surface.test.ts +5 -5
- package/src/remote.test.ts +1 -1
- package/src/removed-legacy-exports.test.ts +5 -1
- package/src/services/audit-service.ts +1 -1
- package/src/services/credential-wire-service.ts +1 -1
- package/src/services/email-service.ts +2 -4
- package/src/services/file-scenario-run-store.test.ts +349 -0
- package/src/services/file-scenario-run-store.ts +282 -0
- package/src/services/in-memory-workflow-service.ts +1 -1
- package/src/services/index.ts +13 -1
- package/src/services/local-content-request-handler.ts +1 -2
- package/src/services/local-secrets.ts +1 -1
- package/src/services/logger.ts +7 -7
- package/src/services/meta-service.ts +2 -2
- package/src/services/queue-webhook-service.test.ts +1 -1
- package/src/services/scoped-secret-service.ts +1 -1
- package/src/services/secret-service.ts +1 -1
- package/src/services/typed-secret-service.test.ts +52 -0
- package/src/services/typed-secret-service.ts +26 -2
- package/src/services/webhook-service.ts +1 -1
- package/src/services/workflow-service.ts +2 -1
- package/src/template-alias-surface.test.ts +240 -0
- package/src/testing/index.ts +1 -0
- package/src/types/core.types.ts +33 -285
- package/src/types/index.ts +2 -0
- package/src/types/state.types.ts +6 -5
- package/src/types/typed-credentials.test.ts +72 -0
- package/src/utils.ts +34 -0
- package/src/wirings/{rpc → addon}/addon-scopes.test.ts +1 -1
- package/src/wirings/addon/index.ts +12 -0
- package/src/wirings/{rpc → addon}/remote-addon-auth.ts +4 -1
- package/src/wirings/{rpc → addon}/wire-addon.ts +37 -2
- package/src/wirings/agent/agent-prepare.ts +8 -5
- package/src/wirings/agent/agent-runner.ts +2 -3
- package/src/wirings/agent/agent-stream.ts +2 -3
- package/src/wirings/agent/agent-utils.ts +19 -0
- package/src/wirings/agent/agent.types.ts +3 -3
- package/src/wirings/agent/index.ts +1 -0
- package/src/wirings/agent/voice-input.ts +1 -1
- package/src/wirings/agent/voice-output.test.ts +2 -4
- package/src/wirings/agent/voice-output.ts +1 -1
- package/src/wirings/agent-scorer/agent-scorer-judge.test.ts +145 -0
- package/src/wirings/agent-scorer/agent-scorer-judge.ts +60 -0
- package/src/wirings/agent-scorer/agent-scorer.ts +8 -0
- package/src/wirings/agent-scorer/agent-scorer.types.ts +21 -0
- package/src/wirings/agent-scorer/index.ts +1 -0
- package/src/wirings/channel/channel-common.ts +4 -2
- package/src/wirings/channel/channel-handler.ts +2 -5
- package/src/wirings/channel/channel-middleware-runner.ts +1 -1
- package/src/wirings/channel/channel-rpc.types.ts +1 -5
- package/src/wirings/channel/channel.types.ts +1 -2
- package/src/wirings/channel/local/local-channel-runner.test.ts +1 -2
- package/src/wirings/channel/serverless/index.ts +1 -0
- package/src/wirings/cli/channel/cli-raw-channel-runner.ts +2 -2
- package/src/wirings/cli/channel/index.ts +1 -3
- package/src/wirings/cli/cli-runner.test.ts +1 -1
- package/src/wirings/cli/cli-runner.ts +11 -7
- package/src/wirings/cli/cli.types.ts +24 -3
- package/src/wirings/cli/command-parser.test.ts +388 -1
- package/src/wirings/cli/command-parser.ts +189 -29
- package/src/wirings/gateway/gateway-runner.test.ts +1 -2
- package/src/wirings/gateway/gateway-runner.ts +3 -6
- package/src/wirings/gateway/gateway.types.ts +5 -4
- package/src/wirings/http/http-routes.test.ts +1 -2
- package/src/wirings/http/http-runner-addon-ref.test.ts +238 -0
- package/src/wirings/http/http-runner.test.ts +3 -8
- package/src/wirings/http/http-runner.ts +9 -3
- package/src/wirings/http/http.types.ts +3 -12
- package/src/wirings/http/index.ts +1 -0
- package/src/wirings/http/pikku-fetch-http-request.ts +1 -1
- package/src/wirings/http/pikku-fetch-http-response.ts +3 -2
- package/src/wirings/mcp/mcp-runner.test.ts +1 -1
- package/src/wirings/mcp/mcp.types.ts +1 -2
- package/src/wirings/persona/index.ts +1 -4
- package/src/wirings/persona/persona.types.ts +14 -0
- package/src/wirings/queue/queue-identity.test.ts +1 -1
- package/src/wirings/queue/queue.types.ts +2 -6
- package/src/wirings/queue/signed-queue-service.ts +1 -1
- package/src/wirings/rpc/index.ts +0 -12
- package/src/wirings/rpc/rpc-runner.test.ts +4 -4
- package/src/wirings/rpc/rpc-runner.ts +3 -3
- package/src/wirings/scheduler/scheduler.types.ts +2 -5
- package/src/wirings/scope/index.ts +1 -0
- package/src/wirings/secret/secret.types.ts +10 -0
- package/src/wirings/secret/validate-secret-definitions.test.ts +23 -0
- package/src/wirings/secret/validate-secret-definitions.ts +2 -0
- package/src/wirings/variable/validate-variable-definitions.test.ts +23 -0
- package/src/wirings/variable/validate-variable-definitions.ts +2 -0
- package/src/wirings/variable/variable.types.ts +9 -0
- package/src/wirings/virtual-user/index.ts +1 -4
- package/src/wirings/virtual-user/prepare-virtual-user-run.ts +1 -1
- package/src/wirings/virtual-user/virtual-user-derive.test.ts +1 -1
- package/src/wirings/virtual-user/virtual-user-derive.ts +1 -1
- package/src/wirings/virtual-user/virtual-user.types.ts +1 -5
- package/src/wirings/workflow/graph/graph-foreach-queued.test.ts +569 -0
- package/src/wirings/workflow/graph/graph-foreach.test.ts +626 -0
- package/src/wirings/workflow/graph/graph-node.ts +16 -1
- package/src/wirings/workflow/graph/graph-runner.ts +387 -61
- package/src/wirings/workflow/graph/workflow-graph.types.ts +28 -3
- package/src/wirings/workflow/index.ts +8 -0
- package/src/wirings/workflow/pikku-scenario-service.ts +59 -19
- package/src/wirings/workflow/pikku-workflow-service.ts +6 -8
- package/src/wirings/workflow/run-timeline.ts +1 -1
- package/src/wirings/workflow/scenario-expectations.test.ts +28 -13
- package/src/wirings/workflow/scenario-run.types.ts +186 -0
- package/src/wirings/workflow/scenario-step-guards.ts +1 -14
- package/src/wirings/workflow/scenario-step.test.ts +32 -7
- package/src/wirings/workflow/scenario-step.types.ts +27 -12
- package/src/wirings/workflow/workflow-approval-policy.test.ts +117 -47
- package/src/wirings/workflow/workflow-approval.ts +2 -1
- package/src/wirings/workflow/workflow-run-engine.types.ts +2 -1
- package/src/wirings/workflow/workflow.types.ts +4 -12
- package/src/wirings-stay-decoupled.test.ts +6 -3
- package/tsconfig.tsbuildinfo +1 -1
- package/tsconfig.type-tests.json +3 -2
- package/dist/ecosystem/ai-agent.d.ts +0 -18
- package/dist/ecosystem/ai-agent.js +0 -6
- package/dist/ecosystem/ai-scorer.d.ts +0 -12
- package/dist/ecosystem/ai-scorer.js +0 -5
- package/dist/services/ai-agent-runner-service.d.ts +0 -220
- package/dist/services/ai-run-state-service.d.ts +0 -29
- package/dist/services/ai-storage-service.d.ts +0 -18
- package/dist/services/in-memory-ai-run-state-service.d.ts +0 -19
- package/dist/services/in-memory-ai-run-state-service.js +0 -54
- package/dist/testing/service-tests/ai-storage-service-tests.d.ts +0 -3
- package/dist/testing/service-tests/ai-storage-service-tests.js +0 -302
- package/dist/wirings/ai-agent/agent-rpc.d.ts +0 -15
- package/dist/wirings/ai-agent/agent-rpc.js +0 -53
- package/dist/wirings/ai-agent/ai-agent-agui.d.ts +0 -79
- package/dist/wirings/ai-agent/ai-agent-agui.js +0 -327
- package/dist/wirings/ai-agent/ai-agent-finalize.d.ts +0 -58
- package/dist/wirings/ai-agent/ai-agent-finalize.js +0 -138
- package/dist/wirings/ai-agent/ai-agent-helpers.d.ts +0 -35
- package/dist/wirings/ai-agent/ai-agent-helpers.js +0 -47
- package/dist/wirings/ai-agent/ai-agent-interrupt.d.ts +0 -153
- package/dist/wirings/ai-agent/ai-agent-interrupt.js +0 -257
- package/dist/wirings/ai-agent/ai-agent-memory.d.ts +0 -42
- package/dist/wirings/ai-agent/ai-agent-memory.js +0 -333
- package/dist/wirings/ai-agent/ai-agent-model-config.d.ts +0 -16
- package/dist/wirings/ai-agent/ai-agent-model-config.js +0 -51
- package/dist/wirings/ai-agent/ai-agent-prepare.d.ts +0 -117
- package/dist/wirings/ai-agent/ai-agent-prepare.js +0 -728
- package/dist/wirings/ai-agent/ai-agent-registry.d.ts +0 -16
- package/dist/wirings/ai-agent/ai-agent-registry.js +0 -51
- package/dist/wirings/ai-agent/ai-agent-runner.d.ts +0 -7
- package/dist/wirings/ai-agent/ai-agent-runner.js +0 -580
- package/dist/wirings/ai-agent/ai-agent-stream.d.ts +0 -41
- package/dist/wirings/ai-agent/ai-agent-stream.js +0 -1029
- package/dist/wirings/ai-agent/ai-agent-turn.d.ts +0 -57
- package/dist/wirings/ai-agent/ai-agent-turn.js +0 -82
- package/dist/wirings/ai-agent/ai-agent-utils.d.ts +0 -1
- package/dist/wirings/ai-agent/ai-agent-utils.js +0 -1
- package/dist/wirings/ai-agent/ai-agent.types.d.ts +0 -625
- package/dist/wirings/ai-agent/index.d.ts +0 -12
- package/dist/wirings/ai-agent/index.js +0 -10
- package/dist/wirings/ai-agent/voice-input.d.ts +0 -69
- package/dist/wirings/ai-agent/voice-input.js +0 -150
- package/dist/wirings/ai-agent/voice-output.d.ts +0 -85
- package/dist/wirings/ai-agent/voice-output.js +0 -196
- package/dist/wirings/ai-scorer/ai-scorer-grade.d.ts +0 -26
- package/dist/wirings/ai-scorer/ai-scorer-grade.js +0 -33
- package/dist/wirings/ai-scorer/ai-scorer-judge.d.ts +0 -17
- package/dist/wirings/ai-scorer/ai-scorer-judge.js +0 -92
- package/dist/wirings/ai-scorer/ai-scorer-live.d.ts +0 -15
- package/dist/wirings/ai-scorer/ai-scorer-live.js +0 -38
- package/dist/wirings/ai-scorer/ai-scorer-registry.d.ts +0 -18
- package/dist/wirings/ai-scorer/ai-scorer-registry.js +0 -46
- package/dist/wirings/ai-scorer/ai-scorer-sampling.d.ts +0 -8
- package/dist/wirings/ai-scorer/ai-scorer-sampling.js +0 -31
- package/dist/wirings/ai-scorer/ai-scorer-snapshots.d.ts +0 -10
- package/dist/wirings/ai-scorer/ai-scorer-snapshots.js +0 -40
- package/dist/wirings/ai-scorer/ai-scorer-worker.d.ts +0 -15
- package/dist/wirings/ai-scorer/ai-scorer-worker.js +0 -58
- package/dist/wirings/ai-scorer/ai-scorer.d.ts +0 -39
- package/dist/wirings/ai-scorer/ai-scorer.js +0 -40
- package/dist/wirings/ai-scorer/ai-scorer.types.d.ts +0 -90
- package/dist/wirings/ai-scorer/ai-scorer.types.js +0 -4
- package/dist/wirings/ai-scorer/index.d.ts +0 -6
- package/dist/wirings/ai-scorer/index.js +0 -5
- package/src/ecosystem/agent-scorer.ts +0 -24
- package/src/ecosystem/agent.ts +0 -42
- package/src/ecosystem/channel/local.ts +0 -1
- package/src/ecosystem/channel.ts +0 -22
- package/src/ecosystem/cli/channel.ts +0 -25
- package/src/ecosystem/cli.ts +0 -21
- package/src/ecosystem/credential.ts +0 -10
- package/src/ecosystem/dev.ts +0 -12
- package/src/ecosystem/function.ts +0 -28
- package/src/ecosystem/gateway.ts +0 -4
- package/src/ecosystem/hmac.ts +0 -1
- package/src/ecosystem/http.ts +0 -22
- package/src/ecosystem/mcp.ts +0 -17
- package/src/ecosystem/middleware.ts +0 -27
- package/src/ecosystem/node-host-resolver.ts +0 -1
- package/src/ecosystem/node.ts +0 -5
- package/src/ecosystem/oauth2.ts +0 -4
- package/src/ecosystem/persona.ts +0 -30
- package/src/ecosystem/queue.ts +0 -23
- package/src/ecosystem/remote.ts +0 -9
- package/src/ecosystem/role.ts +0 -13
- package/src/ecosystem/rpc.ts +0 -7
- package/src/ecosystem/safe-fetch.ts +0 -8
- package/src/ecosystem/scenario.ts +0 -32
- package/src/ecosystem/scheduler.ts +0 -5
- package/src/ecosystem/schema.ts +0 -1
- package/src/ecosystem/scope.ts +0 -14
- package/src/ecosystem/secret.ts +0 -15
- package/src/ecosystem/services/istanbul-coverage.ts +0 -1
- package/src/ecosystem/services/local-content-request-handler.ts +0 -16
- package/src/ecosystem/services/local-content.ts +0 -1
- package/src/ecosystem/services/v8-coverage.ts +0 -1
- package/src/ecosystem/services.ts +0 -82
- package/src/ecosystem/trigger.ts +0 -7
- package/src/ecosystem/types.ts +0 -34
- package/src/ecosystem/variable.ts +0 -14
- package/src/ecosystem/virtual-user.ts +0 -48
- package/src/ecosystem/workflow.ts +0 -68
- package/src/ecosystem.ts +0 -40
- package/src/index.ts +0 -244
- /package/dist/{services/ai-agent-runner-service.js → bootstrap-compat/root.js} +0 -0
- /package/dist/{services/ai-run-state-service.js → classification/data-classification.js} +0 -0
- /package/dist/{services/ai-storage-service.js → errors/serialized-error.js} +0 -0
- /package/dist/{wirings/ai-agent/ai-agent.types.js → function/function-meta.types.js} +0 -0
- /package/src/{column-form.ts → classification/column-form.ts} +0 -0
- /package/src/{pikku-request.test.ts → function/pikku-request.test.ts} +0 -0
- /package/src/{pikku-request.ts → function/pikku-request.ts} +0 -0
- /package/src/wirings/{rpc → addon}/addon-auth-tags.test.ts +0 -0
- /package/src/wirings/{rpc → addon}/addon-runner.ts +0 -0
- /package/src/wirings/{rpc → addon}/addon-secrets.test.ts +0 -0
- /package/src/wirings/{rpc → addon}/wire-addon.test.ts +0 -0
- /package/src/wirings/{rpc → addon}/wire-remote-addon.ts +0 -0
|
@@ -23,11 +23,25 @@ function buildTemplateRegex(nodeId) {
|
|
|
23
23
|
.join('.+');
|
|
24
24
|
return new RegExp(`^${escaped}$`);
|
|
25
25
|
}
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
const FANOUT_INSTANCE = /^(.+)\[(\d+)\]$/;
|
|
27
|
+
function fanoutInstanceKey(base, index) {
|
|
28
|
+
return `${base}[${index}]`;
|
|
29
|
+
}
|
|
30
|
+
function splitFanoutInstance(name) {
|
|
31
|
+
const match = FANOUT_INSTANCE.exec(name);
|
|
32
|
+
if (!match)
|
|
33
|
+
return null;
|
|
34
|
+
return { base: match[1], index: Number(match[2]) };
|
|
35
|
+
}
|
|
36
|
+
export function stripInstanceOrdinal(name) {
|
|
37
|
+
const fanout = splitFanoutInstance(name);
|
|
38
|
+
const withoutItem = fanout ? fanout.base : name;
|
|
39
|
+
const hash = withoutItem.lastIndexOf('#');
|
|
28
40
|
if (hash <= 0)
|
|
29
|
-
return
|
|
30
|
-
return /^\d+$/.test(
|
|
41
|
+
return withoutItem;
|
|
42
|
+
return /^\d+$/.test(withoutItem.slice(hash + 1))
|
|
43
|
+
? withoutItem.slice(0, hash)
|
|
44
|
+
: withoutItem;
|
|
31
45
|
}
|
|
32
46
|
function remapStepNamesToNodeIds(stepNames, nodes, graphName) {
|
|
33
47
|
const templatePatterns = new Map();
|
|
@@ -72,40 +86,123 @@ function closesCycle(source, target, nodes) {
|
|
|
72
86
|
}
|
|
73
87
|
return false;
|
|
74
88
|
}
|
|
75
|
-
function planGraphTransitions(nodes, instances, branchByStep, entryNodeIds, graphName) {
|
|
89
|
+
function planGraphTransitions(nodes, instances, branchByStep, entryNodeIds, graphName, fanoutWidths = {}) {
|
|
76
90
|
const toLogical = (name) => remapStepNamesToNodeIds([name], nodes, graphName)[0];
|
|
77
91
|
const countByLogical = {};
|
|
92
|
+
const instancesByLogical = {};
|
|
78
93
|
const consumed = new Set();
|
|
79
94
|
for (const inst of instances) {
|
|
80
95
|
const logical = toLogical(inst.stepName);
|
|
81
96
|
countByLogical[logical] = (countByLogical[logical] ?? 0) + 1;
|
|
97
|
+
(instancesByLogical[logical] ??= []).push(inst);
|
|
82
98
|
consumed.add(`${inst.fromStepName ?? ENTRY_FROM}->${logical}`);
|
|
83
99
|
}
|
|
100
|
+
const isFanned = (nodeId) => Boolean(nodes[nodeId]?.forEach);
|
|
101
|
+
const fanoutComplete = (nodeId) => {
|
|
102
|
+
const width = fanoutWidths[nodeId];
|
|
103
|
+
if (width === undefined)
|
|
104
|
+
return false;
|
|
105
|
+
const succeeded = (instancesByLogical[nodeId] ?? []).filter((i) => i.status === 'succeeded').length;
|
|
106
|
+
return succeeded >= width;
|
|
107
|
+
};
|
|
84
108
|
const completed = instances.filter((i) => i.status === 'succeeded');
|
|
85
|
-
const completedLogical = new Set(
|
|
109
|
+
const completedLogical = new Set();
|
|
110
|
+
for (const inst of completed) {
|
|
111
|
+
const logical = toLogical(inst.stepName);
|
|
112
|
+
if (!isFanned(logical))
|
|
113
|
+
completedLogical.add(logical);
|
|
114
|
+
}
|
|
115
|
+
for (const nodeId of Object.keys(fanoutWidths)) {
|
|
116
|
+
if (isFanned(nodeId) && fanoutComplete(nodeId))
|
|
117
|
+
completedLogical.add(nodeId);
|
|
118
|
+
}
|
|
86
119
|
const edges = [];
|
|
87
120
|
for (const entryId of entryNodeIds) {
|
|
88
121
|
edges.push({ fromKey: ENTRY_FROM, target: entryId });
|
|
89
122
|
}
|
|
123
|
+
const fannedEdgesEmitted = new Set();
|
|
90
124
|
for (const inst of completed) {
|
|
91
125
|
const fromLogical = toLogical(inst.stepName);
|
|
92
126
|
const node = nodes[fromLogical];
|
|
93
127
|
if (!node?.next)
|
|
94
128
|
continue;
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
129
|
+
let from = inst.stepName;
|
|
130
|
+
let branchKey = branchByStep[inst.stepName];
|
|
131
|
+
if (isFanned(fromLogical)) {
|
|
132
|
+
if (!completedLogical.has(fromLogical))
|
|
133
|
+
continue;
|
|
134
|
+
if (fannedEdgesEmitted.has(fromLogical))
|
|
135
|
+
continue;
|
|
136
|
+
fannedEdgesEmitted.add(fromLogical);
|
|
137
|
+
from = fromLogical;
|
|
138
|
+
branchKey = branchByStep[fromLogical] ?? branchByStep[inst.stepName];
|
|
139
|
+
}
|
|
140
|
+
for (const target of resolveNextFromConfig(node.next, branchKey)) {
|
|
141
|
+
edges.push({ from, fromKey: from, fromLogical, target });
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
// A zero-width fanout produces no instances at all, so its outgoing edges
|
|
145
|
+
// have no completed instance to hang off — emit them from the logical node.
|
|
146
|
+
for (const [nodeId, width] of Object.entries(fanoutWidths)) {
|
|
147
|
+
if (width !== 0 || fannedEdgesEmitted.has(nodeId))
|
|
148
|
+
continue;
|
|
149
|
+
const node = nodes[nodeId];
|
|
150
|
+
if (!node?.next)
|
|
151
|
+
continue;
|
|
152
|
+
fannedEdgesEmitted.add(nodeId);
|
|
153
|
+
for (const target of resolveNextFromConfig(node.next, branchByStep[nodeId])) {
|
|
154
|
+
edges.push({ from: nodeId, fromKey: nodeId, fromLogical: nodeId, target });
|
|
102
155
|
}
|
|
103
156
|
}
|
|
104
157
|
const toFire = [];
|
|
158
|
+
const plannedFanout = new Set();
|
|
105
159
|
let blockedWaiting = false;
|
|
106
160
|
for (const edge of edges) {
|
|
107
161
|
const target = edge.target;
|
|
108
162
|
const edgeKey = `${edge.fromKey}->${target}`;
|
|
163
|
+
if (isFanned(target)) {
|
|
164
|
+
if (plannedFanout.has(target))
|
|
165
|
+
continue;
|
|
166
|
+
plannedFanout.add(target);
|
|
167
|
+
if (!areDependenciesSatisfied(nodes[target] ?? {}, completedLogical)) {
|
|
168
|
+
blockedWaiting = true;
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
const width = fanoutWidths[target];
|
|
172
|
+
if (width === undefined) {
|
|
173
|
+
blockedWaiting = true;
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
const existing = instancesByLogical[target] ?? [];
|
|
177
|
+
const existingIndexes = new Set(existing
|
|
178
|
+
.map((i) => splitFanoutInstance(i.stepName)?.index)
|
|
179
|
+
.filter((index) => index !== undefined));
|
|
180
|
+
const pending = [];
|
|
181
|
+
for (let index = 0; index < width; index++) {
|
|
182
|
+
if (!existingIndexes.has(index))
|
|
183
|
+
pending.push(index);
|
|
184
|
+
}
|
|
185
|
+
if (pending.length === 0)
|
|
186
|
+
continue;
|
|
187
|
+
const sequential = nodes[target]?.mode === 'sequential';
|
|
188
|
+
const anyInFlight = existing.some((i) => i.status !== 'succeeded');
|
|
189
|
+
const emit = sequential
|
|
190
|
+
? anyInFlight
|
|
191
|
+
? []
|
|
192
|
+
: pending.slice(0, 1)
|
|
193
|
+
: pending;
|
|
194
|
+
for (const index of emit) {
|
|
195
|
+
toFire.push({
|
|
196
|
+
logical: target,
|
|
197
|
+
instanceKey: fanoutInstanceKey(target, index),
|
|
198
|
+
fromStepName: edge.from,
|
|
199
|
+
itemIndex: index,
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
if (emit.length === 0)
|
|
203
|
+
blockedWaiting = true;
|
|
204
|
+
continue;
|
|
205
|
+
}
|
|
109
206
|
if (consumed.has(edgeKey))
|
|
110
207
|
continue;
|
|
111
208
|
const visits = countByLogical[target] ?? 0;
|
|
@@ -237,7 +334,7 @@ function resolveTemplate(template, nodeResults) {
|
|
|
237
334
|
}
|
|
238
335
|
function resolveValue(value, nodeResults) {
|
|
239
336
|
if (isDataRef(value)) {
|
|
240
|
-
if (value.$ref === '$item') {
|
|
337
|
+
if (value.$ref === '$item' && !('$item' in nodeResults)) {
|
|
241
338
|
return value;
|
|
242
339
|
}
|
|
243
340
|
const source = nodeResults[value.$ref];
|
|
@@ -297,6 +394,7 @@ function extractReferencedNodeIds(input) {
|
|
|
297
394
|
return [...new Set(nodeIds)];
|
|
298
395
|
}
|
|
299
396
|
const IGNORED_REFS = new Set(['trigger', '$item', 'unknown']);
|
|
397
|
+
const FOREACH_MODES = new Set(['parallel', 'sequential']);
|
|
300
398
|
function normalizeNodeTargets(value) {
|
|
301
399
|
if (!value)
|
|
302
400
|
return [];
|
|
@@ -327,6 +425,21 @@ function validateGraphReferences(graphName, nodes, entryNodes) {
|
|
|
327
425
|
throw new Error(`Workflow graph '${graphName}': node '${nodeId}' references unknown node '${refId}' in input`);
|
|
328
426
|
}
|
|
329
427
|
}
|
|
428
|
+
if (node.forEach !== undefined) {
|
|
429
|
+
const source = forEachSource(node);
|
|
430
|
+
if (!source) {
|
|
431
|
+
throw new Error(`Workflow graph '${graphName}': node '${nodeId}' forEach must reference a node`);
|
|
432
|
+
}
|
|
433
|
+
if (!IGNORED_REFS.has(source.$ref) && !nodeIds.has(source.$ref)) {
|
|
434
|
+
throw new Error(`Workflow graph '${graphName}': node '${nodeId}' references unknown node '${source.$ref}' in forEach`);
|
|
435
|
+
}
|
|
436
|
+
if (node.mode !== undefined && !FOREACH_MODES.has(node.mode)) {
|
|
437
|
+
throw new Error(`Workflow graph '${graphName}': node '${nodeId}' has an unknown forEach mode '${node.mode}'`);
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
else if (node.mode !== undefined) {
|
|
441
|
+
throw new Error(`Workflow graph '${graphName}': node '${nodeId}' sets mode '${node.mode}' without a forEach`);
|
|
442
|
+
}
|
|
330
443
|
const nextTargets = normalizeNodeTargets(node.next);
|
|
331
444
|
for (const nextId of nextTargets) {
|
|
332
445
|
if (!nodeIds.has(nextId)) {
|
|
@@ -341,9 +454,114 @@ function validateGraphReferences(graphName, nodes, entryNodes) {
|
|
|
341
454
|
}
|
|
342
455
|
}
|
|
343
456
|
}
|
|
457
|
+
function forEachSource(node) {
|
|
458
|
+
return isDataRef(node.forEach) ? node.forEach : undefined;
|
|
459
|
+
}
|
|
460
|
+
function nodeDependencies(node) {
|
|
461
|
+
const deps = extractReferencedNodeIds(node.input);
|
|
462
|
+
const source = forEachSource(node);
|
|
463
|
+
if (source)
|
|
464
|
+
deps.push(source.$ref);
|
|
465
|
+
return [...new Set(deps)].filter((id) => !IGNORED_REFS.has(id));
|
|
466
|
+
}
|
|
467
|
+
function describeValue(value) {
|
|
468
|
+
if (value === null)
|
|
469
|
+
return 'null';
|
|
470
|
+
if (value === undefined)
|
|
471
|
+
return 'undefined';
|
|
472
|
+
return typeof value;
|
|
473
|
+
}
|
|
474
|
+
function resolveForEachItems(graphName, nodeId, node, nodeResults) {
|
|
475
|
+
const source = forEachSource(node);
|
|
476
|
+
if (!source) {
|
|
477
|
+
throw new Error(`Workflow graph '${graphName}': node '${nodeId}' has an unusable forEach configuration`);
|
|
478
|
+
}
|
|
479
|
+
const raw = nodeResults[source.$ref];
|
|
480
|
+
const value = source.path ? getValueAtPath(raw, source.path) : raw;
|
|
481
|
+
if (!Array.isArray(value)) {
|
|
482
|
+
const label = source.path ? `${source.$ref}.${source.path}` : source.$ref;
|
|
483
|
+
throw new Error(`Workflow graph '${graphName}': node '${nodeId}' forEach source '${label}' resolved to ${describeValue(value)}, expected an array`);
|
|
484
|
+
}
|
|
485
|
+
return value;
|
|
486
|
+
}
|
|
344
487
|
function areDependenciesSatisfied(node, completedNodeIds) {
|
|
345
|
-
|
|
346
|
-
|
|
488
|
+
return nodeDependencies(node).every((dep) => completedNodeIds.has(dep));
|
|
489
|
+
}
|
|
490
|
+
async function createGraphResultReader(workflowService, runId, graphName, nodes, triggerInput, instances) {
|
|
491
|
+
const succeeded = new Set(instances.filter((i) => i.status === 'succeeded').map((i) => i.stepName));
|
|
492
|
+
const fanoutItems = {};
|
|
493
|
+
const fanoutWidths = {};
|
|
494
|
+
const resultCache = new Map();
|
|
495
|
+
const resolving = new Set();
|
|
496
|
+
const readResult = async (nodeId) => {
|
|
497
|
+
if (nodeId === 'trigger')
|
|
498
|
+
return triggerInput;
|
|
499
|
+
if (resultCache.has(nodeId))
|
|
500
|
+
return resultCache.get(nodeId);
|
|
501
|
+
if (resolving.has(nodeId))
|
|
502
|
+
return undefined;
|
|
503
|
+
resolving.add(nodeId);
|
|
504
|
+
try {
|
|
505
|
+
let value;
|
|
506
|
+
if (nodes[nodeId]?.forEach) {
|
|
507
|
+
const items = await readFanoutItems(nodeId);
|
|
508
|
+
if (items === undefined)
|
|
509
|
+
return undefined;
|
|
510
|
+
const keys = items.map((_, index) => fanoutInstanceKey(nodeId, index));
|
|
511
|
+
if (keys.some((key) => !succeeded.has(key)))
|
|
512
|
+
return undefined;
|
|
513
|
+
const fetched = keys.length
|
|
514
|
+
? await workflowService.getNodeResults(runId, keys)
|
|
515
|
+
: {};
|
|
516
|
+
value = keys.map((key) => fetched[key]);
|
|
517
|
+
}
|
|
518
|
+
else {
|
|
519
|
+
if (!succeeded.has(nodeId))
|
|
520
|
+
return undefined;
|
|
521
|
+
const fetched = await workflowService.getNodeResults(runId, [nodeId]);
|
|
522
|
+
value = fetched[nodeId];
|
|
523
|
+
}
|
|
524
|
+
resultCache.set(nodeId, value);
|
|
525
|
+
return value;
|
|
526
|
+
}
|
|
527
|
+
finally {
|
|
528
|
+
resolving.delete(nodeId);
|
|
529
|
+
}
|
|
530
|
+
};
|
|
531
|
+
const readFanoutItems = async (nodeId) => {
|
|
532
|
+
if (fanoutItems[nodeId])
|
|
533
|
+
return fanoutItems[nodeId];
|
|
534
|
+
const node = nodes[nodeId];
|
|
535
|
+
const source = forEachSource(node);
|
|
536
|
+
if (!source)
|
|
537
|
+
return undefined;
|
|
538
|
+
const sourceValue = await readResult(source.$ref);
|
|
539
|
+
if (sourceValue === undefined)
|
|
540
|
+
return undefined;
|
|
541
|
+
const items = resolveForEachItems(graphName, nodeId, node, {
|
|
542
|
+
[source.$ref]: sourceValue,
|
|
543
|
+
});
|
|
544
|
+
fanoutItems[nodeId] = items;
|
|
545
|
+
fanoutWidths[nodeId] = items.length;
|
|
546
|
+
return items;
|
|
547
|
+
};
|
|
548
|
+
for (const nodeId of Object.keys(nodes)) {
|
|
549
|
+
if (nodes[nodeId]?.forEach)
|
|
550
|
+
await readFanoutItems(nodeId);
|
|
551
|
+
}
|
|
552
|
+
return {
|
|
553
|
+
fanoutItems,
|
|
554
|
+
fanoutWidths,
|
|
555
|
+
read: async (nodeIds) => {
|
|
556
|
+
const results = {};
|
|
557
|
+
for (const nodeId of nodeIds) {
|
|
558
|
+
const value = await readResult(nodeId);
|
|
559
|
+
if (value !== undefined)
|
|
560
|
+
results[nodeId] = value;
|
|
561
|
+
}
|
|
562
|
+
return results;
|
|
563
|
+
},
|
|
564
|
+
};
|
|
347
565
|
}
|
|
348
566
|
async function queueGraphNode(workflowService, runId, _graphName, nodeId, rpcName, input, nodeConfig, fromStepName) {
|
|
349
567
|
const stepOptions = {
|
|
@@ -378,21 +596,28 @@ export async function continueGraph(workflowService, runId, graphName, overrideM
|
|
|
378
596
|
return;
|
|
379
597
|
}
|
|
380
598
|
const instances = await workflowService.getStepInstances(runId);
|
|
381
|
-
const
|
|
599
|
+
const triggerInput = currentRun?.input;
|
|
600
|
+
const reader = await createGraphResultReader(workflowService, runId, graphName, nodes, triggerInput, instances);
|
|
601
|
+
const plan = planGraphTransitions(nodes, instances, branchByStep, meta.entryNodeIds ?? [], graphName, reader.fanoutWidths);
|
|
382
602
|
if (plan.toFire.length === 0) {
|
|
383
603
|
if (!plan.hasInFlight && !plan.blockedWaiting) {
|
|
384
604
|
await workflowService.updateRunStatus(runId, 'completed');
|
|
385
605
|
}
|
|
386
606
|
return;
|
|
387
607
|
}
|
|
388
|
-
const triggerInput = currentRun?.input;
|
|
389
608
|
for (const fire of plan.toFire) {
|
|
390
609
|
const node = nodes[fire.logical];
|
|
391
610
|
if (!node?.rpcName)
|
|
392
611
|
continue;
|
|
393
612
|
const referencedNodeIds = extractReferencedNodeIds(node.input).filter((id) => !IGNORED_REFS.has(id));
|
|
394
|
-
const fetchedResults = await
|
|
395
|
-
const nodeResults = {
|
|
613
|
+
const fetchedResults = await reader.read(referencedNodeIds);
|
|
614
|
+
const nodeResults = {
|
|
615
|
+
trigger: triggerInput,
|
|
616
|
+
...fetchedResults,
|
|
617
|
+
};
|
|
618
|
+
if (fire.itemIndex !== undefined) {
|
|
619
|
+
nodeResults['$item'] = reader.fanoutItems[fire.logical]?.[fire.itemIndex];
|
|
620
|
+
}
|
|
396
621
|
const resolvedInput = resolveSerializedInput(node.input, nodeResults);
|
|
397
622
|
await queueGraphNode(workflowService, runId, graphName, fire.instanceKey, node.rpcName, resolvedInput, node, fire.fromStepName);
|
|
398
623
|
}
|
|
@@ -579,7 +804,8 @@ async function continueGraphInline(workflowService, rpcService, runId, graphName
|
|
|
579
804
|
return;
|
|
580
805
|
}
|
|
581
806
|
const instances = await workflowService.getStepInstances(runId);
|
|
582
|
-
const
|
|
807
|
+
const reader = await createGraphResultReader(workflowService, runId, graphName, nodes, triggerInput, instances);
|
|
808
|
+
const plan = planGraphTransitions(nodes, instances, branchByStep, entryNodeIds, graphName, reader.fanoutWidths);
|
|
583
809
|
if (plan.toFire.length === 0) {
|
|
584
810
|
if (!plan.hasInFlight && !plan.blockedWaiting) {
|
|
585
811
|
await workflowService.updateRunStatus(runId, 'completed');
|
|
@@ -592,8 +818,15 @@ async function continueGraphInline(workflowService, rpcService, runId, graphName
|
|
|
592
818
|
if (!node?.rpcName)
|
|
593
819
|
return;
|
|
594
820
|
const referencedNodeIds = extractReferencedNodeIds(node.input).filter((id) => !IGNORED_REFS.has(id));
|
|
595
|
-
const fetchedResults = await
|
|
596
|
-
const nodeResults = {
|
|
821
|
+
const fetchedResults = await reader.read(referencedNodeIds);
|
|
822
|
+
const nodeResults = {
|
|
823
|
+
trigger: triggerInput,
|
|
824
|
+
...fetchedResults,
|
|
825
|
+
};
|
|
826
|
+
if (fire.itemIndex !== undefined) {
|
|
827
|
+
nodeResults['$item'] =
|
|
828
|
+
reader.fanoutItems[fire.logical]?.[fire.itemIndex];
|
|
829
|
+
}
|
|
597
830
|
const resolvedInput = resolveSerializedInput(node.input, nodeResults);
|
|
598
831
|
executed++;
|
|
599
832
|
await executeGraphNodeInline(workflowService, rpcService, runId, graphName, fire.logical, fire.instanceKey, resolvedInput, nodes, fire.fromStepName);
|
|
@@ -602,6 +835,27 @@ async function continueGraphInline(workflowService, rpcService, runId, graphName
|
|
|
602
835
|
return;
|
|
603
836
|
}
|
|
604
837
|
}
|
|
838
|
+
function planEntryFirings(graphName, nodeId, node, triggerNodeResults, triggerInput) {
|
|
839
|
+
if (!node.forEach) {
|
|
840
|
+
const input = node.input && Object.keys(node.input).length > 0
|
|
841
|
+
? resolveSerializedInput(node.input, triggerNodeResults)
|
|
842
|
+
: triggerInput;
|
|
843
|
+
return [{ instanceKey: nodeId, input }];
|
|
844
|
+
}
|
|
845
|
+
const items = resolveForEachItems(graphName, nodeId, node, triggerNodeResults);
|
|
846
|
+
const width = node.mode === 'sequential' ? Math.min(items.length, 1) : items.length;
|
|
847
|
+
const firings = [];
|
|
848
|
+
for (let index = 0; index < width; index++) {
|
|
849
|
+
firings.push({
|
|
850
|
+
instanceKey: fanoutInstanceKey(nodeId, index),
|
|
851
|
+
input: resolveSerializedInput(node.input, {
|
|
852
|
+
...triggerNodeResults,
|
|
853
|
+
$item: items[index],
|
|
854
|
+
}),
|
|
855
|
+
});
|
|
856
|
+
}
|
|
857
|
+
return firings;
|
|
858
|
+
}
|
|
605
859
|
export async function runWorkflowGraph(workflowService, graphName, triggerInput, rpcService, inline, startNode, wire, overrideMeta) {
|
|
606
860
|
const meta = overrideMeta ?? getWorkflowMeta(graphName);
|
|
607
861
|
if (!meta?.nodes) {
|
|
@@ -643,10 +897,8 @@ export async function runWorkflowGraph(workflowService, graphName, triggerInput,
|
|
|
643
897
|
const node = nodes[nodeId];
|
|
644
898
|
if (!node?.rpcName)
|
|
645
899
|
return;
|
|
646
|
-
const
|
|
647
|
-
|
|
648
|
-
: triggerInput;
|
|
649
|
-
await executeGraphNodeInline(workflowService, rpcService, runId, graphName, nodeId, nodeId, resolvedInput, nodes);
|
|
900
|
+
const firings = planEntryFirings(graphName, nodeId, node, triggerNodeResults, triggerInput);
|
|
901
|
+
await Promise.all(firings.map((firing) => executeGraphNodeInline(workflowService, rpcService, runId, graphName, nodeId, firing.instanceKey, firing.input, nodes)));
|
|
650
902
|
}));
|
|
651
903
|
await continueGraphInline(workflowService, rpcService, runId, graphName, nodes, triggerInput, entryNodes);
|
|
652
904
|
}
|
|
@@ -672,10 +924,10 @@ export async function runWorkflowGraph(workflowService, graphName, triggerInput,
|
|
|
672
924
|
const node = nodes[nodeId];
|
|
673
925
|
if (!node?.rpcName)
|
|
674
926
|
continue;
|
|
675
|
-
const
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
927
|
+
const firings = planEntryFirings(graphName, nodeId, node, triggerNodeResults, triggerInput);
|
|
928
|
+
for (const firing of firings) {
|
|
929
|
+
await queueGraphNode(workflowService, runId, graphName, firing.instanceKey, node.rpcName, firing.input, node);
|
|
930
|
+
}
|
|
679
931
|
}
|
|
680
932
|
if (inline) {
|
|
681
933
|
workflowService.unregisterInlineRun(runId);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { TemplateString } from './template.js';
|
|
1
2
|
export interface RefValue {
|
|
2
3
|
__isRef: true;
|
|
3
4
|
nodeId: string;
|
|
@@ -7,9 +8,23 @@ export declare const createRef: (nodeId: string, path?: string) => RefValue;
|
|
|
7
8
|
export declare const isRef: (value: unknown) => value is RefValue;
|
|
8
9
|
export type RefFn<NodeIds extends string = string> = (nodeId: NodeIds, path?: string) => RefValue;
|
|
9
10
|
export type NextConfig<NodeIds extends string = string> = NodeIds | NodeIds[] | Record<string, NodeIds | NodeIds[]>;
|
|
11
|
+
export type TemplateFn = (templateStr: string, refs: Array<{
|
|
12
|
+
$ref: string;
|
|
13
|
+
path?: string;
|
|
14
|
+
}>) => TemplateString;
|
|
15
|
+
export type ItemFn = (path?: string) => RefValue;
|
|
16
|
+
export type ForEachConfig<NodeIds extends string = string> = NodeIds | RefValue | ((ref: RefFn<NodeIds>) => RefValue);
|
|
17
|
+
export type ForEachMode = 'parallel' | 'sequential';
|
|
10
18
|
export interface GraphNodeConfig<NodeIds extends string = string> {
|
|
11
19
|
func: string;
|
|
12
|
-
|
|
20
|
+
/**
|
|
21
|
+
* Run this node once per element of an upstream array. The node's own result
|
|
22
|
+
* becomes the ordered array of per-item results.
|
|
23
|
+
*/
|
|
24
|
+
forEach?: ForEachConfig<NodeIds>;
|
|
25
|
+
/** How the per-item instances of a `forEach` node run. Defaults to 'parallel'. */
|
|
26
|
+
mode?: ForEachMode;
|
|
27
|
+
input?: (ref: RefFn<NodeIds>, template: TemplateFn, $item: ItemFn) => Record<string, unknown>;
|
|
13
28
|
next?: NextConfig<NodeIds>;
|
|
14
29
|
onError?: NodeIds | NodeIds[];
|
|
15
30
|
retries?: number;
|
|
@@ -15,3 +15,5 @@ export { pikkuWorkflowGraph, type PikkuWorkflowGraphConfig, type PikkuWorkflowGr
|
|
|
15
15
|
export type { WorkflowStepInput as WorkflowStepQueueInput, PikkuWorkflowOrchestratorInput, PikkuWorkflowSleeperInput, } from './workflow-queue-workers.js';
|
|
16
16
|
export type { WorkflowService, WorkflowQueueOptions, WorkflowServiceConfig, WorkflowPlannedStep, WorkflowRunWire, WorkflowStatus, WorkflowVersionStatus, StepStatus, WorkflowRun, WorkflowRunStatus, StepState, WorkflowRunService, WorkflowRunMirror, CoreWorkflow, PikkuWorkflow, ContextVariable, WorkflowContext, WorkflowsMeta, WorkflowsRuntimeMeta, } from './workflow.types.js';
|
|
17
17
|
export type { WorkflowStepOptions, WorkflowWireDoRPC, WorkflowApprovalOptions, ApprovalOutcome, InputSource, OutputBinding, RpcStepMeta, Condition, BranchStepMeta, ParallelGroupStepMeta, FanoutStepMeta, ReturnStepMeta, InlineStepMeta, SleepStepMeta, CancelStepMeta, SuspendStepMeta, ApprovalStepMeta, SetStepMeta, SwitchCaseMeta, SwitchStepMeta, FilterStepMeta, ArrayPredicateStepMeta, WorkflowStepMeta, WorkflowStepWire, PikkuWorkflowWire, } from './workflow.types.js';
|
|
18
|
+
export { createGraph } from './graph/graph-node.js';
|
|
19
|
+
export type { GraphNodeConfig, ForEachConfig, ForEachMode, ItemFn, TemplateFn, } from './graph/workflow-graph.types.js';
|
|
@@ -9,3 +9,4 @@ export { buildRunTimeline, reconstructStateAt, reconstructFinalState, } from './
|
|
|
9
9
|
export { addWorkflow } from './dsl/workflow-runner.js';
|
|
10
10
|
export { template } from './graph/template.js';
|
|
11
11
|
export { pikkuWorkflowGraph, } from './graph/wire-workflow-graph.js';
|
|
12
|
+
export { createGraph } from './graph/graph-node.js';
|
|
@@ -7,13 +7,14 @@ import type { ScenarioBrowserProvider, ScenarioEnvironment, ScenarioSurface } fr
|
|
|
7
7
|
import type { PikkuWorkflowWire, WorkflowQueueOptions } from './workflow.types.js';
|
|
8
8
|
export { addFeature, resolveFeatureScenarios } from './feature.js';
|
|
9
9
|
export type * from './scenario.types.js';
|
|
10
|
+
export type * from './scenario-run.types.js';
|
|
10
11
|
export { SCENARIO_SURFACES } from './scenario-step.types.js';
|
|
11
12
|
export { resolveScenarioSurfaces } from './scenario-surface.js';
|
|
12
13
|
export { pollUntil, type PollOptions } from './scenario-poll.js';
|
|
13
14
|
export { createCookieJar } from './scenario-cookie-jar.js';
|
|
14
15
|
export type { ScenarioCookieJar } from './scenario-cookie-jar.js';
|
|
15
16
|
export { composeStepProse, renderStepTemplate } from './scenario-prose.js';
|
|
16
|
-
export {
|
|
17
|
+
export { requireScenarioEnv } from './scenario-step-guards.js';
|
|
17
18
|
export type { ScenarioHttpResponse, ScenarioJsonRequest, } from '../../services/personas-service.js';
|
|
18
19
|
/**
|
|
19
20
|
* A workflow service with the scenario capability attached — the two lines
|
|
@@ -72,10 +73,14 @@ export declare class ScenarioBrowserUnavailable extends PikkuError {
|
|
|
72
73
|
constructor(stepFunc: string);
|
|
73
74
|
}
|
|
74
75
|
/**
|
|
75
|
-
* A
|
|
76
|
-
*
|
|
76
|
+
* A step that is driven by a persona was dispatched without one.
|
|
77
|
+
*
|
|
78
|
+
* Raised before the body runs rather than partway through it: the step declared
|
|
79
|
+
* that it needs an actor — by having a `browser` binding, which cannot be
|
|
80
|
+
* provisioned without a window to open, or by `actor: true` — so a call site
|
|
81
|
+
* that omits one is a mistake at the call site.
|
|
77
82
|
*/
|
|
78
|
-
export declare class
|
|
83
|
+
export declare class ScenarioActorRequired extends PikkuError {
|
|
79
84
|
readonly stepFunc: string;
|
|
80
85
|
constructor(stepFunc: string);
|
|
81
86
|
}
|
|
@@ -224,4 +229,9 @@ export declare class PikkuScenarioService implements WorkflowRunExtension {
|
|
|
224
229
|
* treated as server-side only, which is what an ordinary function is.
|
|
225
230
|
*/
|
|
226
231
|
private declaredSurfaces;
|
|
232
|
+
/**
|
|
233
|
+
* Whether a step is driven by a persona, and so must be given one. Stamped by
|
|
234
|
+
* the definer from a `browser` binding or an explicit `actor: true`.
|
|
235
|
+
*/
|
|
236
|
+
private requiresActor;
|
|
227
237
|
}
|
|
@@ -7,6 +7,21 @@ import { InMemoryWorkflowService } from '../../services/in-memory-workflow-servi
|
|
|
7
7
|
import { runScheduledTask } from '../scheduler/scheduler-runner.js';
|
|
8
8
|
import { WorkflowStepNameNotString } from './workflow-errors.js';
|
|
9
9
|
import { resolveScenarioSurfaces, witnessesAgree } from './scenario-surface.js';
|
|
10
|
+
/**
|
|
11
|
+
* The retry default for the `expect*` family: none, the same as `given`,
|
|
12
|
+
* `when` and `then` take, and for the same reason — a failed assertion is a
|
|
13
|
+
* result, not a transient fault.
|
|
14
|
+
*
|
|
15
|
+
* Left to the workflow-wide `DEFAULT_STEP_RETRIES` they get six attempts each,
|
|
16
|
+
* which re-runs the RPC behind a failing `expectError`, restarts a whole
|
|
17
|
+
* `expectEventually` poll after it has already spent its own deadline, and
|
|
18
|
+
* lets a live judge re-roll a grade until one lands inside the band. The last
|
|
19
|
+
* of those is how it was found: a judge scored a deliberately useless answer
|
|
20
|
+
* 1, and the scenario went green anyway on the next attempt.
|
|
21
|
+
*
|
|
22
|
+
* A caller that genuinely wants attempts still asks for them.
|
|
23
|
+
*/
|
|
24
|
+
const assertionStep = (options) => ({ ...options, retries: options?.retries ?? 0 });
|
|
10
25
|
export { addFeature, resolveFeatureScenarios } from './feature.js';
|
|
11
26
|
export { SCENARIO_SURFACES } from './scenario-step.types.js';
|
|
12
27
|
// Which of a step's bindings run: one for an action, every witness for a `then`
|
|
@@ -14,7 +29,7 @@ export { resolveScenarioSurfaces } from './scenario-surface.js';
|
|
|
14
29
|
export { pollUntil } from './scenario-poll.js';
|
|
15
30
|
export { createCookieJar } from './scenario-cookie-jar.js';
|
|
16
31
|
export { composeStepProse, renderStepTemplate } from './scenario-prose.js';
|
|
17
|
-
export {
|
|
32
|
+
export { requireScenarioEnv } from './scenario-step-guards.js';
|
|
18
33
|
// The readers themselves live on `@pikku/core/persona`
|
|
19
34
|
/**
|
|
20
35
|
* A workflow service with the scenario capability attached — the two lines
|
|
@@ -90,20 +105,24 @@ addError(ScenarioBrowserUnavailable, {
|
|
|
90
105
|
message: 'No browser provider is registered.',
|
|
91
106
|
});
|
|
92
107
|
/**
|
|
93
|
-
* A
|
|
94
|
-
*
|
|
108
|
+
* A step that is driven by a persona was dispatched without one.
|
|
109
|
+
*
|
|
110
|
+
* Raised before the body runs rather than partway through it: the step declared
|
|
111
|
+
* that it needs an actor — by having a `browser` binding, which cannot be
|
|
112
|
+
* provisioned without a window to open, or by `actor: true` — so a call site
|
|
113
|
+
* that omits one is a mistake at the call site.
|
|
95
114
|
*/
|
|
96
|
-
export class
|
|
115
|
+
export class ScenarioActorRequired extends PikkuError {
|
|
97
116
|
stepFunc;
|
|
98
117
|
constructor(stepFunc) {
|
|
99
|
-
super(`[scenario] step '${stepFunc}'
|
|
100
|
-
`Pass { actor: actors.<name> } so
|
|
118
|
+
super(`[scenario] step '${stepFunc}' is driven by a persona but was called without an actor. ` +
|
|
119
|
+
`Pass { actor: actors.<name> } so it runs as that persona.`);
|
|
101
120
|
this.stepFunc = stepFunc;
|
|
102
121
|
}
|
|
103
122
|
}
|
|
104
|
-
addError(
|
|
123
|
+
addError(ScenarioActorRequired, {
|
|
105
124
|
status: 500,
|
|
106
|
-
message: '
|
|
125
|
+
message: 'This step needs an actor.',
|
|
107
126
|
});
|
|
108
127
|
/**
|
|
109
128
|
* A step declares no binding the run can execute.
|
|
@@ -433,7 +452,7 @@ export class PikkuScenarioService {
|
|
|
433
452
|
}
|
|
434
453
|
await new Promise((resolve) => setTimeout(resolve, interval));
|
|
435
454
|
}
|
|
436
|
-
}, options);
|
|
455
|
+
}, assertionStep(options));
|
|
437
456
|
},
|
|
438
457
|
expectError: async (stepName, rpcName, data, options) => {
|
|
439
458
|
this.engine.verifyStepName(stepName);
|
|
@@ -460,7 +479,7 @@ export class PikkuScenarioService {
|
|
|
460
479
|
return message;
|
|
461
480
|
}
|
|
462
481
|
throw new Error(`[workflow] expectError '${stepName}' ('${resolvedRpcName}') expected an error but the call succeeded: ${JSON.stringify(result)?.slice(0, 300)}`);
|
|
463
|
-
}, options);
|
|
482
|
+
}, assertionStep(options));
|
|
464
483
|
},
|
|
465
484
|
expectService: async (stepName, serviceMethod, options) => {
|
|
466
485
|
this.engine.verifyStepName(stepName);
|
|
@@ -489,7 +508,7 @@ export class PikkuScenarioService {
|
|
|
489
508
|
throw new Error(`[workflow] expectService '${stepName}' expected ${expected ?? 'at least one'} call(s) to '${serviceMethod}'` +
|
|
490
509
|
`${options?.calledWith !== undefined ? ` with ${JSON.stringify(options.calledWith)}` : ''}, found ${matching.length}. Recorded:\n ${seen}`);
|
|
491
510
|
}
|
|
492
|
-
}, options);
|
|
511
|
+
}, assertionStep(options));
|
|
493
512
|
},
|
|
494
513
|
expectScore: async (stepName, agentRunId, scorerName, options) => {
|
|
495
514
|
this.engine.verifyStepName(stepName);
|
|
@@ -518,7 +537,7 @@ export class PikkuScenarioService {
|
|
|
518
537
|
`${grade.reason ? `: ${grade.reason}` : ''}`);
|
|
519
538
|
}
|
|
520
539
|
return grade;
|
|
521
|
-
}, options);
|
|
540
|
+
}, assertionStep(options));
|
|
522
541
|
},
|
|
523
542
|
// knowledge: decisions/internals/scenario-given-and-when-are-sugar-but-then-is-not.md
|
|
524
543
|
given: (stepName, stepFunc, data, options) => this.scenarioStep('given', scenarioStepContext(), stepName, stepFunc, data, options),
|
|
@@ -546,6 +565,9 @@ export class PikkuScenarioService {
|
|
|
546
565
|
const description = options?.description ??
|
|
547
566
|
this.scenarioStepDescription(packageName, resolvedStepFunc) ??
|
|
548
567
|
stepName;
|
|
568
|
+
if (!actor && this.requiresActor(packageName, resolvedStepFunc)) {
|
|
569
|
+
throw new ScenarioActorRequired(resolvedStepFunc);
|
|
570
|
+
}
|
|
549
571
|
const declared = this.declaredSurfaces(packageName, resolvedStepFunc);
|
|
550
572
|
const resolution = resolveScenarioSurfaces(phase, declared, this.runSurface);
|
|
551
573
|
return await this.engine.inlineStep(runId, stepName, async () => {
|
|
@@ -556,13 +578,13 @@ export class PikkuScenarioService {
|
|
|
556
578
|
scenario: workflowWire,
|
|
557
579
|
pikkuUserId: workflowWire.pikkuUserId,
|
|
558
580
|
actors: this.runActors.get(runId),
|
|
581
|
+
...(actor ? { actor } : {}),
|
|
559
582
|
scenarioStep: {
|
|
560
583
|
name: resolvedStepFunc,
|
|
561
584
|
stepName,
|
|
562
585
|
runId,
|
|
563
586
|
phase,
|
|
564
587
|
surface,
|
|
565
|
-
actor,
|
|
566
588
|
env: this.scenarioEnvironment,
|
|
567
589
|
},
|
|
568
590
|
};
|
|
@@ -570,9 +592,8 @@ export class PikkuScenarioService {
|
|
|
570
592
|
if (!this.scenarioBrowserProvider) {
|
|
571
593
|
throw new ScenarioBrowserUnavailable(resolvedStepFunc);
|
|
572
594
|
}
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
}
|
|
595
|
+
// The dispatch guard above already refused an actor-less call, and a
|
|
596
|
+
// browser binding always requires one.
|
|
576
597
|
wire.browser = await this.scenarioBrowserProvider.sessionFor(actor.name);
|
|
577
598
|
}
|
|
578
599
|
return await runPikkuFunc('workflow', workflowName, resolvedStepFunc, {
|
|
@@ -640,4 +661,11 @@ export class PikkuScenarioService {
|
|
|
640
661
|
const surfaces = this.scenarioStepConfig(packageName, stepFunc)?.surfaces;
|
|
641
662
|
return surfaces?.length ? surfaces : ['default'];
|
|
642
663
|
}
|
|
664
|
+
/**
|
|
665
|
+
* Whether a step is driven by a persona, and so must be given one. Stamped by
|
|
666
|
+
* the definer from a `browser` binding or an explicit `actor: true`.
|
|
667
|
+
*/
|
|
668
|
+
requiresActor(packageName, stepFunc) {
|
|
669
|
+
return (this.scenarioStepConfig(packageName, stepFunc)?.requiresActor === true);
|
|
670
|
+
}
|
|
643
671
|
}
|