@pikku/core 0.12.79 → 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 +161 -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.js +2 -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/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 +9 -2
- 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 -2
- 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 +28 -147
- package/dist/wirings/workflow/pikku-workflow-service.js +71 -493
- 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/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 +13 -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 +2 -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/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-channel-meta.test.ts +44 -0
- package/src/wirings/gateway/gateway-runner.ts +27 -19
- 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 +22 -0
- package/src/wirings/secret/validate-secret-definitions.ts +2 -2
- 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 +190 -743
- 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-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
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import type { PikkuRawWire, SerializedError } from '../../types/core.types.js'
|
|
2
|
+
import type {
|
|
3
|
+
CoreWorkflow,
|
|
4
|
+
PikkuWorkflowWire,
|
|
5
|
+
StepState,
|
|
6
|
+
WorkflowRun,
|
|
7
|
+
WorkflowStatus,
|
|
8
|
+
WorkflowStepOptions,
|
|
9
|
+
} from './workflow.types.js'
|
|
10
|
+
|
|
11
|
+
export interface RunLifecycleContext {
|
|
12
|
+
runId: string
|
|
13
|
+
run: WorkflowRun
|
|
14
|
+
workflowMeta: any
|
|
15
|
+
workflow: CoreWorkflow
|
|
16
|
+
wire: PikkuRawWire
|
|
17
|
+
packageName: string | null
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* The subset of the workflow service a step executor is allowed to call back
|
|
22
|
+
* into.
|
|
23
|
+
*/
|
|
24
|
+
export interface WorkflowRunEngine {
|
|
25
|
+
inlineStep(
|
|
26
|
+
runId: string,
|
|
27
|
+
logicalStepName: string,
|
|
28
|
+
fn: Function,
|
|
29
|
+
stepOptions?: WorkflowStepOptions,
|
|
30
|
+
data?: any,
|
|
31
|
+
funcName?: string
|
|
32
|
+
): Promise<any>
|
|
33
|
+
updateRunStatus(
|
|
34
|
+
runId: string,
|
|
35
|
+
status: WorkflowStatus,
|
|
36
|
+
output?: any,
|
|
37
|
+
error?: SerializedError
|
|
38
|
+
): Promise<void>
|
|
39
|
+
onChildWorkflowFailed(run: WorkflowRun, error: unknown): Promise<void>
|
|
40
|
+
verifyStepName(stepName: unknown): void
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Hooks a host may install to decorate runs it did not start. Used by the
|
|
45
|
+
* scenario service to attach its own per-run state.
|
|
46
|
+
*/
|
|
47
|
+
export interface WorkflowRunExtension {
|
|
48
|
+
attachRunContext(
|
|
49
|
+
runId: string,
|
|
50
|
+
workflowMeta: any,
|
|
51
|
+
options?: Record<string, any>
|
|
52
|
+
): Promise<void>
|
|
53
|
+
detachRunContext(runId: string): void
|
|
54
|
+
decorateRunWire(
|
|
55
|
+
wire: PikkuRawWire,
|
|
56
|
+
context: {
|
|
57
|
+
runId: string
|
|
58
|
+
workflowMeta: any
|
|
59
|
+
workflowWire: PikkuWorkflowWire
|
|
60
|
+
}
|
|
61
|
+
): void
|
|
62
|
+
decorateWorkflowWire(
|
|
63
|
+
workflowWire: PikkuWorkflowWire,
|
|
64
|
+
context: {
|
|
65
|
+
name: string
|
|
66
|
+
runId: string
|
|
67
|
+
rpcService: any
|
|
68
|
+
addonNamespace?: string | null
|
|
69
|
+
}
|
|
70
|
+
): void
|
|
71
|
+
onBeforeRunFunc(context: RunLifecycleContext): Promise<void>
|
|
72
|
+
onAfterRunFunc(
|
|
73
|
+
context: RunLifecycleContext,
|
|
74
|
+
outcome: 'completed' | 'failed' | 'interrupted',
|
|
75
|
+
failure: unknown
|
|
76
|
+
): Promise<void>
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Per-run bookkeeping held only for the lifetime of an in-process execution.
|
|
81
|
+
*/
|
|
82
|
+
export type RunContext = {
|
|
83
|
+
activeExecutions: number
|
|
84
|
+
inline?: boolean
|
|
85
|
+
ordinals: Map<string, number>
|
|
86
|
+
lastStep?: string
|
|
87
|
+
replay?: {
|
|
88
|
+
steps?: Map<string, StepState>
|
|
89
|
+
run?: WorkflowRun
|
|
90
|
+
}
|
|
91
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { ForbiddenError } from '../../errors/errors.js'
|
|
2
|
+
import { addError } from '../../errors/error-handler.js'
|
|
3
|
+
import type { CoreUserSession } from '../../types/core.types.js'
|
|
4
|
+
import type { WorkflowRunWire } from './workflow.types.js'
|
|
5
|
+
|
|
6
|
+
export class WorkflowRunForbiddenError extends ForbiddenError {
|
|
7
|
+
constructor() {
|
|
8
|
+
super('Not authorized to access this workflow run')
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
addError(WorkflowRunForbiddenError, {
|
|
12
|
+
status: 403,
|
|
13
|
+
message: 'Not authorized to access this workflow run.',
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* A run started through a session records that session's user as its owner, and
|
|
18
|
+
* only that user may read it or answer its approval gates.
|
|
19
|
+
*
|
|
20
|
+
* A run with no recorded owner — started by a trigger, a scheduler, or a route
|
|
21
|
+
* wired without auth — has nobody to compare a caller against, so ownership is
|
|
22
|
+
* not a control that exists for it. Gate those with `auth` or `permissions` on
|
|
23
|
+
* the entrypoint instead.
|
|
24
|
+
*/
|
|
25
|
+
export const assertWorkflowRunOwner = (
|
|
26
|
+
wire: WorkflowRunWire | undefined,
|
|
27
|
+
session: CoreUserSession | undefined
|
|
28
|
+
): void => {
|
|
29
|
+
const owner = wire?.pikkuUserId
|
|
30
|
+
if (!owner) {
|
|
31
|
+
return
|
|
32
|
+
}
|
|
33
|
+
if (!session?.userId || session.userId !== owner) {
|
|
34
|
+
throw new WorkflowRunForbiddenError()
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { WorkflowSuspendedException } from './workflow-errors.js'
|
|
2
|
+
import type { ApprovalStore } from './workflow-approval.js'
|
|
3
|
+
import type { StepState } from './workflow.types.js'
|
|
4
|
+
|
|
5
|
+
/** The durable step name a suspension point is recorded under. */
|
|
6
|
+
export const suspendStepNameFor = (reason: string): string =>
|
|
7
|
+
`__workflow_suspend:${reason}`
|
|
8
|
+
|
|
9
|
+
/** What the suspend gate needs from the workflow service. */
|
|
10
|
+
export type SuspendStore = Pick<
|
|
11
|
+
ApprovalStore,
|
|
12
|
+
'getStepState' | 'insertStepState' | 'setStepRunning' | 'setStepResult'
|
|
13
|
+
>
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Record a suspension point and unwind the run.
|
|
17
|
+
*
|
|
18
|
+
* A suspension that has already succeeded returns instead of throwing, so a
|
|
19
|
+
* replay walks past a gate the run has already passed through.
|
|
20
|
+
*/
|
|
21
|
+
export const recordSuspension = async (
|
|
22
|
+
store: SuspendStore,
|
|
23
|
+
runId: string,
|
|
24
|
+
reason: string,
|
|
25
|
+
stepName: string,
|
|
26
|
+
fromStepName: string | undefined
|
|
27
|
+
): Promise<void> => {
|
|
28
|
+
const insert = () =>
|
|
29
|
+
store.insertStepState(
|
|
30
|
+
runId,
|
|
31
|
+
stepName,
|
|
32
|
+
'pikkuWorkflowSuspend',
|
|
33
|
+
{ reason },
|
|
34
|
+
undefined,
|
|
35
|
+
fromStepName
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
let stepState: StepState
|
|
39
|
+
try {
|
|
40
|
+
stepState = await store.getStepState(runId, stepName)
|
|
41
|
+
} catch {
|
|
42
|
+
stepState = await insert()
|
|
43
|
+
}
|
|
44
|
+
if (!stepState.stepId) {
|
|
45
|
+
stepState = await insert()
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (stepState.status === 'succeeded') {
|
|
49
|
+
return
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (stepState.status === 'pending') {
|
|
53
|
+
await store.setStepRunning(stepState.stepId)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
await store.setStepResult(stepState.stepId, {
|
|
57
|
+
reason,
|
|
58
|
+
suspendedAt: new Date().toISOString(),
|
|
59
|
+
})
|
|
60
|
+
throw new WorkflowSuspendedException(runId, reason)
|
|
61
|
+
}
|
|
@@ -120,6 +120,13 @@ export interface WorkflowRun {
|
|
|
120
120
|
export interface StepState {
|
|
121
121
|
stepId: string
|
|
122
122
|
status: StepStatus
|
|
123
|
+
/**
|
|
124
|
+
* The function the workflow dispatched this step with, recorded so a worker
|
|
125
|
+
* can reject a queue message naming anything else. `null` is a step with no
|
|
126
|
+
* function of its own (inline work); `undefined` is a store that never
|
|
127
|
+
* recorded one, and cannot be compared against.
|
|
128
|
+
*/
|
|
129
|
+
rpcName?: string | null
|
|
123
130
|
result?: any
|
|
124
131
|
error?: SerializedError
|
|
125
132
|
attemptCount: number
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { describe, test } from 'node:test'
|
|
2
|
+
import assert from 'node:assert/strict'
|
|
3
|
+
import { readFileSync, readdirSync } from 'node:fs'
|
|
4
|
+
import { fileURLToPath } from 'node:url'
|
|
5
|
+
import { join, dirname, resolve, relative, sep } from 'node:path'
|
|
6
|
+
|
|
7
|
+
const srcRoot = dirname(fileURLToPath(import.meta.url))
|
|
8
|
+
const wiringsRoot = join(srcRoot, 'wirings')
|
|
9
|
+
|
|
10
|
+
/** Type-only statements are erased, so they couple nothing at runtime. */
|
|
11
|
+
const TYPE_ONLY = /^\s*(?:import|export)\s+type\b/
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Which wire may reach which, at runtime.
|
|
15
|
+
*
|
|
16
|
+
* A wire should be self-contained apart from what is genuinely common, so every
|
|
17
|
+
* entry here is a deliberate crossover point rather than an accident. Adding one
|
|
18
|
+
* is a visible diff — and worth asking whether the dependency could be
|
|
19
|
+
* type-only, which couples nothing at runtime.
|
|
20
|
+
*/
|
|
21
|
+
const ALLOWED: Record<string, string[]> = {
|
|
22
|
+
// A channel is an HTTP upgrade, so it builds on the HTTP wire.
|
|
23
|
+
channel: ['http'],
|
|
24
|
+
// `remote` on a wire with no peer — one leaf module, not the channel runtime.
|
|
25
|
+
http: ['channel'],
|
|
26
|
+
cli: ['channel'],
|
|
27
|
+
// An agent streams over a channel and is invoked over rpc.
|
|
28
|
+
'ai-agent': ['channel', 'rpc'],
|
|
29
|
+
// `wire.rpc.agent` — the facade lives with the agent runtime it delegates to.
|
|
30
|
+
rpc: ['ai-agent'],
|
|
31
|
+
gateway: ['http'],
|
|
32
|
+
mcp: ['rpc'],
|
|
33
|
+
trigger: ['rpc'],
|
|
34
|
+
// A workflow step is dispatched to a queue, invoked over rpc, or slept on.
|
|
35
|
+
workflow: ['queue', 'rpc', 'scheduler'],
|
|
36
|
+
// A virtual user drives a scenario, which is a workflow, as a persona.
|
|
37
|
+
'virtual-user': ['role', 'workflow'],
|
|
38
|
+
persona: ['virtual-user'],
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const sourceFiles = (directory: string): string[] =>
|
|
42
|
+
readdirSync(directory, { withFileTypes: true }).flatMap((entry) => {
|
|
43
|
+
const entryPath = join(directory, entry.name)
|
|
44
|
+
if (entry.isDirectory()) {
|
|
45
|
+
return entry.name === 'dist' ? [] : sourceFiles(entryPath)
|
|
46
|
+
}
|
|
47
|
+
return entry.name.endsWith('.ts') && !entry.name.endsWith('.test.ts')
|
|
48
|
+
? [entryPath]
|
|
49
|
+
: []
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
/** Every wire each wire reaches at runtime, following value imports only. */
|
|
53
|
+
const crossWireEdges = (): Map<string, Set<string>> => {
|
|
54
|
+
const edges = new Map<string, Set<string>>()
|
|
55
|
+
|
|
56
|
+
for (const file of sourceFiles(wiringsRoot)) {
|
|
57
|
+
const from = relative(wiringsRoot, file).split(sep)[0]!
|
|
58
|
+
|
|
59
|
+
for (const line of readFileSync(file, 'utf-8').split('\n')) {
|
|
60
|
+
if (TYPE_ONLY.test(line)) continue
|
|
61
|
+
const specifier = line.match(/\bfrom\s+'(\.[^']+)'/)?.[1]
|
|
62
|
+
if (!specifier) continue
|
|
63
|
+
|
|
64
|
+
const target = resolve(dirname(file), specifier)
|
|
65
|
+
if (!target.startsWith(wiringsRoot + sep)) continue
|
|
66
|
+
|
|
67
|
+
const to = relative(wiringsRoot, target).split(sep)[0]!
|
|
68
|
+
if (to === from) continue
|
|
69
|
+
if (!edges.has(from)) edges.set(from, new Set())
|
|
70
|
+
edges.get(from)!.add(to)
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return edges
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
describe('wirings stay decoupled', () => {
|
|
78
|
+
test('no wire reaches a wire it is not declared to', () => {
|
|
79
|
+
const undeclared = [...crossWireEdges()]
|
|
80
|
+
.flatMap(([from, tos]) =>
|
|
81
|
+
[...tos]
|
|
82
|
+
.filter((to) => !(ALLOWED[from] ?? []).includes(to))
|
|
83
|
+
.map((to) => `${from} -> ${to}`)
|
|
84
|
+
)
|
|
85
|
+
.sort()
|
|
86
|
+
|
|
87
|
+
assert.deepEqual(
|
|
88
|
+
undeclared,
|
|
89
|
+
[],
|
|
90
|
+
'a wire gained a runtime dependency on another wire:\n' +
|
|
91
|
+
`${undeclared.join('\n')}\n` +
|
|
92
|
+
'Add it to ALLOWED only if it is genuinely necessary — a type-only ' +
|
|
93
|
+
'import couples nothing.'
|
|
94
|
+
)
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
test('every declared crossover still exists', () => {
|
|
98
|
+
const actual = crossWireEdges()
|
|
99
|
+
const stale = Object.entries(ALLOWED)
|
|
100
|
+
.flatMap(([from, tos]) =>
|
|
101
|
+
tos
|
|
102
|
+
.filter((to) => !actual.get(from)?.has(to))
|
|
103
|
+
.map((to) => `${from} -> ${to}`)
|
|
104
|
+
)
|
|
105
|
+
.sort()
|
|
106
|
+
|
|
107
|
+
assert.deepEqual(
|
|
108
|
+
stale,
|
|
109
|
+
[],
|
|
110
|
+
`these crossovers are declared but no longer real — delete them:\n${stale.join('\n')}`
|
|
111
|
+
)
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
test('the walker finds the edges it is meant to', () => {
|
|
115
|
+
// Guards both tests above: a walker that resolved nothing would pass
|
|
116
|
+
// whatever the wirings imported.
|
|
117
|
+
const edges = crossWireEdges()
|
|
118
|
+
assert.ok(
|
|
119
|
+
edges.get('channel')?.has('http'),
|
|
120
|
+
`expected channel -> http; walker found ${edges.size} wires with edges`
|
|
121
|
+
)
|
|
122
|
+
})
|
|
123
|
+
})
|