@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
package/src/cloud-run.ts
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { RUN_COMPLETION_REASONS } from '@relayflows/surface';
|
|
2
|
+
import type { RunCompletionReason } from './protocol.js';
|
|
3
|
+
import { readFile } from 'node:fs/promises';
|
|
4
|
+
import { setTimeout } from 'node:timers/promises';
|
|
5
|
+
import { parse as parseYaml } from 'yaml';
|
|
6
|
+
import { canonicalize, specHash } from './canonical.js';
|
|
7
|
+
import { CompileError, compileSpec, kernelToAuthoring, toKernelSpec } from './compile.js';
|
|
8
|
+
import type { FlowSpec } from './spec.js';
|
|
9
|
+
import {
|
|
10
|
+
CloudFlowError, cloudConnection, cloudRequest, cloudRunId, isCloudRecord,
|
|
11
|
+
type CloudConnectionOptions,
|
|
12
|
+
} from './cloud-http.js';
|
|
13
|
+
|
|
14
|
+
export type CloudFlowSource = FlowSpec | { path: string };
|
|
15
|
+
export interface RunInCloudOptions extends CloudConnectionOptions {
|
|
16
|
+
/** Existing Cloud workspace to run in; provisioning/authorization remains server-side. */
|
|
17
|
+
workspaceId?: string;
|
|
18
|
+
}
|
|
19
|
+
export interface CloudRunReceipt {
|
|
20
|
+
runId: string;
|
|
21
|
+
status: 'pending' | 'running';
|
|
22
|
+
/** Local correlation hash of the compiled spec; NOT server attestation or a sealed bundle digest. */
|
|
23
|
+
specHash: string;
|
|
24
|
+
/** Authenticated run API resource; this is not a public sharing URL. */
|
|
25
|
+
apiUrl: string;
|
|
26
|
+
}
|
|
27
|
+
export type CloudRunState =
|
|
28
|
+
| { runId: string; status: 'pending' | 'running' }
|
|
29
|
+
| { runId: string; status: 'completed' | 'failed' | 'cancelled'; completionReason: RunCompletionReason };
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Submit a declarative flow to Cloud's pinned v2 runtime. Compilation only
|
|
33
|
+
* happens here; Cloud owns provisioning and the Rust engine owns execution.
|
|
34
|
+
* Returns acceptance, not completion. No local daemon, CLI probe, or node up.
|
|
35
|
+
*/
|
|
36
|
+
export async function runInCloud(
|
|
37
|
+
flow: CloudFlowSource,
|
|
38
|
+
options: RunInCloudOptions = {},
|
|
39
|
+
): Promise<CloudRunReceipt> {
|
|
40
|
+
const { baseUrl } = cloudConnection(options);
|
|
41
|
+
let spec: FlowSpec;
|
|
42
|
+
try {
|
|
43
|
+
options.signal?.throwIfAborted();
|
|
44
|
+
if ('path' in flow) {
|
|
45
|
+
if (!/\.(?:ya?ml|json)$/iu.test(flow.path)) {
|
|
46
|
+
throw new CloudFlowError('unsupported_source',
|
|
47
|
+
'Cloud v2 currently accepts declarative YAML or JSON specs; authored TypeScript flows are not supported by the hosted runtime.');
|
|
48
|
+
}
|
|
49
|
+
const parsed: unknown = parseYaml(await readFile(flow.path, 'utf8'));
|
|
50
|
+
try {
|
|
51
|
+
spec = compileSpec(parsed);
|
|
52
|
+
} catch (error) {
|
|
53
|
+
if (!(error instanceof CompileError)) throw error;
|
|
54
|
+
// Prefer the authoring dialect; accept compiled kernel JSON for local-CLI
|
|
55
|
+
// compatibility. If both fail, retain the original authoring diagnostic.
|
|
56
|
+
try {
|
|
57
|
+
spec = compileSpec(kernelToAuthoring(parsed));
|
|
58
|
+
} catch {
|
|
59
|
+
throw error;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
} else {
|
|
63
|
+
spec = compileSpec(flow);
|
|
64
|
+
}
|
|
65
|
+
} catch (error) {
|
|
66
|
+
options.signal?.throwIfAborted();
|
|
67
|
+
if (error instanceof CloudFlowError) throw error;
|
|
68
|
+
throw new CloudFlowError('invalid_input', 'Cannot read or compile the declarative flow. Check the file path and YAML/JSON spec.');
|
|
69
|
+
}
|
|
70
|
+
options.signal?.throwIfAborted();
|
|
71
|
+
const hash = specHash(toKernelSpec(spec));
|
|
72
|
+
const result = await cloudRequest('/api/v1/workflows/run', options, {
|
|
73
|
+
// JSON is a YAML subset. Sending canonical data preserves the exact spec
|
|
74
|
+
// while using the server's existing YAML-to-config admission path.
|
|
75
|
+
workflow: canonicalize(spec),
|
|
76
|
+
fileType: 'yaml',
|
|
77
|
+
relayflowVersion: 'v2',
|
|
78
|
+
...(options.workspaceId === undefined ? {} : { workspaceId: options.workspaceId }),
|
|
79
|
+
});
|
|
80
|
+
if (!isCloudRecord(result) || (result.status !== 'pending' && result.status !== 'running')) {
|
|
81
|
+
throw new CloudFlowError('invalid_response', 'Cloud did not return an accepted run.');
|
|
82
|
+
}
|
|
83
|
+
const runId = cloudRunId(result.runId);
|
|
84
|
+
return { runId, status: result.status, specHash: hash, apiUrl: `${baseUrl}/api/v1/workflows/runs/${runId}` };
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export async function getCloudFlowRun(
|
|
88
|
+
runId: string,
|
|
89
|
+
options: CloudConnectionOptions = {},
|
|
90
|
+
): Promise<CloudRunState> {
|
|
91
|
+
cloudRunId(runId);
|
|
92
|
+
const result = await cloudRequest(`/api/v1/workflows/runs/${runId}`, options);
|
|
93
|
+
if (!isCloudRecord(result) || result.runId !== runId || result.relayflowVersion !== 'v2'
|
|
94
|
+
|| typeof result.status !== 'string'
|
|
95
|
+
|| !['pending', 'running', 'completed', 'failed', 'cancelled'].includes(result.status)) {
|
|
96
|
+
throw new CloudFlowError('invalid_response', 'Cloud returned an invalid v2 run record.');
|
|
97
|
+
}
|
|
98
|
+
if (result.status === 'pending' || result.status === 'running') return { runId, status: result.status };
|
|
99
|
+
const report = result.result;
|
|
100
|
+
const reason = isCloudRecord(report) ? report.completionReason : undefined;
|
|
101
|
+
if (typeof reason !== 'string' || !(RUN_COMPLETION_REASONS as readonly string[]).includes(reason)
|
|
102
|
+
|| (result.status === 'completed' && (reason !== 'success' || !isCloudRecord(report) || report.ok !== true || report.status !== 'completed'))
|
|
103
|
+
|| (result.status === 'failed' && !['step_failed', 'budget_exceeded'].includes(reason))
|
|
104
|
+
|| (result.status === 'cancelled' && reason !== 'canceled')) {
|
|
105
|
+
throw new CloudFlowError('invalid_response', 'Cloud terminal record lacks a valid, consistent run completionReason; no execution outcome is attested.');
|
|
106
|
+
}
|
|
107
|
+
return { runId, status: result.status as 'completed' | 'failed' | 'cancelled', completionReason: reason as RunCompletionReason };
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/** Wait without a fixed execution deadline. Abort stops observation, not the hosted run. */
|
|
111
|
+
export async function waitForCloudFlowRun(
|
|
112
|
+
runId: string,
|
|
113
|
+
options: CloudConnectionOptions & { pollIntervalMs?: number } = {},
|
|
114
|
+
): Promise<CloudRunState> {
|
|
115
|
+
const interval = options.pollIntervalMs ?? 2_000;
|
|
116
|
+
if (!Number.isSafeInteger(interval) || interval < 1 || interval > 60_000) {
|
|
117
|
+
throw new CloudFlowError('configuration', 'pollIntervalMs must be an integer from 1 to 60000.');
|
|
118
|
+
}
|
|
119
|
+
let failures = 0;
|
|
120
|
+
for (;;) {
|
|
121
|
+
options.signal?.throwIfAborted();
|
|
122
|
+
try {
|
|
123
|
+
const run = await getCloudFlowRun(runId, options);
|
|
124
|
+
failures = 0;
|
|
125
|
+
if (run.status !== 'pending' && run.status !== 'running') return run;
|
|
126
|
+
} catch (error) {
|
|
127
|
+
options.signal?.throwIfAborted();
|
|
128
|
+
const transient = error instanceof CloudFlowError && (error.code === 'transient_error'
|
|
129
|
+
|| (error.code === 'http_error' && [408, 429, 500, 502, 503, 504].includes(error.status ?? 0)));
|
|
130
|
+
if (!transient) throw error;
|
|
131
|
+
// Only safe GET observation retries. Delay caps at 30s; no overall run deadline.
|
|
132
|
+
failures = Math.min(failures + 1, 16);
|
|
133
|
+
}
|
|
134
|
+
await setTimeout(failures ? Math.min(interval * 2 ** (failures - 1), 30_000) : interval,
|
|
135
|
+
undefined, { signal: options.signal });
|
|
136
|
+
}
|
|
137
|
+
}
|
package/src/compile.ts
CHANGED
|
@@ -15,6 +15,10 @@
|
|
|
15
15
|
// `tests/spec_parity.rs` pin both sides to the same `testdata/` fixture.
|
|
16
16
|
|
|
17
17
|
import { parse as parseYaml } from 'yaml';
|
|
18
|
+
import { parseBudget, toKernelBudget } from './budget.js';
|
|
19
|
+
import { bindingDependencies } from './input-binding.js';
|
|
20
|
+
import { namedGateFailure } from './named-gates.js';
|
|
21
|
+
import { lowerNamedGates } from './named-gate-lowering.js';
|
|
18
22
|
import type {
|
|
19
23
|
AgentStepSpec,
|
|
20
24
|
DeterministicStepSpec,
|
|
@@ -38,21 +42,67 @@ import { canonicalize, specHash } from './canonical.js';
|
|
|
38
42
|
import { validateOutputDeclaration } from './output-schema.js';
|
|
39
43
|
import { validateSpec, type ValidationResult } from './validate.js';
|
|
40
44
|
import { snapshotJsonValue } from './json-value.js';
|
|
45
|
+
import { expandYamlHelpers } from './yaml-helpers.js';
|
|
41
46
|
|
|
42
47
|
export class CompileError extends Error {
|
|
43
48
|
readonly errors: string[];
|
|
44
|
-
|
|
49
|
+
/** Optional diagnostic kind for callers that classify refusals (e.g. preflight). */
|
|
50
|
+
readonly kind?: string;
|
|
51
|
+
constructor(errors: string[], kind?: string) {
|
|
45
52
|
super('spec compile failed:\n - ' + errors.join('\n - '));
|
|
46
53
|
this.name = 'CompileError';
|
|
47
54
|
this.errors = errors;
|
|
55
|
+
if (kind !== undefined) this.kind = kind;
|
|
48
56
|
}
|
|
49
57
|
}
|
|
50
58
|
|
|
59
|
+
/**
|
|
60
|
+
* Parse the f.run timeout before submitting any command to the kernel.
|
|
61
|
+
*
|
|
62
|
+
* Accepts a positive whole-millisecond `number`, or a duration string of the
|
|
63
|
+
* form `<coefficient><unit>` where unit is `ms` / `s` / `m` — matched by an
|
|
64
|
+
* anchored regex that captures the coefficient first (group 1) and the unit
|
|
65
|
+
* second (group 2), so a future edit cannot silently swap them. Fractional
|
|
66
|
+
* coefficients that resolve to integer milliseconds are accepted; the
|
|
67
|
+
* multiplication is rounded to the nearest integer so `1.1s → 1100`
|
|
68
|
+
* survives float-precision (`1.1 * 1000 = 1100.0000000000002`) rather than
|
|
69
|
+
* being rejected by `isSafeInteger`.
|
|
70
|
+
*/
|
|
71
|
+
export function parseStepTimeout(timeout: unknown): number {
|
|
72
|
+
const unitToMs: Record<'ms' | 's' | 'm', number> = { ms: 1, s: 1000, m: 60_000 };
|
|
73
|
+
let milliseconds: number;
|
|
74
|
+
if (typeof timeout === 'number') {
|
|
75
|
+
milliseconds = timeout;
|
|
76
|
+
} else if (typeof timeout === 'string') {
|
|
77
|
+
const match = /^(\d+(?:\.\d+)?)(ms|s|m)$/.exec(timeout);
|
|
78
|
+
if (match === null) {
|
|
79
|
+
milliseconds = NaN;
|
|
80
|
+
} else {
|
|
81
|
+
const coefficient = Number(match[1]);
|
|
82
|
+
const unit = match[2] as 'ms' | 's' | 'm';
|
|
83
|
+
// Round to defeat float precision (1.1 * 1000 = 1100.0000000000002).
|
|
84
|
+
// Fractional milliseconds themselves (e.g. `1.5ms`) still round to `2`,
|
|
85
|
+
// which the isSafeInteger check below accepts. Sub-ms precision is not
|
|
86
|
+
// a supported unit — authors expressing "1.5ms" get the closest int.
|
|
87
|
+
milliseconds = Math.round(coefficient * unitToMs[unit]);
|
|
88
|
+
}
|
|
89
|
+
} else {
|
|
90
|
+
milliseconds = NaN;
|
|
91
|
+
}
|
|
92
|
+
if (!Number.isSafeInteger(milliseconds) || milliseconds <= 0) {
|
|
93
|
+
throw new CompileError(['f.run timeout must be a positive whole number of milliseconds or a duration such as "10s" or "5m".'], 'timeout_invalid');
|
|
94
|
+
}
|
|
95
|
+
if (milliseconds > 15 * 60_000) {
|
|
96
|
+
throw new CompileError(['f.run timeout exceeds the maximum of 15 minutes declared in SURFACE.md.'], 'lease_exceeded');
|
|
97
|
+
}
|
|
98
|
+
return milliseconds;
|
|
99
|
+
}
|
|
100
|
+
|
|
51
101
|
/**
|
|
52
102
|
* Compile a YAML string into a validated authoring `FlowSpec`.
|
|
53
103
|
* Throws `CompileError` on a YAML parse error or any validation failure.
|
|
54
104
|
*/
|
|
55
|
-
export function compileYaml(yaml: string):
|
|
105
|
+
export function compileYaml(yaml: string): CompiledFlowSpec {
|
|
56
106
|
const parsed = parseYaml(yaml);
|
|
57
107
|
if (parsed === null || typeof parsed !== 'object') {
|
|
58
108
|
throw new CompileError(['YAML: expected a mapping at the top level']);
|
|
@@ -69,24 +119,43 @@ export function compileYamlToCanonicalJson(yaml: string): string {
|
|
|
69
119
|
* Validate a parsed spec object and apply authoring defaults, returning a
|
|
70
120
|
* normalized `FlowSpec`. Throws `CompileError` on validation failure.
|
|
71
121
|
*/
|
|
72
|
-
export
|
|
122
|
+
export type CompiledFlowSpec = Omit<FlowSpec, 'budget'> & { budget?: import('./spec.js').BudgetSpec };
|
|
123
|
+
|
|
124
|
+
export function compileSpec(spec: unknown): CompiledFlowSpec {
|
|
73
125
|
let snapshot: unknown;
|
|
74
126
|
try {
|
|
75
127
|
snapshot = snapshotJsonValue(spec, 'spec');
|
|
128
|
+
snapshot = expandYamlHelpers(snapshot);
|
|
76
129
|
} catch (error) {
|
|
77
130
|
throw new CompileError([
|
|
78
131
|
error instanceof Error ? error.message : 'spec: expected JSON-compatible data',
|
|
79
132
|
]);
|
|
80
133
|
}
|
|
134
|
+
if (snapshot !== null && typeof snapshot === 'object' && !Array.isArray(snapshot) && 'budget' in snapshot && snapshot.budget !== undefined) {
|
|
135
|
+
// parseBudget throws BudgetSyntaxError on any malformed header. Without
|
|
136
|
+
// this wrap, that throw escaped compileSpec's own CompileError contract,
|
|
137
|
+
// so callers (validate, cli/check) that only catch CompileError would
|
|
138
|
+
// surface the budget error as an uncaught exception instead of a
|
|
139
|
+
// diagnostic. Rewrap as CompileError so it flows through the same
|
|
140
|
+
// gate-1 refusal path as every other invalid spec.
|
|
141
|
+
try {
|
|
142
|
+
snapshot = { ...snapshot, budget: parseBudget(snapshot.budget) };
|
|
143
|
+
} catch (error) {
|
|
144
|
+
throw new CompileError(
|
|
145
|
+
[`spec.budget: ${error instanceof Error ? error.message : 'budget_syntax_invalid'}`],
|
|
146
|
+
'budget_syntax_invalid',
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
81
150
|
const validation: ValidationResult = validateSpec(snapshot);
|
|
82
|
-
if (!validation.ok) throw new CompileError(validation.errors);
|
|
151
|
+
if (!validation.ok) throw new CompileError(validation.errors, namedGateFailure(validation.errors));
|
|
83
152
|
|
|
84
|
-
const input = snapshot as
|
|
153
|
+
const input = snapshot as CompiledFlowSpec;
|
|
85
154
|
// Preserve named declarations and selectors through authoring normalization.
|
|
86
155
|
// They are resolved exactly once at the kernel boundary, after public
|
|
87
156
|
// preflight has validated every declaration with truthful provenance.
|
|
88
157
|
const steps = input.steps.map(compileStep);
|
|
89
|
-
const flow:
|
|
158
|
+
const flow: CompiledFlowSpec = {
|
|
90
159
|
version: input.version,
|
|
91
160
|
...(input.name !== undefined ? { name: input.name } : {}),
|
|
92
161
|
...(input.description !== undefined ? { description: input.description } : {}),
|
|
@@ -97,6 +166,8 @@ export function compileSpec(spec: unknown): FlowSpec {
|
|
|
97
166
|
...(input.triggers?.length ? { triggers: input.triggers } : {}),
|
|
98
167
|
steps,
|
|
99
168
|
...(input.budget !== undefined ? { budget: input.budget } : {}),
|
|
169
|
+
...(input.workspace !== undefined ? { workspace: input.workspace } : {}),
|
|
170
|
+
...(input.tools !== undefined ? { tools: input.tools } : {}),
|
|
100
171
|
};
|
|
101
172
|
return flow;
|
|
102
173
|
}
|
|
@@ -107,7 +178,9 @@ function compileStep(step: StepSpec): StepSpec {
|
|
|
107
178
|
const base = {
|
|
108
179
|
id: step.id,
|
|
109
180
|
type: step.type,
|
|
110
|
-
...(step.dependsOn !== undefined
|
|
181
|
+
...((step.dependsOn !== undefined || step.input !== undefined)
|
|
182
|
+
? { dependsOn: step.input === undefined ? step.dependsOn : [...new Set([...(step.dependsOn ?? []), ...bindingDependencies(step.input)])] } : {}),
|
|
183
|
+
...(step.input !== undefined ? { input: step.input } : {}),
|
|
111
184
|
maxIterations,
|
|
112
185
|
...(step.memory !== undefined ? { memory: step.memory } : {}),
|
|
113
186
|
...(step.requirements !== undefined ? { requirements: step.requirements } : {}),
|
|
@@ -131,6 +204,7 @@ function compileStep(step: StepSpec): StepSpec {
|
|
|
131
204
|
// #138: `timeoutMs` is deterministic-only — worker-backed verbs own
|
|
132
205
|
// their dispatch timeout. It must be spread HERE and nowhere in `base`.
|
|
133
206
|
...(s.timeoutMs !== undefined ? { timeoutMs: s.timeoutMs } : {}),
|
|
207
|
+
...(s.lease_ms !== undefined ? { lease_ms: parseStepTimeout(s.lease_ms) } : {}),
|
|
134
208
|
};
|
|
135
209
|
}
|
|
136
210
|
case 'llm': {
|
|
@@ -158,6 +232,8 @@ function compileStep(step: StepSpec): StepSpec {
|
|
|
158
232
|
...(s.agent !== undefined ? { agent: s.agent } : {}),
|
|
159
233
|
...(s.cli !== undefined ? { cli: s.cli } : {}),
|
|
160
234
|
...(s.model !== undefined ? { model: s.model } : {}),
|
|
235
|
+
...(s.cwd !== undefined ? { cwd: s.cwd } : {}),
|
|
236
|
+
...(s.transport !== undefined ? { transport: s.transport } : {}),
|
|
161
237
|
recoveryMode,
|
|
162
238
|
...(s.surfaces !== undefined ? { surfaces: s.surfaces } : {}),
|
|
163
239
|
...(s.permissions !== undefined ? { permissions: s.permissions } : {}),
|
|
@@ -252,16 +328,8 @@ export function toKernelSpec(flow: FlowSpec): KernelRunSpec {
|
|
|
252
328
|
// reverts the other, and `validateSpec` and `flows check` would both still
|
|
253
329
|
// look correct. See ops/reviews/20260903-pr139-repair-0903.md section 10.
|
|
254
330
|
...(compiled.triggers?.length ? { triggers: compiled.triggers.map(toKernelTrigger) } : {}),
|
|
255
|
-
steps: compiled.steps.map((step) => toKernelStep(resolveNamedAgent(step, compiled.agents))),
|
|
256
|
-
...(compiled.budget !== undefined
|
|
257
|
-
? {
|
|
258
|
-
budget: {
|
|
259
|
-
...(compiled.budget.maxTokensIn !== undefined ? { max_tokens_in: compiled.budget.maxTokensIn } : {}),
|
|
260
|
-
...(compiled.budget.maxTokensOut !== undefined ? { max_tokens_out: compiled.budget.maxTokensOut } : {}),
|
|
261
|
-
...(compiled.budget.maxDollars !== undefined ? { max_dollars: compiled.budget.maxDollars } : {}),
|
|
262
|
-
},
|
|
263
|
-
}
|
|
264
|
-
: {}),
|
|
331
|
+
steps: lowerNamedGates(compiled.steps).map((step) => toKernelStep(resolveNamedAgent(step, compiled.agents))),
|
|
332
|
+
...(compiled.budget !== undefined ? { budget: toKernelBudget(compiled.budget) } : {}),
|
|
265
333
|
};
|
|
266
334
|
}
|
|
267
335
|
|
|
@@ -354,15 +422,15 @@ function kernelTriggerToAuthoring(value: unknown, at: string): unknown {
|
|
|
354
422
|
|
|
355
423
|
function kernelStepToAuthoring(value: unknown, at: string): unknown {
|
|
356
424
|
const unionKeys = [
|
|
357
|
-
'id', 'type', 'depends_on', 'max_iterations', 'retry', 'verification', 'memory', 'requirements',
|
|
358
|
-
'command', 'timeout_ms', 'prompt', 'model', 'cli', 'instruction',
|
|
425
|
+
'id', 'type', 'depends_on', 'max_iterations', 'retry', 'verification', 'memory', 'requirements', 'input',
|
|
426
|
+
'command', 'timeout_ms', 'lease_ms', 'prompt', 'model', 'cli', 'instruction',
|
|
359
427
|
'recovery_mode', 'surfaces', 'permissions',
|
|
360
428
|
] as const;
|
|
361
429
|
const step = requireKernelObject(value, unionKeys, at);
|
|
362
430
|
const type = step['type'];
|
|
363
|
-
const commonKeys = ['id', 'type', 'depends_on', 'max_iterations', 'retry', 'verification', 'memory', 'requirements'] as const;
|
|
431
|
+
const commonKeys = ['id', 'type', 'depends_on', 'max_iterations', 'retry', 'verification', 'memory', 'requirements', 'input'] as const;
|
|
364
432
|
const typeKeys = type === 'deterministic'
|
|
365
|
-
? ['command', 'timeout_ms'] as const
|
|
433
|
+
? ['command', 'timeout_ms', 'lease_ms'] as const
|
|
366
434
|
: type === 'llm'
|
|
367
435
|
? ['prompt', 'model', 'cli'] as const
|
|
368
436
|
: type === 'agent'
|
|
@@ -374,6 +442,7 @@ function kernelStepToAuthoring(value: unknown, at: string): unknown {
|
|
|
374
442
|
const common = {
|
|
375
443
|
id: step['id'],
|
|
376
444
|
type,
|
|
445
|
+
...(step['input'] !== undefined ? { input: step['input'] } : {}),
|
|
377
446
|
...(step['requirements'] !== undefined ? { requirements: kernelRequirementsToAuthoring(step['requirements'], `${at}.requirements`) } : {}),
|
|
378
447
|
...(dependsOn !== undefined && (!Array.isArray(dependsOn) || dependsOn.length > 0)
|
|
379
448
|
? { dependsOn }
|
|
@@ -387,6 +456,7 @@ function kernelStepToAuthoring(value: unknown, at: string): unknown {
|
|
|
387
456
|
...common,
|
|
388
457
|
command: step['command'],
|
|
389
458
|
...(step['timeout_ms'] !== undefined ? { timeoutMs: step['timeout_ms'] } : {}),
|
|
459
|
+
...(step['lease_ms'] !== undefined ? { lease_ms: step['lease_ms'] } : {}),
|
|
390
460
|
};
|
|
391
461
|
}
|
|
392
462
|
if (type === 'llm') {
|
|
@@ -447,8 +517,12 @@ function kernelMemoryToAuthoring(value: unknown, at: string): unknown {
|
|
|
447
517
|
}
|
|
448
518
|
|
|
449
519
|
function kernelBudgetToAuthoring(value: unknown, at: string): unknown {
|
|
450
|
-
const budget = requireKernelObject(value, ['max_tokens_in', 'max_tokens_out', 'max_dollars'], at);
|
|
520
|
+
const budget = requireKernelObject(value, ['max_tokens_in', 'max_tokens_out', 'max_dollars', 'max_tokens', 'max_wallclock_ms', 'window', 'pricing'], at);
|
|
451
521
|
return {
|
|
522
|
+
...(budget['pricing'] !== undefined ? { pricing: budget['pricing'] } : {}),
|
|
523
|
+
...(budget['max_tokens'] !== undefined ? { maxTokens: budget['max_tokens'] } : {}),
|
|
524
|
+
...(budget['max_wallclock_ms'] !== undefined ? { maxWallclockMs: budget['max_wallclock_ms'] } : {}),
|
|
525
|
+
...(budget['window'] !== undefined ? { window: budget['window'] } : {}),
|
|
452
526
|
...(budget['max_tokens_in'] !== undefined ? { maxTokensIn: budget['max_tokens_in'] } : {}),
|
|
453
527
|
...(budget['max_tokens_out'] !== undefined ? { maxTokensOut: budget['max_tokens_out'] } : {}),
|
|
454
528
|
...(budget['max_dollars'] !== undefined ? { maxDollars: budget['max_dollars'] } : {}),
|
|
@@ -505,7 +579,9 @@ function isObject(value: unknown): value is Record<string, unknown> {
|
|
|
505
579
|
function toKernelStep(step: StepSpec): KernelStepSpec {
|
|
506
580
|
const common: KernelStepCommon = {
|
|
507
581
|
id: step.id,
|
|
508
|
-
depends_on: step.dependsOn ?? []
|
|
582
|
+
depends_on: step.input === undefined ? step.dependsOn ?? []
|
|
583
|
+
: [...new Set([...(step.dependsOn ?? []), ...bindingDependencies(step.input)])],
|
|
584
|
+
...(step.input !== undefined ? { input: step.input } : {}),
|
|
509
585
|
max_iterations: step.maxIterations ?? 1,
|
|
510
586
|
retry: { ...KERNEL_RETRY_DEFAULTS },
|
|
511
587
|
verification: toKernelVerification(step),
|
|
@@ -530,6 +606,7 @@ function toKernelStep(step: StepSpec): KernelStepSpec {
|
|
|
530
606
|
type: 'deterministic',
|
|
531
607
|
command: step.command,
|
|
532
608
|
...(step.timeoutMs !== undefined ? { timeout_ms: step.timeoutMs } : {}),
|
|
609
|
+
...(step.lease_ms !== undefined ? { lease_ms: parseStepTimeout(step.lease_ms) } : {}),
|
|
533
610
|
};
|
|
534
611
|
case 'llm': {
|
|
535
612
|
return {
|
|
@@ -547,6 +624,8 @@ function toKernelStep(step: StepSpec): KernelStepSpec {
|
|
|
547
624
|
instruction: step.instruction,
|
|
548
625
|
...(step.cli !== undefined ? { cli: step.cli } : {}),
|
|
549
626
|
...(step.model !== undefined ? { model: step.model } : {}),
|
|
627
|
+
...(step.cwd !== undefined ? { cwd: step.cwd } : {}),
|
|
628
|
+
...(step.transport !== undefined ? { transport: step.transport } : {}),
|
|
550
629
|
recovery_mode: step.recoveryMode ?? 'reset',
|
|
551
630
|
};
|
|
552
631
|
const surfaces = {
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
3
|
+
import { basename, dirname, join, resolve } from 'node:path';
|
|
4
|
+
|
|
5
|
+
export interface CreateFlowOptions {
|
|
6
|
+
name?: string;
|
|
7
|
+
template?: 'agent' | 'deterministic';
|
|
8
|
+
/** CLI used by future f.agent steps. It must already be authenticated. */
|
|
9
|
+
cli?: string;
|
|
10
|
+
/** Install project dependencies (default true). */
|
|
11
|
+
install?: boolean;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface CreatedFlow {
|
|
15
|
+
directory: string;
|
|
16
|
+
flowPath: string;
|
|
17
|
+
configPath: string;
|
|
18
|
+
installed: boolean;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** Scaffold a new project. Existing directories are never overwritten. */
|
|
22
|
+
export async function createFlow(target: string, opts: CreateFlowOptions = {}): Promise<CreatedFlow> {
|
|
23
|
+
if (!target.trim()) throw new Error('Choose a new directory, for example: create-flow my-flow');
|
|
24
|
+
const directory = resolve(target);
|
|
25
|
+
const name = opts.name ?? basename(directory);
|
|
26
|
+
if (!/^[a-z0-9][a-z0-9-]{0,63}$/.test(name)) {
|
|
27
|
+
throw new Error('Flow name must be 1–64 lowercase letters, numbers or hyphens, starting with a letter or number.');
|
|
28
|
+
}
|
|
29
|
+
const template = opts.template ?? 'agent';
|
|
30
|
+
if (template !== 'agent' && template !== 'deterministic') throw new Error('Template must be agent or deterministic.');
|
|
31
|
+
const cli = opts.cli ?? 'claude';
|
|
32
|
+
if (!cli.trim() || /[\x00-\x1f\x7f]/.test(cli)) throw new Error('CLI must be a nonempty command or path.');
|
|
33
|
+
const { version } = JSON.parse(await readFile(new URL('../package.json', import.meta.url), 'utf8')) as { version: string };
|
|
34
|
+
await mkdir(dirname(directory), { recursive: true });
|
|
35
|
+
try {
|
|
36
|
+
await mkdir(directory);
|
|
37
|
+
} catch (error) {
|
|
38
|
+
if ((error as NodeJS.ErrnoException).code === 'EEXIST') {
|
|
39
|
+
throw new Error(`Directory already exists: ${directory}. Choose a new directory; no files were changed.`);
|
|
40
|
+
}
|
|
41
|
+
throw error;
|
|
42
|
+
}
|
|
43
|
+
const flowFile = `${name}.flow.ts`;
|
|
44
|
+
const agentStep = template === 'agent'
|
|
45
|
+
? ` const answer = await f.agent('greeter', { task: 'Reply with one short hello sentence. Do not use tools or modify files.' });\n console.log(answer.summary);\n`
|
|
46
|
+
: '';
|
|
47
|
+
const runFlags = template === 'agent' ? ' --local-agent' : '';
|
|
48
|
+
const files: Record<string, string> = {
|
|
49
|
+
[flowFile]: `import { flow } from '@relayflows/surface';\n\nexport default flow(${JSON.stringify(name)}, async (f) => {\n const greeting = await f.run('echo "Hello from Relayflows"');\n console.log(greeting.trim());\n${agentStep} f.done('success');\n});\n`,
|
|
50
|
+
'flows.json': JSON.stringify({ cli }, null, 2) + '\n',
|
|
51
|
+
'package.json': JSON.stringify({
|
|
52
|
+
name, private: true, type: 'module',
|
|
53
|
+
scripts: { start: `flows run ${flowFile}${runFlags} --input '{}'` },
|
|
54
|
+
engines: { node: '>=22.18.0' },
|
|
55
|
+
dependencies: { relayflows: version, '@relayflows/surface': version },
|
|
56
|
+
}, null, 2) + '\n',
|
|
57
|
+
'.gitignore': 'node_modules/\n.relayflowd/\n',
|
|
58
|
+
'README.md': `# ${name}\n\nRun \`npm start\` (or \`npx flows run ${flowFile}${runFlags} --input '{}'\`).\n\n${template === 'agent' ? `Requires ${JSON.stringify(cli)} installed and authenticated. The local agent worker runs\non this machine with the CLI's existing access; it provides no workspace isolation.\n` : 'This starter needs no model credentials.\n'}\n\`flows.json\` selects the CLI for agent steps. Steps execute through the local\njournal; authored TypeScript bodies are not yet durably resumable as a whole.\n`,
|
|
59
|
+
};
|
|
60
|
+
for (const [file, contents] of Object.entries(files)) {
|
|
61
|
+
await writeFile(join(directory, file), contents, { flag: 'wx' });
|
|
62
|
+
}
|
|
63
|
+
if (opts.install !== false) {
|
|
64
|
+
try {
|
|
65
|
+
await installDependencies(directory);
|
|
66
|
+
} catch (error) {
|
|
67
|
+
throw new Error(`Created ${directory}, but dependency installation failed. Run npm install there to retry.`, { cause: error });
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return { directory, flowPath: join(directory, flowFile), configPath: join(directory, 'flows.json'), installed: opts.install !== false };
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function installDependencies(cwd: string): Promise<void> {
|
|
74
|
+
return new Promise((resolveInstall, reject) => {
|
|
75
|
+
const child = spawn('npm', ['install', '--no-audit', '--no-fund'], { cwd, stdio: 'inherit' });
|
|
76
|
+
child.once('error', reject);
|
|
77
|
+
child.once('close', (code, signal) => {
|
|
78
|
+
if (code === 0) resolveInstall();
|
|
79
|
+
else reject(new Error(`npm install exited ${signal ?? code}`));
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
}
|
package/src/daemon-connection.ts
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
// what a file says about itself.
|
|
14
14
|
|
|
15
15
|
import { spawn, type ChildProcess, type SpawnOptions } from 'node:child_process';
|
|
16
|
+
import { createHash } from 'node:crypto';
|
|
16
17
|
import {
|
|
17
18
|
closeSync,
|
|
18
19
|
mkdirSync,
|
|
@@ -20,6 +21,7 @@ import {
|
|
|
20
21
|
readFileSync,
|
|
21
22
|
rmSync,
|
|
22
23
|
} from 'node:fs';
|
|
24
|
+
import { tmpdir } from 'node:os';
|
|
23
25
|
import { join, resolve } from 'node:path';
|
|
24
26
|
import { JournalClient, JournalProtocolError } from './journal-client.js';
|
|
25
27
|
import { PROTOCOL_VERSION } from './protocol.js';
|
|
@@ -145,8 +147,37 @@ export const defaultDaemonLifecycleDeps: DaemonLifecycleDeps = {
|
|
|
145
147
|
}),
|
|
146
148
|
};
|
|
147
149
|
|
|
150
|
+
/**
|
|
151
|
+
* Derive the daemon socket path from the data dir.
|
|
152
|
+
*
|
|
153
|
+
* The socket lives outside the data dir at a short hashed path so that a deep
|
|
154
|
+
* working directory cannot push the full socket path past `SUN_LEN` (~104
|
|
155
|
+
* bytes on macOS, ~108 on Linux) — the first-run papercut in #262. Anti-hijack
|
|
156
|
+
* still holds because the daemon derives the same path from the same input;
|
|
157
|
+
* this function and Rust's `crate::socket_path::derive_socket_path` must stay
|
|
158
|
+
* in lockstep. Both use `resolve()` / `std::path::absolute` (lexical, no
|
|
159
|
+
* symlink resolution) so `/var` vs macOS's `/private/var` can never diverge.
|
|
160
|
+
*
|
|
161
|
+
* `SOCKET_FILE` is retained as the exported constant name for compatibility,
|
|
162
|
+
* but is no longer the *entire* socket filename — see the derivation below.
|
|
163
|
+
*/
|
|
148
164
|
export function socketPathFor(dataDir: string): string {
|
|
149
|
-
|
|
165
|
+
const absolute = resolve(dataDir);
|
|
166
|
+
// 12 hex chars = 6 bytes of SHA-256. Enough separation between data dirs on
|
|
167
|
+
// one machine, small enough to leave headroom under SUN_LEN.
|
|
168
|
+
const hash = createHash('sha256').update(absolute).digest('hex').slice(0, 12);
|
|
169
|
+
return join(runtimeDir(), `relayflowd-${hash}.sock`);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function runtimeDir(): string {
|
|
173
|
+
// XDG first (Linux, typically `/run/user/<uid>/`, ~14 chars — the shortest
|
|
174
|
+
// per-user private option). TMPDIR next (macOS, `/var/folders/xx/YYY/T/`,
|
|
175
|
+
// per-user private). Only fall back to os.tmpdir() when neither is set.
|
|
176
|
+
const xdg = process.env['XDG_RUNTIME_DIR'];
|
|
177
|
+
if (xdg && xdg.length > 0) return xdg;
|
|
178
|
+
const tmp = process.env['TMPDIR'];
|
|
179
|
+
if (tmp && tmp.length > 0) return tmp;
|
|
180
|
+
return tmpdir();
|
|
150
181
|
}
|
|
151
182
|
|
|
152
183
|
export function connectionPathFor(dataDir: string): string {
|
package/src/demo-hn-monitor.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { readFile } from 'node:fs/promises';
|
|
2
2
|
import { dirname, join, resolve } from 'node:path';
|
|
3
3
|
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import { socketPathFor } from './daemon-connection.js';
|
|
4
5
|
import { pollHackerNewsOnce, type EventSink } from './hn-poller.js';
|
|
5
6
|
import { JournalClient } from './journal-client.js';
|
|
6
7
|
import type { EventSubmitResult } from './protocol.js';
|
|
@@ -8,7 +9,7 @@ import type { EventSubmitResult } from './protocol.js';
|
|
|
8
9
|
const sdkRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..');
|
|
9
10
|
const repositoryRoot = resolve(sdkRoot, '..');
|
|
10
11
|
const dataDir = resolve(process.env.RELAYFLOW_DATA_DIR ?? join(repositoryRoot, '.relayflowd'));
|
|
11
|
-
const socketPath =
|
|
12
|
+
const socketPath = socketPathFor(dataDir);
|
|
12
13
|
const specPath = join(repositoryRoot, 'testdata', 'hn-monitor.spec.canonical.json');
|
|
13
14
|
|
|
14
15
|
interface Submission {
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { compileSpec, toKernelSpec } from './compile.js';
|
|
3
|
+
import type { JournalClient } from './journal-client.js';
|
|
4
|
+
import type { StepDispatchEvent } from './protocol.js';
|
|
5
|
+
import { SPEC_SCHEMA_VERSION } from './spec.js';
|
|
6
|
+
import { withWorkerLease } from './worker-lease.js';
|
|
7
|
+
import { snapshotJsonValue } from './json-value.js';
|
|
8
|
+
|
|
9
|
+
/** Structural subset of Agent Relay's messages.dm; credentials stay in the client. */
|
|
10
|
+
export interface ChannelBroker {
|
|
11
|
+
messages: {
|
|
12
|
+
dm(input: {
|
|
13
|
+
to: string;
|
|
14
|
+
text: string;
|
|
15
|
+
idempotencyKey: string;
|
|
16
|
+
metadata: { channel: string; messageId: string };
|
|
17
|
+
}): Promise<unknown>;
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface ChannelPost {
|
|
22
|
+
channel: string;
|
|
23
|
+
to: string;
|
|
24
|
+
text: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function validatePost(post: ChannelPost, participants: readonly string[]): void {
|
|
28
|
+
if (typeof post.channel !== 'string' || !/^[A-Za-z0-9][A-Za-z0-9_-]*$/.test(post.channel)) {
|
|
29
|
+
throw new Error('channel_invalid');
|
|
30
|
+
}
|
|
31
|
+
if (typeof post.to !== 'string' || !post.to.trim() || !participants.includes(post.to)) {
|
|
32
|
+
throw new Error(`channel_participant_unresolved: ${post.to}`);
|
|
33
|
+
}
|
|
34
|
+
if (typeof post.text !== 'string') throw new Error('channel_text_invalid');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** One message boundary per step. Participant resolution precedes journal submission. */
|
|
38
|
+
export function channelPostSpec(
|
|
39
|
+
name: string, stepId: string, post: ChannelPost, participants: readonly string[],
|
|
40
|
+
) {
|
|
41
|
+
post = snapshotJsonValue(post, 'channel post') as unknown as ChannelPost;
|
|
42
|
+
validatePost(post, participants);
|
|
43
|
+
return toKernelSpec(compileSpec({
|
|
44
|
+
version: SPEC_SCHEMA_VERSION, name,
|
|
45
|
+
steps: [{ id: stepId, type: 'agent',
|
|
46
|
+
instruction: JSON.stringify({ type: 'effect', provider: 'channel', verb: 'post',
|
|
47
|
+
channel: post.channel, to: post.to, text: post.text }),
|
|
48
|
+
maxIterations: 3, recoveryMode: 'reset',
|
|
49
|
+
surfaces: { streams: [{ stream: `channel-${post.channel}` }], external: [`/channel/${post.channel}`] },
|
|
50
|
+
}],
|
|
51
|
+
}));
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Internal post-only proof, for an already provisioned run-scoped broker client.
|
|
56
|
+
* The broker must honor idempotencyKey across worker retries. The application
|
|
57
|
+
* message ID is deliberately distinct from any provider-generated record ID.
|
|
58
|
+
*/
|
|
59
|
+
export async function completeChannelPost(
|
|
60
|
+
client: JournalClient, dispatch: StepDispatchEvent, broker: ChannelBroker,
|
|
61
|
+
participants: readonly string[],
|
|
62
|
+
): Promise<void> {
|
|
63
|
+
const spec = dispatch.spec as { instruction?: string; surfaces?: { external?: string[] } };
|
|
64
|
+
if (dispatch.step_type !== 'agent' || typeof spec.instruction !== 'string') {
|
|
65
|
+
throw new Error('channel_dispatch_invalid');
|
|
66
|
+
}
|
|
67
|
+
const call = JSON.parse(spec.instruction) as ChannelPost & { type: string; provider: string; verb: string };
|
|
68
|
+
if (call.type !== 'effect' || call.provider !== 'channel' || call.verb !== 'post') {
|
|
69
|
+
throw new Error('channel_dispatch_invalid');
|
|
70
|
+
}
|
|
71
|
+
validatePost(call, participants);
|
|
72
|
+
const surfacePath = `/channel/${call.channel}`;
|
|
73
|
+
if (!spec.surfaces?.external?.includes(surfacePath)) throw new Error('channel_surface_undeclared');
|
|
74
|
+
const messageId = createHash('sha256')
|
|
75
|
+
.update(JSON.stringify([dispatch.run_id, dispatch.step_id, call.channel])).digest('hex');
|
|
76
|
+
await withWorkerLease(client, dispatch, async signal => {
|
|
77
|
+
await client.performEffect({
|
|
78
|
+
runId: dispatch.run_id, stepId: dispatch.step_id, attempt: dispatch.attempt,
|
|
79
|
+
idempotencyKey: dispatch.idempotency_key, surfacePath,
|
|
80
|
+
revisionBefore: 'pending', revisionAfter: messageId,
|
|
81
|
+
}, async () => {
|
|
82
|
+
signal.throwIfAborted();
|
|
83
|
+
await broker.messages.dm({ to: call.to, text: call.text, idempotencyKey: messageId,
|
|
84
|
+
metadata: { channel: call.channel, messageId } });
|
|
85
|
+
signal.throwIfAborted();
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
// All receipt fields already exist in the journaled spec and stable run identity.
|
|
89
|
+
// A crash after confirm needs neither a local receipt cache nor a broker read.
|
|
90
|
+
await client.stepComplete(dispatch.run_id, dispatch.step_id, dispatch.attempt,
|
|
91
|
+
dispatch.idempotency_key, 'success', {
|
|
92
|
+
output: { type: 'effect', provider: 'channel', verb: 'post',
|
|
93
|
+
messageId, channel: call.channel, to: call.to, text: call.text },
|
|
94
|
+
started_pins: dispatch.pins, end_pins: dispatch.pins,
|
|
95
|
+
effects: [{ surface_path: surfacePath, idempotency_key: dispatch.idempotency_key }],
|
|
96
|
+
});
|
|
97
|
+
}
|