@relayflows/sdk 2.0.8 → 2.0.10
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/dist/adapters/base.d.ts +46 -0
- package/dist/adapters/base.d.ts.map +1 -0
- package/dist/adapters/base.js +15 -0
- package/dist/adapters/base.js.map +1 -0
- package/dist/adapters/claude.d.ts +5 -0
- package/dist/adapters/claude.d.ts.map +1 -0
- package/dist/adapters/claude.js +44 -0
- package/dist/adapters/claude.js.map +1 -0
- package/dist/adapters/codex.d.ts +5 -0
- package/dist/adapters/codex.d.ts.map +1 -0
- package/dist/adapters/codex.js +45 -0
- package/dist/adapters/codex.js.map +1 -0
- package/dist/adapters/index.d.ts +14 -0
- package/dist/adapters/index.d.ts.map +1 -0
- package/dist/adapters/index.js +29 -0
- package/dist/adapters/index.js.map +1 -0
- package/dist/adapters/wrapper.d.ts +12 -0
- package/dist/adapters/wrapper.d.ts.map +1 -0
- package/dist/adapters/wrapper.js +31 -0
- package/dist/adapters/wrapper.js.map +1 -0
- package/dist/agent-relay-transport.d.ts +84 -0
- package/dist/agent-relay-transport.d.ts.map +1 -0
- package/dist/agent-relay-transport.js +134 -0
- package/dist/agent-relay-transport.js.map +1 -0
- package/dist/authored-budget.d.ts +13 -0
- package/dist/authored-budget.d.ts.map +1 -0
- package/dist/authored-budget.js +78 -0
- package/dist/authored-budget.js.map +1 -0
- package/dist/authored-flow-error.d.ts +1 -1
- package/dist/authored-flow-error.d.ts.map +1 -1
- package/dist/authored-flow-error.js.map +1 -1
- package/dist/authored-flow-executor.d.ts +13 -7
- package/dist/authored-flow-executor.d.ts.map +1 -1
- package/dist/authored-flow-executor.js +115 -194
- package/dist/authored-flow-executor.js.map +1 -1
- package/dist/authored-flow-loader.d.ts +10 -1
- package/dist/authored-flow-loader.d.ts.map +1 -1
- package/dist/authored-flow-loader.js +56 -3
- package/dist/authored-flow-loader.js.map +1 -1
- package/dist/authored-flow-operation.d.ts +9 -1
- package/dist/authored-flow-operation.d.ts.map +1 -1
- package/dist/authored-flow-operation.js +35 -4
- package/dist/authored-flow-operation.js.map +1 -1
- package/dist/authored-helper-effect.d.ts +12 -0
- package/dist/authored-helper-effect.d.ts.map +1 -0
- package/dist/authored-helper-effect.js +144 -0
- package/dist/authored-helper-effect.js.map +1 -0
- package/dist/authored-mcp.d.ts +17 -0
- package/dist/authored-mcp.d.ts.map +1 -0
- package/dist/authored-mcp.js +171 -0
- package/dist/authored-mcp.js.map +1 -0
- package/dist/authored-memory.d.ts +7 -0
- package/dist/authored-memory.d.ts.map +1 -0
- package/dist/authored-memory.js +60 -0
- package/dist/authored-memory.js.map +1 -0
- package/dist/authored-plugin-effect.d.ts +16 -0
- package/dist/authored-plugin-effect.d.ts.map +1 -0
- package/dist/authored-plugin-effect.js +163 -0
- package/dist/authored-plugin-effect.js.map +1 -0
- package/dist/authored-step-output.d.ts +23 -0
- package/dist/authored-step-output.d.ts.map +1 -0
- package/dist/authored-step-output.js +67 -0
- package/dist/authored-step-output.js.map +1 -0
- package/dist/authored-worker-step.d.ts +15 -0
- package/dist/authored-worker-step.d.ts.map +1 -0
- package/dist/authored-worker-step.js +136 -0
- package/dist/authored-worker-step.js.map +1 -0
- package/dist/budget-preflight.d.ts +5 -0
- package/dist/budget-preflight.d.ts.map +1 -0
- package/dist/budget-preflight.js +33 -0
- package/dist/budget-preflight.js.map +1 -0
- package/dist/budget.d.ts +14 -0
- package/dist/budget.d.ts.map +1 -0
- package/dist/budget.js +52 -0
- package/dist/budget.js.map +1 -0
- package/dist/bundle-transport.d.ts +18 -0
- package/dist/bundle-transport.d.ts.map +1 -0
- package/dist/bundle-transport.js +111 -0
- package/dist/bundle-transport.js.map +1 -0
- package/dist/bundle-typescript.d.ts +8 -0
- package/dist/bundle-typescript.d.ts.map +1 -0
- package/dist/bundle-typescript.js +108 -0
- package/dist/bundle-typescript.js.map +1 -0
- package/dist/bundle.d.ts +25 -0
- package/dist/bundle.d.ts.map +1 -0
- package/dist/bundle.js +199 -0
- package/dist/bundle.js.map +1 -0
- package/dist/child-stop.d.ts +59 -0
- package/dist/child-stop.d.ts.map +1 -0
- package/dist/child-stop.js +94 -0
- package/dist/child-stop.js.map +1 -0
- package/dist/cli/add.d.ts +6 -0
- package/dist/cli/add.d.ts.map +1 -0
- package/dist/cli/add.js +47 -0
- package/dist/cli/add.js.map +1 -0
- package/dist/cli/build.d.ts +13 -0
- package/dist/cli/build.d.ts.map +1 -0
- package/dist/cli/build.js +224 -0
- package/dist/cli/build.js.map +1 -0
- package/dist/cli/bundle-preflight.d.ts +5 -0
- package/dist/cli/bundle-preflight.d.ts.map +1 -0
- package/dist/cli/bundle-preflight.js +28 -0
- package/dist/cli/bundle-preflight.js.map +1 -0
- package/dist/cli/check-helper-body.d.ts +4 -0
- package/dist/cli/check-helper-body.d.ts.map +1 -0
- package/dist/cli/check-helper-body.js +15 -0
- package/dist/cli/check-helper-body.js.map +1 -0
- package/dist/cli/check-triggers.d.ts +14 -0
- package/dist/cli/check-triggers.d.ts.map +1 -0
- package/dist/cli/check-triggers.js +37 -0
- package/dist/cli/check-triggers.js.map +1 -0
- package/dist/cli/check-typescript.d.ts +20 -0
- package/dist/cli/check-typescript.d.ts.map +1 -0
- package/dist/cli/check-typescript.js +54 -0
- package/dist/cli/check-typescript.js.map +1 -0
- package/dist/cli/check.d.ts +36 -3
- package/dist/cli/check.d.ts.map +1 -1
- package/dist/cli/check.js +122 -11
- package/dist/cli/check.js.map +1 -1
- package/dist/cli/cloud-run.d.ts +8 -0
- package/dist/cli/cloud-run.d.ts.map +1 -0
- package/dist/cli/cloud-run.js +50 -0
- package/dist/cli/cloud-run.js.map +1 -0
- package/dist/cli/deploy.d.ts +9 -0
- package/dist/cli/deploy.d.ts.map +1 -0
- package/dist/cli/deploy.js +58 -0
- package/dist/cli/deploy.js.map +1 -0
- package/dist/cli/deterministic-failure.d.ts +5 -0
- package/dist/cli/deterministic-failure.d.ts.map +1 -0
- package/dist/cli/deterministic-failure.js +66 -0
- package/dist/cli/deterministic-failure.js.map +1 -0
- package/dist/cli/direct-run.d.ts.map +1 -1
- package/dist/cli/direct-run.js +83 -7
- package/dist/cli/direct-run.js.map +1 -1
- package/dist/cli/hn-monitor.d.ts.map +1 -1
- package/dist/cli/hn-monitor.js +2 -1
- package/dist/cli/hn-monitor.js.map +1 -1
- package/dist/cli/replay.d.ts +12 -0
- package/dist/cli/replay.d.ts.map +1 -0
- package/dist/cli/replay.js +82 -0
- package/dist/cli/replay.js.map +1 -0
- package/dist/cli/reuse.d.ts +8 -0
- package/dist/cli/reuse.d.ts.map +1 -0
- package/dist/cli/reuse.js +25 -0
- package/dist/cli/reuse.js.map +1 -0
- package/dist/cli/run-digest.d.ts +4 -0
- package/dist/cli/run-digest.d.ts.map +1 -0
- package/dist/cli/run-digest.js +24 -0
- package/dist/cli/run-digest.js.map +1 -0
- package/dist/cli/run.d.ts +16 -5
- package/dist/cli/run.d.ts.map +1 -1
- package/dist/cli/run.js +99 -17
- package/dist/cli/run.js.map +1 -1
- package/dist/cli/serve-webhook.d.ts +40 -0
- package/dist/cli/serve-webhook.d.ts.map +1 -0
- package/dist/cli/serve-webhook.js +233 -0
- package/dist/cli/serve-webhook.js.map +1 -0
- package/dist/cli/tick-runner.d.ts.map +1 -1
- package/dist/cli/tick-runner.js +2 -1
- package/dist/cli/tick-runner.js.map +1 -1
- package/dist/cli-adapter.d.ts +8 -14
- package/dist/cli-adapter.d.ts.map +1 -1
- package/dist/cli-adapter.js +19 -64
- package/dist/cli-adapter.js.map +1 -1
- package/dist/cli-watch.d.ts +13 -0
- package/dist/cli-watch.d.ts.map +1 -0
- package/dist/cli-watch.js +185 -0
- package/dist/cli-watch.js.map +1 -0
- package/dist/cli.d.ts +4 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +387 -14
- package/dist/cli.js.map +1 -1
- package/dist/cloud-http.d.ts +22 -0
- package/dist/cloud-http.d.ts.map +1 -0
- package/dist/cloud-http.js +84 -0
- package/dist/cloud-http.js.map +1 -0
- package/dist/cloud-run.d.ts +38 -0
- package/dist/cloud-run.d.ts.map +1 -0
- package/dist/cloud-run.js +112 -0
- package/dist/cloud-run.js.map +1 -0
- package/dist/compile.d.ts +21 -3
- package/dist/compile.d.ts.map +1 -1
- package/dist/compile.js +97 -23
- package/dist/compile.js.map +1 -1
- package/dist/create-flow.d.ts +17 -0
- package/dist/create-flow.d.ts.map +1 -0
- package/dist/create-flow.js +72 -0
- package/dist/create-flow.js.map +1 -0
- package/dist/daemon-connection.d.ts +14 -0
- package/dist/daemon-connection.d.ts.map +1 -1
- package/dist/daemon-connection.js +33 -1
- package/dist/daemon-connection.js.map +1 -1
- package/dist/demo-hn-monitor.js +2 -1
- package/dist/demo-hn-monitor.js.map +1 -1
- package/dist/effect-channel.d.ts +30 -0
- package/dist/effect-channel.d.ts.map +1 -0
- package/dist/effect-channel.js +71 -0
- package/dist/effect-channel.js.map +1 -0
- package/dist/failure-kinds.d.ts +14 -3
- package/dist/failure-kinds.d.ts.map +1 -1
- package/dist/failure-kinds.js +27 -0
- package/dist/failure-kinds.js.map +1 -1
- package/dist/gate-contract.d.ts.map +1 -1
- package/dist/gate-contract.js +9 -0
- package/dist/gate-contract.js.map +1 -1
- package/dist/helper-preflight.d.ts +18 -0
- package/dist/helper-preflight.d.ts.map +1 -0
- package/dist/helper-preflight.js +32 -0
- package/dist/helper-preflight.js.map +1 -0
- package/dist/helper-storage.d.ts +5 -0
- package/dist/helper-storage.d.ts.map +1 -0
- package/dist/helper-storage.js +31 -0
- package/dist/helper-storage.js.map +1 -0
- package/dist/helper-writeback.d.ts +12 -0
- package/dist/helper-writeback.d.ts.map +1 -0
- package/dist/helper-writeback.js +95 -0
- package/dist/helper-writeback.js.map +1 -0
- package/dist/index.d.ts +10 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -1
- package/dist/index.js.map +1 -1
- package/dist/input-binding.d.ts +4 -0
- package/dist/input-binding.d.ts.map +1 -0
- package/dist/input-binding.js +78 -0
- package/dist/input-binding.js.map +1 -0
- package/dist/journal-client.d.ts +12 -4
- package/dist/journal-client.d.ts.map +1 -1
- package/dist/journal-client.js +17 -6
- package/dist/journal-client.js.map +1 -1
- package/dist/journal-offset.d.ts +3 -0
- package/dist/journal-offset.d.ts.map +1 -0
- package/dist/journal-offset.js +69 -0
- package/dist/journal-offset.js.map +1 -0
- package/dist/journal-reader.d.ts +30 -0
- package/dist/journal-reader.d.ts.map +1 -0
- package/dist/journal-reader.js +156 -0
- package/dist/journal-reader.js.map +1 -0
- package/dist/json-schema.d.ts +2 -0
- package/dist/json-schema.d.ts.map +1 -1
- package/dist/json-schema.js +38 -22
- package/dist/json-schema.js.map +1 -1
- package/dist/llm-worker.d.ts +16 -0
- package/dist/llm-worker.d.ts.map +1 -0
- package/dist/llm-worker.js +79 -0
- package/dist/llm-worker.js.map +1 -0
- package/dist/local-agent.d.ts +8 -0
- package/dist/local-agent.d.ts.map +1 -0
- package/dist/local-agent.js +25 -0
- package/dist/local-agent.js.map +1 -0
- package/dist/mcp-client.d.ts +15 -0
- package/dist/mcp-client.d.ts.map +1 -0
- package/dist/mcp-client.js +104 -0
- package/dist/mcp-client.js.map +1 -0
- package/dist/mcp-config.d.ts +4 -0
- package/dist/mcp-config.d.ts.map +1 -0
- package/dist/mcp-config.js +61 -0
- package/dist/mcp-config.js.map +1 -0
- package/dist/mcp-stdio.d.ts +24 -0
- package/dist/mcp-stdio.d.ts.map +1 -0
- package/dist/mcp-stdio.js +89 -0
- package/dist/mcp-stdio.js.map +1 -0
- package/dist/model-pricing.d.ts +22 -0
- package/dist/model-pricing.d.ts.map +1 -0
- package/dist/model-pricing.js +30 -0
- package/dist/model-pricing.js.map +1 -0
- package/dist/mount-registry.d.ts +9 -0
- package/dist/mount-registry.d.ts.map +1 -0
- package/dist/mount-registry.js +47 -0
- package/dist/mount-registry.js.map +1 -0
- package/dist/named-gate-lowering.d.ts +9 -0
- package/dist/named-gate-lowering.d.ts.map +1 -0
- package/dist/named-gate-lowering.js +152 -0
- package/dist/named-gate-lowering.js.map +1 -0
- package/dist/named-gates.d.ts +10 -0
- package/dist/named-gates.d.ts.map +1 -0
- package/dist/named-gates.js +69 -0
- package/dist/named-gates.js.map +1 -0
- package/dist/observer-link.d.ts +129 -0
- package/dist/observer-link.d.ts.map +1 -0
- package/dist/observer-link.js +274 -0
- package/dist/observer-link.js.map +1 -0
- package/dist/plugin-loader.d.ts +13 -0
- package/dist/plugin-loader.d.ts.map +1 -0
- package/dist/plugin-loader.js +110 -0
- package/dist/plugin-loader.js.map +1 -0
- package/dist/plugin-manifest.d.ts +27 -0
- package/dist/plugin-manifest.d.ts.map +1 -0
- package/dist/plugin-manifest.js +89 -0
- package/dist/plugin-manifest.js.map +1 -0
- package/dist/preflight.d.ts +26 -2
- package/dist/preflight.d.ts.map +1 -1
- package/dist/preflight.js +175 -1
- package/dist/preflight.js.map +1 -1
- package/dist/progress.d.ts +14 -0
- package/dist/progress.d.ts.map +1 -0
- package/dist/progress.js +39 -0
- package/dist/progress.js.map +1 -0
- package/dist/protocol.d.ts +11 -0
- package/dist/protocol.d.ts.map +1 -1
- package/dist/pty-sidechannel.d.ts +14 -0
- package/dist/pty-sidechannel.d.ts.map +1 -0
- package/dist/pty-sidechannel.js +103 -0
- package/dist/pty-sidechannel.js.map +1 -0
- package/dist/scope-compiler.d.ts +52 -0
- package/dist/scope-compiler.d.ts.map +1 -0
- package/dist/scope-compiler.js +62 -0
- package/dist/scope-compiler.js.map +1 -0
- package/dist/slack-preflight.d.ts +6 -0
- package/dist/slack-preflight.d.ts.map +1 -0
- package/dist/slack-preflight.js +28 -0
- package/dist/slack-preflight.js.map +1 -0
- package/dist/slack-writeback.d.ts +40 -0
- package/dist/slack-writeback.d.ts.map +1 -0
- package/dist/slack-writeback.js +60 -0
- package/dist/slack-writeback.js.map +1 -0
- package/dist/spec.d.ts +134 -4
- package/dist/spec.d.ts.map +1 -1
- package/dist/spec.js.map +1 -1
- package/dist/step-dependencies.d.ts.map +1 -1
- package/dist/step-dependencies.js +2 -1
- package/dist/step-dependencies.js.map +1 -1
- package/dist/step-fields.d.ts +4 -4
- package/dist/step-fields.d.ts.map +1 -1
- package/dist/step-fields.js +5 -2
- package/dist/step-fields.js.map +1 -1
- package/dist/trigger-executor.d.ts +7 -0
- package/dist/trigger-executor.d.ts.map +1 -0
- package/dist/trigger-executor.js +35 -0
- package/dist/trigger-executor.js.map +1 -0
- package/dist/validate.d.ts.map +1 -1
- package/dist/validate.js +53 -6
- package/dist/validate.js.map +1 -1
- package/dist/webhook-rate-limit.d.ts +29 -0
- package/dist/webhook-rate-limit.d.ts.map +1 -0
- package/dist/webhook-rate-limit.js +38 -0
- package/dist/webhook-rate-limit.js.map +1 -0
- package/dist/webhook-signature.d.ts +22 -0
- package/dist/webhook-signature.d.ts.map +1 -0
- package/dist/webhook-signature.js +53 -0
- package/dist/webhook-signature.js.map +1 -0
- package/dist/worker-cli.d.ts +13 -1
- package/dist/worker-cli.d.ts.map +1 -1
- package/dist/worker-cli.js +133 -12
- package/dist/worker-cli.js.map +1 -1
- package/dist/worker-input.d.ts +4 -0
- package/dist/worker-input.d.ts.map +1 -0
- package/dist/worker-input.js +14 -0
- package/dist/worker-input.js.map +1 -0
- package/dist/worker-lease.d.ts +5 -0
- package/dist/worker-lease.d.ts.map +1 -0
- package/dist/worker-lease.js +80 -0
- package/dist/worker-lease.js.map +1 -0
- package/dist/worker-spend.d.ts +19 -0
- package/dist/worker-spend.d.ts.map +1 -0
- package/dist/worker-spend.js +22 -0
- package/dist/worker-spend.js.map +1 -0
- package/dist/worker-usage.d.ts +7 -0
- package/dist/worker-usage.d.ts.map +1 -0
- package/dist/worker-usage.js +54 -0
- package/dist/worker-usage.js.map +1 -0
- package/dist/worker.d.ts +2 -0
- package/dist/worker.d.ts.map +1 -1
- package/dist/worker.js +22 -8
- package/dist/worker.js.map +1 -1
- package/dist/wrapper-session.d.ts +1 -1
- package/dist/wrapper-session.d.ts.map +1 -1
- package/dist/wrapper-session.js +56 -19
- package/dist/wrapper-session.js.map +1 -1
- package/dist/yaml-helper-effect.d.ts +8 -0
- package/dist/yaml-helper-effect.d.ts.map +1 -0
- package/dist/yaml-helper-effect.js +79 -0
- package/dist/yaml-helper-effect.js.map +1 -0
- package/dist/yaml-helpers.d.ts +18 -0
- package/dist/yaml-helpers.d.ts.map +1 -0
- package/dist/yaml-helpers.js +116 -0
- package/dist/yaml-helpers.js.map +1 -0
- package/package.json +19 -2
- package/src/adapters/base.ts +53 -0
- package/src/adapters/claude.ts +55 -0
- package/src/adapters/codex.ts +56 -0
- package/src/adapters/index.ts +36 -0
- package/src/adapters/wrapper.ts +42 -0
- package/src/agent-relay-transport.ts +187 -0
- package/src/authored-budget.ts +69 -0
- package/src/authored-flow-error.ts +10 -0
- package/src/authored-flow-executor.ts +170 -293
- package/src/authored-flow-loader.ts +60 -3
- package/src/authored-flow-operation.ts +38 -5
- package/src/authored-helper-effect.ts +143 -0
- package/src/authored-mcp.ts +166 -0
- package/src/authored-memory.ts +55 -0
- package/src/authored-plugin-effect.ts +152 -0
- package/src/authored-step-output.ts +107 -0
- package/src/authored-worker-step.ts +181 -0
- package/src/budget-preflight.ts +31 -0
- package/src/budget.ts +49 -0
- package/src/bundle-transport.ts +79 -0
- package/src/bundle-typescript.ts +98 -0
- package/src/bundle.ts +182 -0
- package/src/child-stop.ts +138 -0
- package/src/cli/add.ts +45 -0
- package/src/cli/build.ts +214 -0
- package/src/cli/bundle-preflight.ts +28 -0
- package/src/cli/check-helper-body.ts +14 -0
- package/src/cli/check-triggers.ts +41 -0
- package/src/cli/check-typescript.ts +63 -0
- package/src/cli/check.ts +132 -14
- package/src/cli/cloud-run.ts +47 -0
- package/src/cli/deploy.ts +52 -0
- package/src/cli/deterministic-failure.ts +69 -0
- package/src/cli/direct-run.ts +79 -7
- package/src/cli/hn-monitor.ts +2 -1
- package/src/cli/replay.ts +81 -0
- package/src/cli/reuse.ts +23 -0
- package/src/cli/run-digest.ts +24 -0
- package/src/cli/run.ts +104 -21
- package/src/cli/serve-webhook.ts +246 -0
- package/src/cli/tick-runner.ts +2 -1
- package/src/cli-adapter.ts +31 -74
- package/src/cli-watch.ts +174 -0
- package/src/cli.ts +396 -17
- package/src/cloud-http.ts +100 -0
- package/src/cloud-run.ts +137 -0
- package/src/compile.ts +102 -23
- package/src/create-flow.ts +82 -0
- package/src/daemon-connection.ts +32 -1
- package/src/demo-hn-monitor.ts +2 -1
- package/src/effect-channel.ts +97 -0
- package/src/failure-kinds.ts +38 -0
- package/src/gate-contract.ts +7 -0
- package/src/helper-preflight.ts +34 -0
- package/src/helper-storage.ts +23 -0
- package/src/helper-writeback.ts +83 -0
- package/src/index.ts +26 -0
- package/src/input-binding.ts +76 -0
- package/src/journal-client.ts +20 -7
- package/src/journal-offset.ts +60 -0
- package/src/journal-reader.ts +165 -0
- package/src/json-schema.ts +34 -18
- package/src/llm-worker.ts +82 -0
- package/src/local-agent.ts +30 -0
- package/src/mcp-client.ts +97 -0
- package/src/mcp-config.ts +42 -0
- package/src/mcp-stdio.ts +84 -0
- package/src/model-pricing.ts +32 -0
- package/src/mount-registry.ts +44 -0
- package/src/named-gate-lowering.ts +147 -0
- package/src/named-gates.ts +76 -0
- package/src/observer-link.ts +368 -0
- package/src/plugin-loader.ts +88 -0
- package/src/plugin-manifest.ts +76 -0
- package/src/preflight.ts +178 -4
- package/src/progress.ts +51 -0
- package/src/protocol.ts +12 -0
- package/src/pty-sidechannel.ts +92 -0
- package/src/scope-compiler.ts +105 -0
- package/src/slack-preflight.ts +25 -0
- package/src/slack-writeback.ts +63 -0
- package/src/spec.ts +112 -4
- package/src/step-dependencies.ts +3 -1
- package/src/step-fields.ts +5 -2
- package/src/trigger-executor.ts +34 -0
- package/src/validate.ts +47 -6
- package/src/webhook-rate-limit.ts +69 -0
- package/src/webhook-signature.ts +74 -0
- package/src/worker-cli.ts +156 -11
- package/src/worker-input.ts +15 -0
- package/src/worker-lease.ts +85 -0
- package/src/worker-spend.ts +23 -0
- package/src/worker-usage.ts +48 -0
- package/src/worker.ts +27 -9
- package/src/wrapper-session.ts +50 -15
- package/src/yaml-helper-effect.ts +78 -0
- package/src/yaml-helpers.ts +131 -0
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { slackClient } from '@relayfile/relay-helpers';
|
|
2
|
+
import type { RelayTransport } from '@relayfile/relay-helpers/transport';
|
|
3
|
+
import type { SlackHelper } from '@relayflows/surface';
|
|
4
|
+
import { helperTransport } from './helper-writeback.js';
|
|
5
|
+
export { atomicJson, receiptPath, readHelperReceipt as readSlackReceipt } from './helper-storage.js';
|
|
6
|
+
|
|
7
|
+
export type SlackCall =
|
|
8
|
+
| { type: 'effect'; provider: 'slack'; verb: 'post'; params: { channel: string; text: Parameters<SlackHelper['post']>[1]; opts?: Parameters<SlackHelper['post']>[2] } }
|
|
9
|
+
| { type: 'effect'; provider: 'slack'; verb: 'dm'; params: { user: string; text: string } }
|
|
10
|
+
| { type: 'effect'; provider: 'slack'; verb: 'reply'; params: { channel: string; threadTs: string; text: string } }
|
|
11
|
+
| { type: 'effect'; provider: 'slack'; verb: 'react'; params: { channel: string; messageTs: string; emoji: string } };
|
|
12
|
+
|
|
13
|
+
interface SlackPostBodyExtras { text?: string; dropText: boolean; blocks?: unknown; attachments?: unknown }
|
|
14
|
+
|
|
15
|
+
// Slice Y widened SlackPostMessage to carry blocks/attachments; the transport
|
|
16
|
+
// body is otherwise built by slackClient, which only sees the flattened text.
|
|
17
|
+
// Reconstruct the intended body by projecting from the original call.params.
|
|
18
|
+
function slackPostBodyExtras(params: Extract<SlackCall, { verb: 'post' }>['params']): SlackPostBodyExtras {
|
|
19
|
+
const extras: SlackPostBodyExtras = { dropText: false };
|
|
20
|
+
const t = params.text;
|
|
21
|
+
const opts = params.opts as { blocks?: unknown; attachments?: unknown } | undefined;
|
|
22
|
+
if (typeof t === 'object' && t !== null) {
|
|
23
|
+
if ('text' in t && typeof t.text === 'string') extras.text = t.text;
|
|
24
|
+
else extras.dropText = true;
|
|
25
|
+
if ('blocks' in t && t.blocks !== undefined) extras.blocks = t.blocks;
|
|
26
|
+
if ('attachments' in t && t.attachments !== undefined) extras.attachments = t.attachments;
|
|
27
|
+
}
|
|
28
|
+
if (opts?.blocks !== undefined) extras.blocks = opts.blocks;
|
|
29
|
+
if (opts?.attachments !== undefined) extras.attachments = opts.attachments;
|
|
30
|
+
return extras;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function wrapSlackPostTransport(transport: RelayTransport, extras: SlackPostBodyExtras): RelayTransport {
|
|
34
|
+
return {
|
|
35
|
+
read: transport.read.bind(transport),
|
|
36
|
+
list: transport.list.bind(transport),
|
|
37
|
+
async write(request) {
|
|
38
|
+
const body = { ...request.body as Record<string, unknown> };
|
|
39
|
+
if (extras.dropText) delete body.text;
|
|
40
|
+
else if (extras.text !== undefined) body.text = extras.text;
|
|
41
|
+
if (extras.blocks !== undefined) body.blocks = extras.blocks;
|
|
42
|
+
if (extras.attachments !== undefined) body.attachments = extras.attachments;
|
|
43
|
+
return transport.write({ ...request, body });
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export async function slackWriteback(
|
|
49
|
+
call: SlackCall, dataDir: string, runId: string, stepId: string, signal: AbortSignal,
|
|
50
|
+
): Promise<unknown> {
|
|
51
|
+
const { transport, deliveredRef } = helperTransport(call, dataDir, runId, stepId, signal);
|
|
52
|
+
const effectiveTransport = call.verb === 'post'
|
|
53
|
+
? wrapSlackPostTransport(transport, slackPostBodyExtras(call.params))
|
|
54
|
+
: transport;
|
|
55
|
+
const client = slackClient({ transport: effectiveTransport });
|
|
56
|
+
switch (call.verb) {
|
|
57
|
+
case 'post': return client.post(call.params.channel,
|
|
58
|
+
typeof call.params.text === 'string' ? call.params.text : call.params.text.text ?? '', call.params.opts);
|
|
59
|
+
case 'dm': return client.dm(call.params.user, call.params.text);
|
|
60
|
+
case 'reply': return { ...await client.reply(call.params.channel, call.params.threadTs, call.params.text), ref: deliveredRef() };
|
|
61
|
+
case 'react': await client.react(call.params.channel, call.params.messageTs, call.params.emoji); return null;
|
|
62
|
+
}
|
|
63
|
+
}
|
package/src/spec.ts
CHANGED
|
@@ -15,11 +15,24 @@ export type { JsonOutputSchema } from './output-schema.js';
|
|
|
15
15
|
/** The three rungs of the ladder (RFC §1; AGENTS.md rule 7). */
|
|
16
16
|
export type StepType = 'deterministic' | 'llm' | 'agent';
|
|
17
17
|
|
|
18
|
+
/** Project-owned MCP connections. env contains names, never secret values. */
|
|
19
|
+
export type McpServerConfig =
|
|
20
|
+
| { command: string; args?: string[]; env?: string[] }
|
|
21
|
+
| { url: string; headers?: Record<string, string> };
|
|
22
|
+
|
|
23
|
+
export interface FlowsJson {
|
|
24
|
+
deploy?: { bucket: string };
|
|
25
|
+
cli?: string;
|
|
26
|
+
executors?: string[];
|
|
27
|
+
models?: string[];
|
|
28
|
+
mcp?: Record<string, McpServerConfig>;
|
|
29
|
+
}
|
|
30
|
+
|
|
18
31
|
/**
|
|
19
32
|
* Verification is control flow, not decoration (kernel DESIGN.md §3).
|
|
20
33
|
* v0 gates are deterministic so verification is kernel-side and replayable.
|
|
21
34
|
*/
|
|
22
|
-
export type VerificationGateType = '
|
|
35
|
+
export type VerificationGateType = VerificationSpec['type'];
|
|
23
36
|
|
|
24
37
|
/**
|
|
25
38
|
* `exit_code == 0` — the implicit gate for deterministic steps. v0 judges
|
|
@@ -43,8 +56,35 @@ export interface JsonSchemaGate {
|
|
|
43
56
|
schema: boolean | Record<string, unknown>;
|
|
44
57
|
}
|
|
45
58
|
|
|
46
|
-
export
|
|
47
|
-
|
|
59
|
+
export interface ReferencesInputGate {
|
|
60
|
+
type: 'references_input';
|
|
61
|
+
input_key: string;
|
|
62
|
+
in_output_at?: Array<string | number>;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export interface SubprocessGate {
|
|
66
|
+
type: 'subprocess_gate';
|
|
67
|
+
command: string;
|
|
68
|
+
from_output?: Array<string | number>;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export interface WordCountBoundsGate {
|
|
72
|
+
type: 'word_count_bounds';
|
|
73
|
+
min?: number;
|
|
74
|
+
max?: number;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export interface RegexMatchGate {
|
|
78
|
+
type: 'regex_match';
|
|
79
|
+
pattern: string;
|
|
80
|
+
in_output_at?: Array<string | number>;
|
|
81
|
+
/** Only i, m, and s; evaluated by a non-backtracking RE2 engine. */
|
|
82
|
+
flags?: string;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export type NamedDataGate = ReferencesInputGate | SubprocessGate | WordCountBoundsGate | RegexMatchGate;
|
|
86
|
+
export type OutputVerificationSpec = OutputContainsGate | JsonSchemaGate | NamedDataGate;
|
|
87
|
+
export type VerificationSpec = ExitCodeGate | OutputVerificationSpec;
|
|
48
88
|
|
|
49
89
|
/**
|
|
50
90
|
* Agent-step recovery modes (RFC Appendix A rule 4). Default is `reset`.
|
|
@@ -88,6 +128,11 @@ export interface PermissionsSpec {
|
|
|
88
128
|
* are integers.
|
|
89
129
|
*/
|
|
90
130
|
export interface BudgetSpec {
|
|
131
|
+
/** Set when normalizing the surface budget header. Legacy explicit envelopes accept worker-supplied prices. */
|
|
132
|
+
pricing?: 'frozen';
|
|
133
|
+
maxTokens?: number;
|
|
134
|
+
maxWallclockMs?: number;
|
|
135
|
+
window?: 'day';
|
|
91
136
|
maxTokensIn?: number;
|
|
92
137
|
maxTokensOut?: number;
|
|
93
138
|
/** Decimal string, e.g. "1.50". */
|
|
@@ -124,6 +169,8 @@ export interface KernelPlacementRequirements extends Omit<PlacementRequirements,
|
|
|
124
169
|
|
|
125
170
|
/** Fields shared by every step on the ladder. */
|
|
126
171
|
export interface BaseStepSpec {
|
|
172
|
+
/** Named values selected from earlier steps' declared, verified outputs. */
|
|
173
|
+
input?: Record<string, OutputBinding>;
|
|
127
174
|
requirements?: PlacementRequirements;
|
|
128
175
|
memory?: MemorySpec;
|
|
129
176
|
/** Stable step identity; journaled as `step_id` and hashed into the idempotency key. */
|
|
@@ -135,6 +182,12 @@ export interface BaseStepSpec {
|
|
|
135
182
|
maxIterations?: number;
|
|
136
183
|
}
|
|
137
184
|
|
|
185
|
+
export interface OutputBinding {
|
|
186
|
+
step: string;
|
|
187
|
+
/** Object keys or array indices; omit to select the whole output. */
|
|
188
|
+
path?: Array<string | number>;
|
|
189
|
+
}
|
|
190
|
+
|
|
138
191
|
/**
|
|
139
192
|
* Rung 1 — a pure script. Executed by the `relayflowd` binary: spawn command,
|
|
140
193
|
* capture stdout/exit code. Output = `{exit_code, stdout_tail}`. Gate-1
|
|
@@ -145,6 +198,8 @@ export interface DeterministicStepSpec extends BaseStepSpec {
|
|
|
145
198
|
command: string;
|
|
146
199
|
/** Wall-clock command timeout; worker-backed verbs own their dispatch timeout. */
|
|
147
200
|
timeoutMs?: number;
|
|
201
|
+
/** Per-invocation deterministic lease in milliseconds (maximum 15 minutes). */
|
|
202
|
+
lease_ms?: number;
|
|
148
203
|
/** Omit to get the implicit `exit_code` gate. */
|
|
149
204
|
verification?: VerificationSpec;
|
|
150
205
|
}
|
|
@@ -192,6 +247,14 @@ export interface AgentStepSpec extends BaseStepSpec {
|
|
|
192
247
|
surfaces?: AgentSurfaces;
|
|
193
248
|
recoveryMode?: RecoveryMode;
|
|
194
249
|
permissions?: PermissionsSpec;
|
|
250
|
+
/** Working directory for the CLI subprocess; defaults to the flow-runner's cwd. */
|
|
251
|
+
cwd?: string;
|
|
252
|
+
/**
|
|
253
|
+
* Dispatch transport (flows#385). `'direct'` (default) spawns the CLI as
|
|
254
|
+
* a local subprocess. `'relay'` posts to agent-relay so the agent
|
|
255
|
+
* registers as a first-class workspace participant.
|
|
256
|
+
*/
|
|
257
|
+
transport?: 'direct' | 'relay';
|
|
195
258
|
/**
|
|
196
259
|
* Structured-output authoring sugar. A successful CLI JSON object is the parsed
|
|
197
260
|
* value; the kernel persists it only after `json_schema` verification.
|
|
@@ -201,6 +264,33 @@ export interface AgentStepSpec extends BaseStepSpec {
|
|
|
201
264
|
|
|
202
265
|
export type StepSpec = DeterministicStepSpec | LlmStepSpec | AgentStepSpec;
|
|
203
266
|
|
|
267
|
+
/** YAML argument maps use the same pinned client types as the TS helpers. */
|
|
268
|
+
export interface YamlHelperParams {
|
|
269
|
+
slack: { [V in import('./slack-writeback.js').SlackCall['verb']]:
|
|
270
|
+
Extract<import('./slack-writeback.js').SlackCall, { verb: V }>['params'] };
|
|
271
|
+
github: {
|
|
272
|
+
comment: { target: Parameters<import('@relayfile/relay-helpers').GithubClient['comment']>[0]; body: string };
|
|
273
|
+
createIssue: Parameters<import('@relayfile/relay-helpers').GithubClient['createIssue']>[0];
|
|
274
|
+
createPullRequest: Parameters<import('@relayfile/relay-helpers').GithubClient['createPullRequest']>[0];
|
|
275
|
+
closePullRequest: Parameters<import('@relayfile/relay-helpers').GithubClient['closePullRequest']>[0];
|
|
276
|
+
};
|
|
277
|
+
linear: {
|
|
278
|
+
comment: { issueId: string; body: string };
|
|
279
|
+
createIssue: Parameters<import('@relayfile/relay-helpers').LinearClient['createIssue']>[0];
|
|
280
|
+
updateIssue: { issueId: string; args: Parameters<import('@relayfile/relay-helpers').LinearClient['updateIssue']>[1] };
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
type OneKey<T> = { [K in keyof T]: Pick<T, K> & Partial<Record<Exclude<keyof T, K>, never>> }[keyof T];
|
|
285
|
+
|
|
286
|
+
/** Helper sugar is removed before validation of the three kernel step types. */
|
|
287
|
+
export type YamlHelperStepSpec = Pick<BaseStepSpec, 'id' | 'dependsOn' | 'maxIterations'> & {
|
|
288
|
+
verification?: OutputVerificationSpec;
|
|
289
|
+
output?: JsonOutputSchema;
|
|
290
|
+
} & OneKey<{ [P in keyof YamlHelperParams]: OneKey<YamlHelperParams[P]> }>;
|
|
291
|
+
|
|
292
|
+
export type YamlFlowSpec = Omit<FlowSpec, 'steps'> & { steps: Array<StepSpec | YamlHelperStepSpec> };
|
|
293
|
+
|
|
204
294
|
/**
|
|
205
295
|
* Reusable authoring declaration for an agent CLI/model pair. Both fields are
|
|
206
296
|
* required so selecting a named agent can never inherit a host model. The
|
|
@@ -268,7 +358,11 @@ export interface FlowSpec {
|
|
|
268
358
|
/** Declarations checked by preflight; gate 1 never dispatches them. */
|
|
269
359
|
triggers?: TriggerSpec[];
|
|
270
360
|
steps: StepSpec[];
|
|
271
|
-
budget?: BudgetSpec;
|
|
361
|
+
budget?: BudgetSpec | import('./budget.js').HeaderBudget;
|
|
362
|
+
/** Path-scoped workspace grants — "mount/path: readonly|readwrite|append". Compiled by preflight. */
|
|
363
|
+
workspace?: string | readonly string[];
|
|
364
|
+
/** Path-scoped tool grants; `fs` mirrors workspace for shell/deterministic scope. */
|
|
365
|
+
tools?: { fs?: string | readonly string[] };
|
|
272
366
|
}
|
|
273
367
|
|
|
274
368
|
/** Current spec schema version emitted by this SDK. */
|
|
@@ -302,6 +396,7 @@ export interface KernelVerificationSpec {
|
|
|
302
396
|
}
|
|
303
397
|
|
|
304
398
|
export interface KernelStepCommon {
|
|
399
|
+
input?: Record<string, OutputBinding>;
|
|
305
400
|
requirements?: KernelPlacementRequirements;
|
|
306
401
|
memory?: KernelMemorySpec;
|
|
307
402
|
id: string;
|
|
@@ -315,6 +410,7 @@ export interface KernelDeterministicStep extends KernelStepCommon {
|
|
|
315
410
|
type: 'deterministic';
|
|
316
411
|
command: string;
|
|
317
412
|
timeout_ms?: number;
|
|
413
|
+
lease_ms?: number;
|
|
318
414
|
}
|
|
319
415
|
|
|
320
416
|
export interface KernelLlmStep extends KernelStepCommon {
|
|
@@ -344,11 +440,23 @@ export interface KernelAgentStep extends KernelStepCommon {
|
|
|
344
440
|
recovery_mode: RecoveryMode;
|
|
345
441
|
surfaces?: KernelAgentSurfaces;
|
|
346
442
|
permissions?: KernelPermissionsSpec;
|
|
443
|
+
/** Working directory for the CLI subprocess; kernel passes through untouched. */
|
|
444
|
+
cwd?: string;
|
|
445
|
+
/**
|
|
446
|
+
* Dispatch transport (flows#385). Kernel passes through untouched;
|
|
447
|
+
* the SDK worker selects between direct-spawn and agent-relay routing.
|
|
448
|
+
*/
|
|
449
|
+
transport?: 'direct' | 'relay';
|
|
347
450
|
}
|
|
348
451
|
|
|
349
452
|
export type KernelStepSpec = KernelDeterministicStep | KernelLlmStep | KernelAgentStep;
|
|
350
453
|
|
|
351
454
|
export interface KernelBudgetSpec {
|
|
455
|
+
pricing?: 'frozen';
|
|
456
|
+
prior_spend?: { tokens_in: number; tokens_out: number; dollars: string; wallclock_ms: number; day?: number };
|
|
457
|
+
max_tokens?: number;
|
|
458
|
+
max_wallclock_ms?: number;
|
|
459
|
+
window?: 'day';
|
|
352
460
|
max_tokens_in?: number;
|
|
353
461
|
max_tokens_out?: number;
|
|
354
462
|
max_dollars?: string;
|
package/src/step-dependencies.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { bindingDependencies } from './input-binding.js';
|
|
2
|
+
|
|
1
3
|
/** Return author-facing dependency errors without assuming parsed step shapes. */
|
|
2
4
|
const MAX_REPORTED_CYCLE_PATH_IDS = 16;
|
|
3
5
|
|
|
@@ -21,7 +23,7 @@ export function stepDependencyErrors(
|
|
|
21
23
|
}
|
|
22
24
|
|
|
23
25
|
const id = value['id'];
|
|
24
|
-
const dependencies = (rawDependencies ?? []) as string[];
|
|
26
|
+
const dependencies = [...(rawDependencies ?? []) as string[], ...bindingDependencies(value['input'])];
|
|
25
27
|
for (const dependency of dependencies) {
|
|
26
28
|
if (!knownIds.has(dependency)) {
|
|
27
29
|
errors.push(`spec.steps: step "${id}" dependsOn unknown step "${dependency}"`);
|
package/src/step-fields.ts
CHANGED
|
@@ -10,6 +10,8 @@ export const FLOW_FIELDS = [
|
|
|
10
10
|
'triggers',
|
|
11
11
|
'steps',
|
|
12
12
|
'budget',
|
|
13
|
+
'workspace',
|
|
14
|
+
'tools',
|
|
13
15
|
] as const;
|
|
14
16
|
|
|
15
17
|
/** Closed named-agent declaration schema. */
|
|
@@ -20,6 +22,7 @@ export const STEP_COMMON_FIELDS = [
|
|
|
20
22
|
'id',
|
|
21
23
|
'type',
|
|
22
24
|
'dependsOn',
|
|
25
|
+
'input',
|
|
23
26
|
'verification',
|
|
24
27
|
'maxIterations',
|
|
25
28
|
'memory',
|
|
@@ -32,7 +35,7 @@ export const STEP_COMMON_FIELDS = [
|
|
|
32
35
|
* per-verb boundary through a second, drifting allowlist.
|
|
33
36
|
*/
|
|
34
37
|
export const STEP_FIELDS_BY_TYPE = {
|
|
35
|
-
deterministic: ['command', 'timeoutMs'],
|
|
38
|
+
deterministic: ['command', 'timeoutMs', 'lease_ms'],
|
|
36
39
|
llm: ['prompt', 'model', 'cli', 'output'],
|
|
37
|
-
agent: ['instruction', 'agent', 'cli', 'model', 'surfaces', 'recoveryMode', 'permissions', 'output'],
|
|
40
|
+
agent: ['instruction', 'agent', 'cli', 'model', 'cwd', 'transport', 'surfaces', 'recoveryMode', 'permissions', 'output'],
|
|
38
41
|
} as const satisfies Record<StepType, readonly string[]>;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { providerEventTypes, webhook, type TriggerSource, type WebhookFilter } from '@relayflows/surface';
|
|
2
|
+
import type { TriggerSpec } from './spec.js';
|
|
3
|
+
|
|
4
|
+
/** Lower a surface subscription to the existing inbox executor contract. */
|
|
5
|
+
export function webhookTriggerSpec(id: string, source: TriggerSource): TriggerSpec {
|
|
6
|
+
if (source.kind !== 'webhook') throw new TypeError('unsupported trigger kind');
|
|
7
|
+
const trigger = webhook(source.name, source.filter);
|
|
8
|
+
return {
|
|
9
|
+
id,
|
|
10
|
+
executor: trigger.name,
|
|
11
|
+
eventType: trigger.name,
|
|
12
|
+
...(trigger.filter === undefined ? {} : { pattern: trigger.filter }),
|
|
13
|
+
// The inbox watcher supplies the durable file ID as the event key.
|
|
14
|
+
dedupeKeyTemplate: '{{event.type}}',
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** Provider ingress uses the path as authority; it never trusts a body to reroute. */
|
|
19
|
+
export function providerInboxEvent(provider: string, value: unknown): WebhookFilter {
|
|
20
|
+
if (!Object.hasOwn(providerEventTypes, provider)) throw new TypeError(`unknown provider: ${provider}`);
|
|
21
|
+
if (value === null || typeof value !== 'object' || Array.isArray(value)) {
|
|
22
|
+
throw new TypeError('provider event must be an object');
|
|
23
|
+
}
|
|
24
|
+
const event = value as Record<string, unknown>;
|
|
25
|
+
const types: readonly string[] = providerEventTypes[provider as keyof typeof providerEventTypes];
|
|
26
|
+
if (typeof event.type !== 'string' || !types.includes(event.type)) {
|
|
27
|
+
throw new TypeError(`unknown event type for ${provider}`);
|
|
28
|
+
}
|
|
29
|
+
if (event.provider !== undefined && event.provider !== provider) throw new TypeError('provider does not match inbox');
|
|
30
|
+
if (event.payload === null || typeof event.payload !== 'object' || Array.isArray(event.payload)) {
|
|
31
|
+
throw new TypeError('provider event payload must be an object');
|
|
32
|
+
}
|
|
33
|
+
return webhook(provider, { ...event, provider } as WebhookFilter).filter!;
|
|
34
|
+
}
|
package/src/validate.ts
CHANGED
|
@@ -21,6 +21,8 @@ import { validateOutputDeclaration } from './output-schema.js';
|
|
|
21
21
|
import { modelNameError } from './model-name.js';
|
|
22
22
|
import { unknownKeyErrors } from './unknown-keys.js';
|
|
23
23
|
import { stepDependencyErrors } from './step-dependencies.js';
|
|
24
|
+
import { inputBindingErrors } from './input-binding.js';
|
|
25
|
+
import { NAMED_GATE_KEYS, namedGateErrors } from './named-gates.js';
|
|
24
26
|
import {
|
|
25
27
|
AGENT_DECLARATION_FIELDS,
|
|
26
28
|
FLOW_FIELDS,
|
|
@@ -68,8 +70,9 @@ function isCanonicalPathSurface(value: unknown): value is string {
|
|
|
68
70
|
// unknown keys (AGENTS.md rule 4; RFC covenant 2): a typo'd key like
|
|
69
71
|
// `depends_on` must be an error naming the nearest valid key, never a
|
|
70
72
|
// silently discarded field — silently dropping `dependsOn` loses ordering.
|
|
71
|
-
const BUDGET_KEYS = ['maxTokensIn', 'maxTokensOut', 'maxDollars'] as const;
|
|
73
|
+
const BUDGET_KEYS = ['maxTokensIn', 'maxTokensOut', 'maxDollars', 'maxTokens', 'maxWallclockMs', 'window', 'pricing'] as const;
|
|
72
74
|
const VERIFICATION_KEYS: Record<string, readonly string[]> = {
|
|
75
|
+
...NAMED_GATE_KEYS,
|
|
73
76
|
exit_code: ['type', 'expect'],
|
|
74
77
|
output_contains: ['type', 'value'],
|
|
75
78
|
json_schema: ['type', 'schema'],
|
|
@@ -163,6 +166,22 @@ class Validator {
|
|
|
163
166
|
|
|
164
167
|
if (s['budget'] !== undefined) this.validateBudget(s['budget']);
|
|
165
168
|
|
|
169
|
+
if (s['workspace'] !== undefined) this.validateScopeGrants(s['workspace'], 'spec.workspace');
|
|
170
|
+
|
|
171
|
+
if (s['tools'] !== undefined) {
|
|
172
|
+
if (!isObject(s['tools'])) {
|
|
173
|
+
this.fail('spec.tools: expected an object');
|
|
174
|
+
} else {
|
|
175
|
+
const tools = s['tools'] as Record<string, unknown>;
|
|
176
|
+
for (const key of Object.keys(tools)) {
|
|
177
|
+
if (key !== 'fs' && key !== 'mcp') {
|
|
178
|
+
this.fail(`spec.tools: unknown key "${key}" (expected fs or mcp)`);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
if (tools['fs'] !== undefined) this.validateScopeGrants(tools['fs'], 'spec.tools.fs');
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
166
185
|
if (!Array.isArray(s['steps']) || s['steps'].length === 0) {
|
|
167
186
|
this.fail('spec.steps: expected a non-empty array');
|
|
168
187
|
return this.result();
|
|
@@ -175,6 +194,7 @@ class Validator {
|
|
|
175
194
|
|
|
176
195
|
// Dependents must reference real step ids and form a DAG (no cycles).
|
|
177
196
|
for (const error of stepDependencyErrors(steps, this.ids)) this.fail(error);
|
|
197
|
+
for (const error of inputBindingErrors(steps)) this.fail(error);
|
|
178
198
|
return this.result();
|
|
179
199
|
}
|
|
180
200
|
|
|
@@ -203,13 +223,28 @@ class Validator {
|
|
|
203
223
|
}
|
|
204
224
|
}
|
|
205
225
|
|
|
226
|
+
private validateScopeGrants(value: unknown, at: string): void {
|
|
227
|
+
const grants = Array.isArray(value) ? value : [value];
|
|
228
|
+
for (const grant of grants) {
|
|
229
|
+
if (typeof grant !== 'string' || grant.length === 0) {
|
|
230
|
+
this.fail(`${at}: expected a scope-grant string like "mount/path: readonly" (or an array of them)`);
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
206
236
|
private validateBudget(b: unknown, at = 'spec.budget'): void {
|
|
207
237
|
if (!isObject(b)) {
|
|
208
238
|
this.fail(`${at}: expected an object`);
|
|
209
239
|
return;
|
|
210
240
|
}
|
|
211
|
-
this.checkKeys(b, BUDGET_KEYS, at);
|
|
241
|
+
this.checkKeys(b, at === 'spec.budget' ? BUDGET_KEYS : ['maxTokensIn', 'maxTokensOut', 'maxDollars'], at);
|
|
212
242
|
const budget = b as BudgetSpec;
|
|
243
|
+
for (const key of ['maxTokens', 'maxWallclockMs'] as const) {
|
|
244
|
+
if (budget[key] !== undefined && (!Number.isSafeInteger(budget[key]) || budget[key]! < 0)) this.fail(`${at}.${key}: expected a safe non-negative integer`);
|
|
245
|
+
}
|
|
246
|
+
if (budget.pricing !== undefined && budget.pricing !== 'frozen') this.fail(`${at}.pricing: expected frozen`);
|
|
247
|
+
if (budget.window !== undefined && budget.window !== 'day') this.fail(`${at}.window: expected day`);
|
|
213
248
|
if (
|
|
214
249
|
budget.maxTokensIn !== undefined &&
|
|
215
250
|
!isNonNegInt(budget.maxTokensIn)
|
|
@@ -356,7 +391,7 @@ class Validator {
|
|
|
356
391
|
}
|
|
357
392
|
|
|
358
393
|
if (st['verification'] !== undefined) {
|
|
359
|
-
this.validateVerification(st['verification'], `${at}.verification`, type);
|
|
394
|
+
this.validateVerification(st['verification'], `${at}.verification`, type, st['input']);
|
|
360
395
|
}
|
|
361
396
|
|
|
362
397
|
if (st['maxIterations'] !== undefined && !isPosInt(st['maxIterations'])) {
|
|
@@ -374,13 +409,14 @@ class Validator {
|
|
|
374
409
|
}
|
|
375
410
|
}
|
|
376
411
|
|
|
377
|
-
private validateVerification(v: unknown, at: string, stepType: StepType): void {
|
|
412
|
+
private validateVerification(v: unknown, at: string, stepType: StepType, input: unknown): void {
|
|
378
413
|
if (!isObject(v)) {
|
|
379
414
|
this.fail(`${at}: expected an object`);
|
|
380
415
|
return;
|
|
381
416
|
}
|
|
382
417
|
const gate = v as unknown as VerificationSpec & { expect?: unknown };
|
|
383
|
-
const gateKeys = typeof gate.type === 'string'
|
|
418
|
+
const gateKeys = typeof gate.type === 'string' && Object.hasOwn(VERIFICATION_KEYS, gate.type)
|
|
419
|
+
? VERIFICATION_KEYS[gate.type] : undefined;
|
|
384
420
|
if (gateKeys !== undefined) {
|
|
385
421
|
this.checkKeys(v, gateKeys, at);
|
|
386
422
|
}
|
|
@@ -407,8 +443,10 @@ class Validator {
|
|
|
407
443
|
} catch (error) {
|
|
408
444
|
this.fail(error instanceof Error ? error.message : `${at}.schema: expected JSON-compatible data`);
|
|
409
445
|
}
|
|
446
|
+
} else if (Object.hasOwn(NAMED_GATE_KEYS, gate.type)) {
|
|
447
|
+
for (const error of namedGateErrors(v, input, at)) this.fail(error);
|
|
410
448
|
} else {
|
|
411
|
-
this.fail(`${at}.type: expected exit_code | output_contains | json_schema`);
|
|
449
|
+
this.fail(`${at}.type: unknown_gate_kind: expected exit_code | output_contains | json_schema | references_input | subprocess_gate | word_count_bounds | regex_match`);
|
|
412
450
|
}
|
|
413
451
|
}
|
|
414
452
|
|
|
@@ -416,6 +454,9 @@ class Validator {
|
|
|
416
454
|
if (!isNonEmptyString(st.command)) {
|
|
417
455
|
this.fail(`${at}.command: expected a non-empty string`);
|
|
418
456
|
}
|
|
457
|
+
if (st.lease_ms !== undefined && !isPosInt(st.lease_ms)) {
|
|
458
|
+
this.fail(`${at}.lease_ms: expected a positive integer`);
|
|
459
|
+
}
|
|
419
460
|
if (st.timeoutMs !== undefined && !isPosInt(st.timeoutMs)) {
|
|
420
461
|
this.fail(`${at}.timeoutMs: expected a positive integer`);
|
|
421
462
|
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-process token-bucket rate limiter for the local webhook receiver (#304).
|
|
3
|
+
*
|
|
4
|
+
* Slice E's receiver binds loopback and is documented as development-only, so
|
|
5
|
+
* a single-process in-memory bucket is sufficient. Cluster deployments must
|
|
6
|
+
* layer a shared store in a follow-up slice; the taxonomy stays the same
|
|
7
|
+
* (`webhook_rate_limited`) so client behavior does not change.
|
|
8
|
+
*/
|
|
9
|
+
export interface RateLimitConfig {
|
|
10
|
+
/** Tokens replenished per second. */
|
|
11
|
+
readonly ratePerSecond: number;
|
|
12
|
+
/** Maximum burst capacity. */
|
|
13
|
+
readonly burst: number;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface RateLimitDecision {
|
|
17
|
+
readonly allowed: boolean;
|
|
18
|
+
readonly retryAfterMs: number;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
interface Bucket {
|
|
22
|
+
tokens: number;
|
|
23
|
+
refilledAtMs: number;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export class TokenBucketLimiter {
|
|
27
|
+
private readonly buckets = new Map<string, Bucket>();
|
|
28
|
+
|
|
29
|
+
constructor(
|
|
30
|
+
private readonly config: RateLimitConfig,
|
|
31
|
+
private readonly clock: () => number = Date.now,
|
|
32
|
+
) {
|
|
33
|
+
if (config.burst <= 0 || config.ratePerSecond < 0) {
|
|
34
|
+
throw new Error('rate limiter: burst > 0 and ratePerSecond >= 0 required');
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
consume(key: string, cost = 1): RateLimitDecision {
|
|
39
|
+
const now = this.clock();
|
|
40
|
+
const bucket = this.buckets.get(key) ?? { tokens: this.config.burst, refilledAtMs: now };
|
|
41
|
+
const elapsedSeconds = Math.max(0, (now - bucket.refilledAtMs) / 1000);
|
|
42
|
+
bucket.tokens = Math.min(this.config.burst, bucket.tokens + elapsedSeconds * this.config.ratePerSecond);
|
|
43
|
+
bucket.refilledAtMs = now;
|
|
44
|
+
if (bucket.tokens >= cost) {
|
|
45
|
+
bucket.tokens -= cost;
|
|
46
|
+
this.buckets.set(key, bucket);
|
|
47
|
+
return { allowed: true, retryAfterMs: 0 };
|
|
48
|
+
}
|
|
49
|
+
this.buckets.set(key, bucket);
|
|
50
|
+
const missingTokens = cost - bucket.tokens;
|
|
51
|
+
const retryAfterMs = this.config.ratePerSecond > 0
|
|
52
|
+
? Math.ceil((missingTokens / this.config.ratePerSecond) * 1000)
|
|
53
|
+
: Number.POSITIVE_INFINITY;
|
|
54
|
+
return { allowed: false, retryAfterMs };
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Test/inspection helper — production code should only call {@link consume}. */
|
|
58
|
+
size(): number {
|
|
59
|
+
return this.buckets.size;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function keyFor(
|
|
64
|
+
provider: string | undefined,
|
|
65
|
+
name: string,
|
|
66
|
+
sourceAddress: string,
|
|
67
|
+
): string {
|
|
68
|
+
return `${provider ?? 'raw'}:${name}:${sourceAddress}`;
|
|
69
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { createHmac, timingSafeEqual } from 'node:crypto';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Per-provider HMAC signature schemes for webhook payload authentication (#304).
|
|
5
|
+
*
|
|
6
|
+
* The receiver only invokes verification when a secret is configured for the
|
|
7
|
+
* provider (via `env[WEBHOOK_SECRET_<PROVIDER>]`). When no secret is set the
|
|
8
|
+
* receiver runs unsigned, matching the initial loopback-only development mode
|
|
9
|
+
* documented in #301. Callers deploying to public ingress MUST set the secret.
|
|
10
|
+
*/
|
|
11
|
+
export type SignatureScheme = {
|
|
12
|
+
header: string;
|
|
13
|
+
compute(rawBody: Buffer, secret: string): string;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const HEX = /^[0-9a-f]+$/i;
|
|
17
|
+
|
|
18
|
+
const github: SignatureScheme = {
|
|
19
|
+
header: 'x-hub-signature-256',
|
|
20
|
+
compute(rawBody, secret) {
|
|
21
|
+
return 'sha256=' + createHmac('sha256', secret).update(rawBody).digest('hex');
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const slack: SignatureScheme = {
|
|
26
|
+
header: 'x-slack-signature',
|
|
27
|
+
compute(rawBody, secret) {
|
|
28
|
+
// Slack's v0 scheme is HMAC(SHA256, secret) over `v0:{ts}:{body}`; a
|
|
29
|
+
// deployment that wants Slack signature verification must set the secret
|
|
30
|
+
// to include the timestamp prefix or use provider-specific integration.
|
|
31
|
+
return 'v0=' + createHmac('sha256', secret).update(rawBody).digest('hex');
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const generic: SignatureScheme = {
|
|
36
|
+
header: 'x-flows-signature-256',
|
|
37
|
+
compute(rawBody, secret) {
|
|
38
|
+
return 'sha256=' + createHmac('sha256', secret).update(rawBody).digest('hex');
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export const SIGNATURE_SCHEMES: Record<string, SignatureScheme> = {
|
|
43
|
+
github,
|
|
44
|
+
slack,
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export function schemeFor(provider: string | undefined): SignatureScheme {
|
|
48
|
+
if (provider && Object.hasOwn(SIGNATURE_SCHEMES, provider)) {
|
|
49
|
+
return SIGNATURE_SCHEMES[provider]!;
|
|
50
|
+
}
|
|
51
|
+
return generic;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** Constant-time verification of a hex-encoded signature against the expected. */
|
|
55
|
+
export function verifySignature(
|
|
56
|
+
provider: string | undefined,
|
|
57
|
+
rawBody: Buffer,
|
|
58
|
+
headerValue: string | undefined,
|
|
59
|
+
secret: string,
|
|
60
|
+
): { ok: true } | { ok: false; reason: 'missing' | 'malformed' | 'mismatch' } {
|
|
61
|
+
const scheme = schemeFor(provider);
|
|
62
|
+
const supplied = headerValue?.trim();
|
|
63
|
+
if (!supplied) return { ok: false, reason: 'missing' };
|
|
64
|
+
const expected = scheme.compute(rawBody, secret);
|
|
65
|
+
if (supplied.length !== expected.length) return { ok: false, reason: 'malformed' };
|
|
66
|
+
const [suppliedHex, expectedHex] = [supplied.replace(/^[^=]+=/, ''), expected.replace(/^[^=]+=/, '')];
|
|
67
|
+
if (!HEX.test(suppliedHex) || suppliedHex.length !== expectedHex.length) {
|
|
68
|
+
return { ok: false, reason: 'malformed' };
|
|
69
|
+
}
|
|
70
|
+
const suppliedBytes = Buffer.from(suppliedHex, 'hex');
|
|
71
|
+
const expectedBytes = Buffer.from(expectedHex, 'hex');
|
|
72
|
+
if (suppliedBytes.length !== expectedBytes.length) return { ok: false, reason: 'malformed' };
|
|
73
|
+
return timingSafeEqual(suppliedBytes, expectedBytes) ? { ok: true } : { ok: false, reason: 'mismatch' };
|
|
74
|
+
}
|