deepline 0.3.89 → 0.3.91
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/contracts/install-commands.json +29 -0
- package/dist/bundling-sources/sdk/src/client.ts +221 -43
- package/dist/bundling-sources/sdk/src/index.ts +4 -0
- package/dist/bundling-sources/sdk/src/monitor-fleet-contract.ts +2 -556
- package/dist/bundling-sources/sdk/src/monitor-fleets.ts +2 -2
- package/dist/bundling-sources/sdk/src/pagination.ts +25 -0
- package/dist/bundling-sources/sdk/src/plays/bundle-play-file.ts +1 -1
- package/dist/bundling-sources/sdk/src/release.ts +11 -9
- package/dist/bundling-sources/sdk/src/types.ts +29 -1
- package/dist/bundling-sources/shared_libs/integration-contracts/monitor-fleet-contract.ts +549 -0
- package/dist/bundling-sources/shared_libs/monitors/analytics-public.ts +10 -0
- package/dist/bundling-sources/shared_libs/monitors/fleets/errors.ts +96 -0
- package/dist/bundling-sources/shared_libs/monitors/fleets/limits.ts +8 -0
- package/dist/bundling-sources/shared_libs/monitors/fleets/plan.ts +320 -0
- package/dist/bundling-sources/shared_libs/monitors/fleets/verdict.ts +106 -0
- package/dist/bundling-sources/shared_libs/monitors/http-error-response.ts +8 -1
- package/dist/bundling-sources/shared_libs/monitors/inbound-queue.ts +4 -4
- package/dist/bundling-sources/shared_libs/monitors/inventory-reconciliation.ts +59 -0
- package/dist/bundling-sources/shared_libs/monitors/lifecycle-notifications.ts +7 -9
- package/dist/bundling-sources/shared_libs/monitors/monitor-fleet-limits.ts +2 -12
- package/dist/bundling-sources/shared_libs/monitors/package.json +113 -4
- package/dist/bundling-sources/shared_libs/monitors/provider-monitor-batch-operation.ts +56 -9
- package/dist/bundling-sources/shared_libs/monitors/provider-monitor-consumer-health.ts +2 -2
- package/dist/bundling-sources/shared_libs/monitors/provider-monitor-identity-claims.ts +2 -2
- package/dist/bundling-sources/shared_libs/monitors/provider-monitor-public-aliases.ts +1 -1
- package/dist/bundling-sources/shared_libs/monitors/signal-radars/inbound-dead-letter.ts +245 -0
- package/dist/bundling-sources/shared_libs/monitors/signal-radars/inbound-message.ts +38 -0
- package/dist/bundling-sources/shared_libs/monitors/signal-radars/inbound-queue-ack-probe.ts +94 -0
- package/dist/bundling-sources/shared_libs/monitors/signal-radars/inbound-queue-delivery.ts +60 -0
- package/dist/bundling-sources/shared_libs/monitors/signal-radars/inbound-queue.ts +60 -0
- package/dist/bundling-sources/shared_libs/monitors/snitcher-install-validation.ts +1 -1
- package/dist/bundling-sources/shared_libs/monitors/snitcher-ip2company.ts +2 -2
- package/dist/bundling-sources/shared_libs/monitors/snitcher-radar.ts +1 -1
- package/dist/bundling-sources/shared_libs/monitors/tracking-provider-integrations.ts +1 -1
- package/dist/bundling-sources/shared_libs/observability/build-context.ts +13 -0
- package/dist/bundling-sources/shared_libs/observability/log-metadata.ts +160 -0
- package/dist/bundling-sources/shared_libs/observability/request-context.ts +68 -0
- package/dist/bundling-sources/shared_libs/observability/scheduled-jobs.json +690 -0
- package/dist/bundling-sources/shared_libs/observability/scheduled-work-manual.ts +35 -0
- package/dist/bundling-sources/shared_libs/observability/scheduled-work.ts +5 -0
- package/dist/bundling-sources/shared_libs/play-data-plane/neon-constants.ts +9 -0
- package/dist/bundling-sources/shared_libs/play-data-plane/r2.ts +272 -0
- package/dist/bundling-sources/shared_libs/play-runtime/app-runtime-api.ts +46 -1
- package/dist/bundling-sources/shared_libs/play-runtime/async-operation.ts +66 -1
- package/dist/bundling-sources/shared_libs/play-runtime/async-play-invocation.ts +0 -1
- package/dist/bundling-sources/shared_libs/play-runtime/bounceban-batching.ts +137 -0
- package/dist/bundling-sources/shared_libs/play-runtime/context.ts +779 -140
- package/dist/bundling-sources/shared_libs/play-runtime/ctx-types.ts +46 -0
- package/dist/bundling-sources/shared_libs/play-runtime/daytona-payload-contract.ts +68 -0
- package/dist/bundling-sources/shared_libs/play-runtime/daytona-sandbox-janitor.ts +103 -0
- package/dist/bundling-sources/shared_libs/play-runtime/daytona-sandbox-reaper.ts +725 -0
- package/dist/bundling-sources/shared_libs/play-runtime/db-session-lease-store.ts +578 -0
- package/dist/bundling-sources/shared_libs/play-runtime/deployment/commands/roll-execution-relay.ts +521 -0
- package/dist/bundling-sources/shared_libs/play-runtime/deployment/commands/roll-receipt-gateway.ts +1378 -0
- package/dist/bundling-sources/shared_libs/play-runtime/deployment/commands/runtime-topology.ts +1024 -0
- package/dist/bundling-sources/shared_libs/play-runtime/deployment/fly-gateway-route-probe.ts +193 -0
- package/dist/bundling-sources/shared_libs/play-runtime/deployment/fly-machine-lifecycle.ts +104 -0
- package/dist/bundling-sources/shared_libs/play-runtime/deployment/fly-machines.ts +1844 -0
- package/dist/bundling-sources/shared_libs/play-runtime/deployment/fly-registry.ts +21 -0
- package/dist/bundling-sources/shared_libs/play-runtime/deployment/process-tree.ts +40 -0
- package/dist/bundling-sources/shared_libs/play-runtime/deployment/runtime-gateway-drain.ts +262 -0
- package/dist/bundling-sources/shared_libs/play-runtime/deployment/runtime-surface.ts +98 -0
- package/dist/bundling-sources/shared_libs/play-runtime/deployment/runtime-topology.ts +303 -0
- package/dist/bundling-sources/shared_libs/play-runtime/deployment/runtime-tuple-publication-guardian.ts +70 -0
- package/dist/bundling-sources/shared_libs/play-runtime/deployment/runtime-tuple-recovery.ts +519 -0
- package/dist/bundling-sources/shared_libs/play-runtime/deployment/vercel-fly-rollback-alignment.ts +280 -0
- package/dist/bundling-sources/shared_libs/play-runtime/deployment/vercel-production-gate.ts +159 -0
- package/dist/bundling-sources/shared_libs/play-runtime/durable-call-cache.ts +29 -0
- package/dist/bundling-sources/shared_libs/play-runtime/execution-capabilities.ts +23 -0
- package/dist/bundling-sources/shared_libs/play-runtime/execution-gateway-invocation-fence.ts +442 -0
- package/dist/bundling-sources/shared_libs/play-runtime/execution-protocol.ts +3 -3
- package/dist/bundling-sources/shared_libs/play-runtime/fixture-behavior.ts +18 -0
- package/dist/bundling-sources/shared_libs/play-runtime/governor/app-runtime-rate-state-backend.ts +9 -0
- package/dist/bundling-sources/shared_libs/play-runtime/governor/policy.ts +1 -1
- package/dist/bundling-sources/shared_libs/play-runtime/governor/rate-state-backend.ts +1 -1
- package/dist/bundling-sources/shared_libs/play-runtime/integration-operation-reader-fence/contracts.ts +66 -0
- package/dist/bundling-sources/shared_libs/play-runtime/integration-operation-reader-fence/job.ts +145 -0
- package/dist/bundling-sources/shared_libs/play-runtime/integration-operation-reader-fence/memory-store.ts +114 -0
- package/dist/bundling-sources/shared_libs/play-runtime/integration-operation-reader-fence/ports.ts +21 -0
- package/dist/bundling-sources/shared_libs/play-runtime/integration-operation-reader-fence/postgres-store.ts +170 -0
- package/dist/bundling-sources/shared_libs/play-runtime/internal-bootstrap-token.ts +1322 -0
- package/dist/bundling-sources/shared_libs/play-runtime/internal-step-ids.ts +2 -2
- package/dist/bundling-sources/shared_libs/play-runtime/lane-retirement-gate.ts +1 -1
- package/dist/bundling-sources/shared_libs/play-runtime/launch-source-policy.ts +112 -0
- package/dist/bundling-sources/shared_libs/play-runtime/map-execution-frame.ts +8 -0
- package/dist/bundling-sources/shared_libs/play-runtime/perf-trace.ts +127 -0
- package/dist/bundling-sources/shared_libs/play-runtime/play-artifact-runtime-contract.ts +5 -0
- package/dist/bundling-sources/shared_libs/play-runtime/play-run-recovery-policy.ts +24 -1
- package/dist/bundling-sources/shared_libs/play-runtime/play-runs/admission/contracts.ts +56 -0
- package/dist/bundling-sources/shared_libs/play-runtime/play-runs/admission/index.ts +13 -0
- package/dist/bundling-sources/shared_libs/play-runtime/play-runs/admission/job.ts +41 -0
- package/dist/bundling-sources/shared_libs/play-runtime/play-runs/admission/ports.ts +17 -0
- package/dist/bundling-sources/shared_libs/play-runtime/play-runs/admission/postgres-adapter.ts +54 -0
- package/dist/bundling-sources/shared_libs/play-runtime/play-runs/attempts/contracts.ts +114 -0
- package/dist/bundling-sources/shared_libs/play-runtime/play-runs/attempts/engine-suspension.ts +108 -0
- package/dist/bundling-sources/shared_libs/play-runtime/play-runs/attempts/index.ts +36 -0
- package/dist/bundling-sources/shared_libs/play-runtime/play-runs/attempts/job.ts +97 -0
- package/dist/bundling-sources/shared_libs/play-runtime/play-runs/attempts/ports.ts +104 -0
- package/dist/bundling-sources/shared_libs/play-runtime/play-runs/attempts/postgres-fence-adapter.ts +263 -0
- package/dist/bundling-sources/shared_libs/play-runtime/play-runs/attempts/worker-terminal-projection.ts +64 -0
- package/dist/bundling-sources/shared_libs/play-runtime/play-runs/projection/contracts.ts +29 -0
- package/dist/bundling-sources/shared_libs/play-runtime/play-runs/projection/gateway-adapter.ts +56 -0
- package/dist/bundling-sources/shared_libs/play-runtime/play-runs/projection/index.ts +15 -0
- package/dist/bundling-sources/shared_libs/play-runtime/play-runs/projection/job.ts +65 -0
- package/dist/bundling-sources/shared_libs/play-runtime/play-runs/projection/ports.ts +17 -0
- package/dist/bundling-sources/shared_libs/play-runtime/play-runs/sandbox-cleanup/contracts.ts +42 -0
- package/dist/bundling-sources/shared_libs/play-runtime/play-runs/sandbox-cleanup/index.ts +17 -0
- package/dist/bundling-sources/shared_libs/play-runtime/play-runs/sandbox-cleanup/job.ts +142 -0
- package/dist/bundling-sources/shared_libs/play-runtime/play-runs/sandbox-cleanup/ports.ts +42 -0
- package/dist/bundling-sources/shared_libs/play-runtime/play-runs/sandbox-cleanup/postgres-adapter.ts +102 -0
- package/dist/bundling-sources/shared_libs/play-runtime/play-runs/terminal/contracts.ts +60 -0
- package/dist/bundling-sources/shared_libs/play-runtime/play-runs/terminal/index.ts +16 -0
- package/dist/bundling-sources/shared_libs/play-runtime/play-runs/terminal/job.ts +78 -0
- package/dist/bundling-sources/shared_libs/play-runtime/play-runs/terminal/ports.ts +54 -0
- package/dist/bundling-sources/shared_libs/play-runtime/play-runs/terminal/postgres-adapter.ts +71 -0
- package/dist/bundling-sources/shared_libs/play-runtime/play-runtime-batching-registry.ts +4 -0
- package/dist/bundling-sources/shared_libs/play-runtime/protocol.ts +2 -0
- package/dist/bundling-sources/shared_libs/play-runtime/receipt-status.ts +5 -8
- package/dist/bundling-sources/shared_libs/play-runtime/run-failure.ts +3 -5
- package/dist/bundling-sources/shared_libs/play-runtime/run-identity.ts +168 -0
- package/dist/bundling-sources/shared_libs/play-runtime/run-ledger.ts +44 -5
- package/dist/bundling-sources/shared_libs/play-runtime/runner-app/child-play-resolver.ts +26 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runner-app/index.ts +3662 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runner-app/progress-sidecar.ts +47 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runner-app/runtime-deadline.ts +84 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runner-app/runtime-gateway-transport.ts +91 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runner-app/runtime-heartbeat.ts +424 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runner-app/runtime-progress-push.ts +321 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runner-app/runtime-sheet-persistence-retry.ts +110 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runner-app/runtime-terminal-push.ts +625 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-lifecycle.ts +72 -9
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-payload-transport.ts +6 -12
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona.ts +19 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/bundle.ts +0 -1
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/runtime-sandbox-reconciliation.ts +14 -44
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-api.ts +103 -208
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-authority.ts +201 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-env.ts +6 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-operation-contract.ts +67 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-release-environment.ts +2 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-sandbox-contract.ts +68 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-scheduler-topology.ts +14 -2
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-telemetry.ts +108 -0
- package/dist/bundling-sources/shared_libs/play-runtime/sandbox-runs/contract.ts +4 -0
- package/dist/bundling-sources/shared_libs/play-runtime/sandbox-runs/runner-backend-adapter.ts +3 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backend.ts +7 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-deadline-reconciliation.ts +88 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-play-deploy-task.ts +124 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-play-run-task.ts +68 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-pool.ts +193 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-queue-lifecycle.ts +269 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-queue-suffix.ts +73 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-runtime-worker.ts +6642 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-shared.ts +897 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-worker-metrics.ts +120 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-worker-required-env.ts +76 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd.ts +476 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/gateway-heavy-request-admission.ts +47 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/gateway-progress-registry.ts +147 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/index.ts +27 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/lazy.ts +50 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/play-run-recovery.ts +98 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres-budget-state.ts +260 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres-pool.ts +1416 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres-progress.ts +1255 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres-rate-state.ts +942 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres.ts +14636 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/receipt-gateway.ts +888 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/run-ledger-outbox-recovery.ts +355 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/run-terminal-transition.ts +46 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/runner-push.ts +60 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/runtime-api-origin.ts +33 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/runtime-receipt-gateway-health.ts +64 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/runtime-sandbox-capacity.ts +835 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/runtime-sandbox-ledger-reconciliation.ts +1218 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/runtime-sandbox-operation.ts +207 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/runtime-scheduler-schema.ts +10 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/scheduler-capacity.ts +123 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/scheduler-launch-read-observability.ts +78 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/submit-admission.ts +169 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/terminal-contract.ts +20 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/worker-queue-metrics.ts +34 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/worker-resource-metrics.ts +249 -0
- package/dist/bundling-sources/shared_libs/play-runtime/sheet-attempt-sql.ts +7 -0
- package/dist/bundling-sources/shared_libs/play-runtime/step-progress.ts +296 -0
- package/dist/bundling-sources/shared_libs/play-runtime/tool-call/native-batch.ts +3 -3
- package/dist/bundling-sources/shared_libs/play-runtime/tool-call/runtime-step-receipts-adapter.ts +20 -1
- package/dist/bundling-sources/shared_libs/play-runtime/tool-execute-retry-policy.ts +60 -15
- package/dist/bundling-sources/shared_libs/play-runtime/tool-execution-outcome.ts +20 -0
- package/dist/bundling-sources/shared_libs/play-runtime/transient-service-error.ts +12 -0
- package/dist/bundling-sources/shared_libs/play-runtime/vercel-protection.ts +14 -1
- package/dist/bundling-sources/shared_libs/play-runtime/webhook-receipt-retry.ts +50 -0
- package/dist/bundling-sources/shared_libs/play-runtime/work-receipt-storage-contract.ts +165 -0
- package/dist/bundling-sources/shared_libs/play-runtime/work-receipts.ts +15 -23
- package/dist/bundling-sources/shared_libs/play-runtime/workflow-executor-auth.ts +169 -0
- package/dist/bundling-sources/shared_libs/play-runtime/zerobounce-batching.ts +122 -0
- package/dist/bundling-sources/shared_libs/plays/artifact-types.ts +2 -0
- package/dist/bundling-sources/shared_libs/plays/bundling/repository-adapter.ts +24 -0
- package/dist/bundling-sources/shared_libs/plays/child-run-projection.ts +174 -0
- package/dist/bundling-sources/shared_libs/plays/contracts.ts +2 -0
- package/dist/bundling-sources/shared_libs/plays/deploy/api-client.ts +267 -0
- package/dist/bundling-sources/shared_libs/plays/deploy/control-plane-types.ts +73 -0
- package/dist/bundling-sources/shared_libs/plays/deploy/manifest.ts +162 -0
- package/dist/bundling-sources/shared_libs/plays/deploy/project-serializer.ts +87 -0
- package/dist/bundling-sources/shared_libs/plays/deploy/source-file-path.ts +26 -0
- package/dist/bundling-sources/shared_libs/plays/deploy/target-paths.ts +36 -0
- package/dist/bundling-sources/shared_libs/plays/deployment-status.ts +1 -1
- package/dist/bundling-sources/shared_libs/plays/docflow.ts +1 -1
- package/dist/bundling-sources/shared_libs/plays/launch/input-contract.ts +400 -0
- package/dist/bundling-sources/shared_libs/plays/launch/prepare.ts +96 -0
- package/dist/bundling-sources/shared_libs/plays/local-file-discovery.ts +385 -0
- package/dist/bundling-sources/shared_libs/plays/play-grep-search.ts +112 -0
- package/dist/bundling-sources/shared_libs/plays/play-reference.ts +117 -0
- package/dist/bundling-sources/shared_libs/plays/run-input-summary.ts +260 -0
- package/dist/bundling-sources/shared_libs/plays/run-summary-types.ts +49 -0
- package/dist/bundling-sources/shared_libs/plays/secrets/errors.ts +19 -0
- package/dist/bundling-sources/shared_libs/plays/share/copy.ts +271 -0
- package/dist/bundling-sources/shared_libs/plays/share/metrics.ts +140 -0
- package/dist/bundling-sources/shared_libs/plays/share/public-read-model.ts +86 -0
- package/dist/bundling-sources/shared_libs/plays/share/seo-quality.ts +181 -0
- package/dist/bundling-sources/shared_libs/plays/share/static-pipeline-summary.ts +658 -0
- package/dist/bundling-sources/shared_libs/plays/share/types.ts +197 -0
- package/dist/bundling-sources/shared_libs/plays/sheet-filter.ts +347 -0
- package/dist/bundling-sources/shared_libs/plays/slack-hitl-timeout-error.ts +37 -0
- package/dist/bundling-sources/shared_libs/plays/standard-webhooks.ts +117 -0
- package/dist/bundling-sources/shared_libs/plays/static-pipeline.ts +6 -1
- package/dist/bundling-sources/shared_libs/plays/status-result.ts +388 -0
- package/dist/bundling-sources/shared_libs/plays/tool-result-types.ts +1 -1
- package/dist/bundling-sources/shared_libs/plays/transform-factor-structure.ts +377 -0
- package/dist/bundling-sources/shared_libs/plays/webhook-payload.ts +113 -0
- package/dist/bundling-sources/shared_libs/product-notifications/configuration.ts +7 -1
- package/dist/bundling-sources/shared_libs/product-notifications/default-policy.ts +3 -2
- package/dist/bundling-sources/shared_libs/product-notifications/overflow.ts +18 -0
- package/dist/bundling-sources/shared_libs/product-notifications/rolling-limit.ts +24 -0
- package/dist/bundling-sources/shared_libs/product-notifications/slack-delivery.ts +624 -0
- package/dist/bundling-sources/shared_libs/product-notifications/slack-payload.ts +140 -0
- package/dist/bundling-sources/shared_libs/product-notifications/slack-transport.ts +75 -0
- package/dist/bundling-sources/shared_libs/runtime-env.ts +6 -0
- package/dist/bundling-sources/shared_libs/security/email-blacklist.ts +42 -0
- package/dist/bundling-sources/shared_libs/security/header-safe-env.ts +10 -3
- package/dist/bundling-sources/shared_libs/security/slack-sanitize.ts +133 -0
- package/dist/cli/getting-started.d.mts +52 -0
- package/dist/cli/getting-started.d.ts +52 -0
- package/dist/cli/getting-started.js +65 -0
- package/dist/cli/getting-started.mjs +36 -0
- package/dist/cli/index.d.mts +9 -0
- package/dist/cli/index.d.ts +9 -0
- package/dist/cli/index.js +895 -407
- package/dist/cli/index.mjs +893 -414
- package/dist/{compiler-manifest-DsLbQaew.d.mts → compiler-manifest-x14KL83E.d.mts} +4 -2
- package/dist/{compiler-manifest-DsLbQaew.d.ts → compiler-manifest-x14KL83E.d.ts} +4 -2
- package/dist/index.d.mts +124 -154
- package/dist/index.d.ts +124 -154
- package/dist/index.js +132 -28
- package/dist/index.mjs +132 -28
- package/dist/install-integrity.json +182 -2
- package/dist/monitor-fleet-contract.d.mts +131 -0
- package/dist/monitor-fleet-contract.d.ts +131 -0
- package/dist/monitor-fleet-contract.js +406 -0
- package/dist/monitor-fleet-contract.mjs +367 -0
- package/dist/plays/bundle-play-file.d.mts +2 -2
- package/dist/plays/bundle-play-file.d.ts +2 -2
- package/dist/release.d.mts +255 -0
- package/dist/release.d.ts +255 -0
- package/dist/release.js +210 -0
- package/dist/release.mjs +183 -0
- package/package.json +21 -4
|
@@ -0,0 +1,897 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared constants and pure helpers for the `absurd` play-runtime scheduler
|
|
3
|
+
* profile — imported by both the scheduler backend (`absurd.ts`, submit side)
|
|
4
|
+
* and the durable task handler (`absurd-runtime-worker.ts`, worker side).
|
|
5
|
+
*
|
|
6
|
+
* This module is greenfield and does not import legacy scheduler modules. The
|
|
7
|
+
* battle-tested Postgres run-state store
|
|
8
|
+
* (`postgres.ts`) remains the source of truth for launch rows, attempts,
|
|
9
|
+
* receipts, signals, and terminal state; `absurd-shared` only adds the
|
|
10
|
+
* absurd-specific wiring (queue, event names, checkpoint boundary shaping).
|
|
11
|
+
*/
|
|
12
|
+
import { createHash } from 'node:crypto';
|
|
13
|
+
import type {
|
|
14
|
+
PlayCheckpoint,
|
|
15
|
+
PlayExecutionEvent,
|
|
16
|
+
PlayRowUpdate,
|
|
17
|
+
} from '../ctx-types';
|
|
18
|
+
import type { PlayExecutionSuspension } from '../suspension';
|
|
19
|
+
import type { PlayRunLedgerEvent } from '../run-ledger';
|
|
20
|
+
import type { PlaySchedulerSubmitInput } from '../scheduler-backend';
|
|
21
|
+
import type { WorkerRuntimeApiContext } from '../app-runtime-api';
|
|
22
|
+
import { RUNTIME_CAPACITY_POLICY } from '../runtime-capacity-policy';
|
|
23
|
+
import { assertKnownResolvedBoundary } from '../checkpoint-boundaries';
|
|
24
|
+
import type { Absurd, Log, RetryStrategy } from '@deepline/absurd/sdk';
|
|
25
|
+
import { isIsolatedRuntimeSchedulerSchema } from '../runtime-scheduler-topology';
|
|
26
|
+
import {
|
|
27
|
+
ensureAbsurdSchema,
|
|
28
|
+
failPostgresSchedulerScheduledChildWaits,
|
|
29
|
+
type PostgresSchedulerWorkerOnceResult,
|
|
30
|
+
} from './postgres';
|
|
31
|
+
import { createAbsurdForRuntimeScheduler } from './absurd-pool';
|
|
32
|
+
import {
|
|
33
|
+
RUNTIME_SCHEDULER_CONTROL_PLANE_MAX_CLIENTS,
|
|
34
|
+
resolveWorkflowRuntimePostgresUrl,
|
|
35
|
+
withRuntimeSchedulerClient,
|
|
36
|
+
type RuntimeSchedulerPoolOptions,
|
|
37
|
+
} from './postgres-pool';
|
|
38
|
+
import { runtimeApiBaseUrlFromLaunch } from './runtime-api-origin';
|
|
39
|
+
import {
|
|
40
|
+
ABSURD_DEV_RELEASE_PREFIX,
|
|
41
|
+
ABSURD_PROD_DEFAULT_SCHEMA,
|
|
42
|
+
ABSURD_QUEUE_SUFFIX_ENV,
|
|
43
|
+
ABSURD_SCHEDULER_SCHEMA_ENV,
|
|
44
|
+
absurdDevReleaseIdForQueueSuffix,
|
|
45
|
+
resolveAbsurdQueueSuffix,
|
|
46
|
+
resolveAbsurdQueueSuffixForSchema,
|
|
47
|
+
} from './absurd-queue-suffix';
|
|
48
|
+
|
|
49
|
+
export {
|
|
50
|
+
ABSURD_PROD_DEFAULT_SCHEMA,
|
|
51
|
+
ABSURD_QUEUE_SUFFIX_ENV,
|
|
52
|
+
ABSURD_SCHEDULER_SCHEMA_ENV,
|
|
53
|
+
resolveAbsurdQueueSuffix,
|
|
54
|
+
resolveAbsurdQueueSuffixForSchema,
|
|
55
|
+
} from './absurd-queue-suffix';
|
|
56
|
+
|
|
57
|
+
type EnvMap = Record<string, string | undefined>;
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Per-worktree ENGINE-table isolation — option (a): queue-NAME scoping.
|
|
61
|
+
*
|
|
62
|
+
* The vendored Absurd SQL hardcodes the schema literal `absurd`, and dev:v2
|
|
63
|
+
* points every worktree at the SAME docker Postgres (`deepline_dev`), so all
|
|
64
|
+
* worktrees write into one shared `absurd` schema. Absurd partitions its state
|
|
65
|
+
* BY QUEUE: each queue owns its own `t_/r_/c_/e_/w_` tables
|
|
66
|
+
* (`absurd.t_<queue>`, `absurd.r_<queue>`, ...). Two worktrees with DISTINCT
|
|
67
|
+
* queue names therefore touch DISJOINT tables inside the shared schema. We scope
|
|
68
|
+
* the queue NAMES by a per-worktree suffix instead of rewriting the schema
|
|
69
|
+
* literal (option (b)), which would mean patching the whole SQL blob and every
|
|
70
|
+
* cross-queue `absurd.` function reference — far riskier for zero extra benefit.
|
|
71
|
+
*
|
|
72
|
+
* The suffix comes from a DEDICATED env var `DEEPLINE_ABSURD_QUEUE_SUFFIX` (set
|
|
73
|
+
* per worktree by dev-v2-runtime.sh), NOT from DEEPLINE_RUNTIME_SCHEDULER_SCHEMA.
|
|
74
|
+
* Keeping it separate means unit tests — which never set it, and which mutate
|
|
75
|
+
* the scheduler-schema env — keep the canonical base name `runs`
|
|
76
|
+
* and stay hermetic. Unset (tests, prod default) => base names, so nothing
|
|
77
|
+
* changes anywhere the suffix is absent.
|
|
78
|
+
*
|
|
79
|
+
* Agreement across processes: the backend (submit/cancel/signal) and the worker
|
|
80
|
+
* (task registration + poll loop) both import THESE constants, so within a
|
|
81
|
+
* process they are identical, and across the app + worker processes they agree
|
|
82
|
+
* because dev:v2 exports the same suffix into both environments. Queue metrics
|
|
83
|
+
* read the run-state store's `attempts` table, isolated by
|
|
84
|
+
* DEEPLINE_RUNTIME_SCHEDULER_SCHEMA, so they never touch these queue-name tables.
|
|
85
|
+
*/
|
|
86
|
+
/**
|
|
87
|
+
* Worker release identity env var. Baked into the Fly image at build time as an
|
|
88
|
+
* ARG/ENV (see the Fly Dockerfile), so a given machine's process knows exactly
|
|
89
|
+
* which release lane it belongs to. Absent in dev/unit tests => the dev-collapse
|
|
90
|
+
* rule applies and lane checks pass trivially.
|
|
91
|
+
*/
|
|
92
|
+
export const ABSURD_RELEASE_ID_ENV = 'DEEPLINE_RUNTIME_DEPLOY_VERSION';
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Sentinel release id used everywhere no real deploy version exists (unit tests,
|
|
96
|
+
* plain local `dev:v2` with no worktree suffix, prod default). Maps to the plain
|
|
97
|
+
* base `runs` queue — byte-identical to today.
|
|
98
|
+
*/
|
|
99
|
+
export const ABSURD_DEV_RELEASE_ID = 'dev';
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Prefix stamped on the dev-collapse release id when a worktree/preview queue
|
|
103
|
+
* suffix is active. `dev-<suffix>` deterministically maps back to today's
|
|
104
|
+
* `runs_<suffix>` queue via `absurdQueueNameForRelease`, so local dev is
|
|
105
|
+
* unchanged while the release id still travels on the run for the lane guard.
|
|
106
|
+
*/
|
|
107
|
+
const ABSURD_RUNS_QUEUE_BASE = 'runs';
|
|
108
|
+
|
|
109
|
+
/** True for any release lane derived from the durable Absurd runs queue. */
|
|
110
|
+
export function isAbsurdRunsQueue(queueName: string): boolean {
|
|
111
|
+
return (
|
|
112
|
+
queueName === ABSURD_RUNS_QUEUE_BASE ||
|
|
113
|
+
queueName.startsWith(`${ABSURD_RUNS_QUEUE_BASE}_`)
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Longest queue-name suffix we allow before hashing. Absurd derives engine
|
|
119
|
+
* tables `t_<queue>` / `r_<queue>` (2-char prefixes) from the queue name, and
|
|
120
|
+
* Postgres caps identifiers at 63 bytes, so `runs_` (5) + prefix (2) + suffix
|
|
121
|
+
* must stay under 63. We cap the release-derived suffix well below that and hash
|
|
122
|
+
* any overflow deterministically so two long release ids never collide onto one
|
|
123
|
+
* lane.
|
|
124
|
+
*/
|
|
125
|
+
const ABSURD_RELEASE_QUEUE_SUFFIX_MAX = 40;
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* A worker whose scheduler schema is anything OTHER than the prod default is
|
|
129
|
+
* running an isolated lane topology — a per-PR preview fleet or a per-worktree
|
|
130
|
+
* dev fleet — where the release lane (and therefore the queue it must poll) is
|
|
131
|
+
* NOT the base `runs` queue but `runs_<suffix>`. In that topology the suffix is
|
|
132
|
+
* the SINGLE provisioned lane identity: the submitting app derives it from the
|
|
133
|
+
* per-request schema and the polling worker reads it from its own env, and both
|
|
134
|
+
* MUST agree or every run enqueues onto a queue no worker drains and hangs.
|
|
135
|
+
*
|
|
136
|
+
* This returns true exactly when the worker's schema demands a provisioned
|
|
137
|
+
* suffix. The boot guard uses it to refuse to start (loudly, naming the missing
|
|
138
|
+
* variable) rather than silently fall back to the base `runs` queue — the
|
|
139
|
+
* dev-collapse fallback is safe ONLY when the topology can guarantee all parties
|
|
140
|
+
* the same inputs, which an isolated-schema worker with no suffix cannot.
|
|
141
|
+
* Delegates to the shared topology predicate so the Daytona egress guard and
|
|
142
|
+
* this lane guard can never disagree about what "isolated" means.
|
|
143
|
+
*/
|
|
144
|
+
export function absurdWorkerSchemaRequiresQueueSuffix(
|
|
145
|
+
schedulerSchema: string | null | undefined,
|
|
146
|
+
): boolean {
|
|
147
|
+
return isIsolatedRuntimeSchedulerSchema(schedulerSchema);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Sanitize an arbitrary release id into a queue-name segment: lowercase, collapse
|
|
152
|
+
* non-`[a-z0-9_]` to `_`, trim leading/trailing `_`. Overflow past
|
|
153
|
+
* `ABSURD_RELEASE_QUEUE_SUFFIX_MAX` is truncated AND fingerprinted with a short
|
|
154
|
+
* sha256 tail so two long-but-distinct release ids can never alias onto the same
|
|
155
|
+
* lane. Returns `''` for an empty/garbage input so the caller can fail loudly.
|
|
156
|
+
*/
|
|
157
|
+
function sanitizeReleaseQueueSegment(releaseId: string): string {
|
|
158
|
+
const normalized = releaseId
|
|
159
|
+
.trim()
|
|
160
|
+
.toLowerCase()
|
|
161
|
+
.replace(/[^a-z0-9_]+/g, '_')
|
|
162
|
+
.replace(/^_+|_+$/g, '');
|
|
163
|
+
if (!normalized) return '';
|
|
164
|
+
if (normalized.length <= ABSURD_RELEASE_QUEUE_SUFFIX_MAX) return normalized;
|
|
165
|
+
// Deterministic overflow: keep a readable head, append a hash tail so the full
|
|
166
|
+
// (untruncated) release id disambiguates collisions.
|
|
167
|
+
const hashTail = createHash('sha256')
|
|
168
|
+
.update(normalized)
|
|
169
|
+
.digest('hex')
|
|
170
|
+
.slice(0, 8);
|
|
171
|
+
const headBudget = ABSURD_RELEASE_QUEUE_SUFFIX_MAX - hashTail.length - 1;
|
|
172
|
+
return `${normalized.slice(0, headBudget).replace(/_+$/g, '')}_${hashTail}`;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Resolve THIS worker process's release lane id (the "current" lane). Precedence,
|
|
177
|
+
* matching the dev-collapse rule:
|
|
178
|
+
* 1. queue suffix set (worktree/local dev/preview CI) => `dev-<suffix>`, which
|
|
179
|
+
* maps back to today's `runs_<suffix>` queue — local dev is unchanged.
|
|
180
|
+
* 2. deploy version set (Fly release machine) => sanitized deploy version.
|
|
181
|
+
* 3. neither (unit tests / prod default) => `dev` => plain `runs` queue.
|
|
182
|
+
*/
|
|
183
|
+
export function currentAbsurdReleaseId(env: EnvMap = process.env): string {
|
|
184
|
+
const suffix = resolveAbsurdQueueSuffixFromEnv(env);
|
|
185
|
+
if (suffix) return `${ABSURD_DEV_RELEASE_PREFIX}${suffix}`;
|
|
186
|
+
const deployVersion = env[ABSURD_RELEASE_ID_ENV]?.trim();
|
|
187
|
+
if (deployVersion) {
|
|
188
|
+
const sanitized = sanitizeReleaseQueueSegment(deployVersion);
|
|
189
|
+
if (!sanitized) {
|
|
190
|
+
throw new Error(
|
|
191
|
+
`absurd release id: ${ABSURD_RELEASE_ID_ENV}="${deployVersion}" ` +
|
|
192
|
+
`sanitizes to empty. A deploy version must contain at least one ` +
|
|
193
|
+
`[a-z0-9] character.`,
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
return sanitized;
|
|
197
|
+
}
|
|
198
|
+
return ABSURD_DEV_RELEASE_ID;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Release id as persisted in `runs.launch_json`. Production launches retain the
|
|
203
|
+
* raw registry id, while the worker lane id above is sanitized/hashed to fit an
|
|
204
|
+
* Absurd queue name. Preview suffixes still use the dev-collapse id because the
|
|
205
|
+
* suffix outranks a baked deploy version in both the app and worker topology.
|
|
206
|
+
*/
|
|
207
|
+
export function currentAbsurdLaunchReleaseId(
|
|
208
|
+
env: EnvMap = process.env,
|
|
209
|
+
): string {
|
|
210
|
+
const suffix = resolveAbsurdQueueSuffixFromEnv(env);
|
|
211
|
+
if (suffix) return `${ABSURD_DEV_RELEASE_PREFIX}${suffix}`;
|
|
212
|
+
return env[ABSURD_RELEASE_ID_ENV]?.trim() || ABSURD_DEV_RELEASE_ID;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* The release lane queue for a given release id. This is the ONE queue a run and
|
|
217
|
+
* all of its descendants/wakeups ride for its whole life.
|
|
218
|
+
*
|
|
219
|
+
* Dev-collapse mapping (byte-identical to today's suffix behavior):
|
|
220
|
+
* - `dev` => `runs` (plain base queue)
|
|
221
|
+
* - `dev-<suffix>` => `runs_<suffix>` (existing worktree/preview queue)
|
|
222
|
+
* - `<release>` => `runs_<sanitized-release>` (a real Fly release lane)
|
|
223
|
+
*
|
|
224
|
+
* Loud failure: an empty/whitespace release id is a programming error (the
|
|
225
|
+
* resolver never returns one), so we throw rather than silently landing work on
|
|
226
|
+
* the base queue where no lane worker may be draining.
|
|
227
|
+
*/
|
|
228
|
+
export function absurdQueueNameForRelease(releaseId: string): string {
|
|
229
|
+
const trimmed = releaseId?.trim();
|
|
230
|
+
if (!trimmed) {
|
|
231
|
+
throw new Error(
|
|
232
|
+
'absurdQueueNameForRelease requires a non-empty release id. ' +
|
|
233
|
+
'Resolve it via currentAbsurdReleaseId() or the launch row.',
|
|
234
|
+
);
|
|
235
|
+
}
|
|
236
|
+
if (trimmed === ABSURD_DEV_RELEASE_ID) {
|
|
237
|
+
return absurdQueueName(ABSURD_RUNS_QUEUE_BASE, '');
|
|
238
|
+
}
|
|
239
|
+
if (trimmed.startsWith(ABSURD_DEV_RELEASE_PREFIX)) {
|
|
240
|
+
const suffix = normalizeQueueSuffixValue(
|
|
241
|
+
trimmed.slice(ABSURD_DEV_RELEASE_PREFIX.length),
|
|
242
|
+
);
|
|
243
|
+
return absurdQueueName(ABSURD_RUNS_QUEUE_BASE, suffix);
|
|
244
|
+
}
|
|
245
|
+
const segment = sanitizeReleaseQueueSegment(trimmed);
|
|
246
|
+
if (!segment) {
|
|
247
|
+
throw new Error(
|
|
248
|
+
`absurdQueueNameForRelease: release id "${trimmed}" sanitizes to empty.`,
|
|
249
|
+
);
|
|
250
|
+
}
|
|
251
|
+
return absurdQueueName(ABSURD_RUNS_QUEUE_BASE, segment);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Loud lane guard used belt-and-suspenders when a worker executes a claimed run.
|
|
256
|
+
* The monogamous poll loop makes a mismatch structurally unreachable; this exists
|
|
257
|
+
* to catch operator error (e.g. a lane machine pointed at the wrong queue). The
|
|
258
|
+
* message is operator-actionable: it names both ids and the remediation.
|
|
259
|
+
*/
|
|
260
|
+
export function assertAbsurdReleaseLaneMatches(input: {
|
|
261
|
+
runId: string;
|
|
262
|
+
runReleaseId: string;
|
|
263
|
+
workerReleaseId: string;
|
|
264
|
+
}): void {
|
|
265
|
+
if (input.runReleaseId === input.workerReleaseId) return;
|
|
266
|
+
// A release id reaches this guard in one of two forms: the raw deploy slug
|
|
267
|
+
// stamped onto a launch from the release registry (`main-<sha>`), or the
|
|
268
|
+
// sanitized lane id a worker reports via `currentAbsurdReleaseId()`
|
|
269
|
+
// (`main_<sha>_<hash>`). Both map to the SAME lane queue, which is the real
|
|
270
|
+
// claim boundary — a run lands on, and is claimed off, its queue regardless
|
|
271
|
+
// of which spelling named it. Compare derived queues so the two spellings of
|
|
272
|
+
// one lane match. Without this, a production-length id (whose sanitized form
|
|
273
|
+
// diverges from the raw slug once it overflows the queue-segment budget)
|
|
274
|
+
// fails this guard on EVERY attempt even though the run correctly landed on
|
|
275
|
+
// this worker's queue; short dev/preview ids never overflow, so raw ==
|
|
276
|
+
// sanitized and they slipped past this guard in CI.
|
|
277
|
+
if (
|
|
278
|
+
absurdQueueNameForRelease(input.runReleaseId) ===
|
|
279
|
+
absurdQueueNameForRelease(input.workerReleaseId)
|
|
280
|
+
) {
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
throw new Error(
|
|
284
|
+
`absurd release lane mismatch for run ${input.runId}: the run was launched ` +
|
|
285
|
+
`on release "${input.runReleaseId}" but this worker serves release ` +
|
|
286
|
+
`"${input.workerReleaseId}". A run must finish on the release that ` +
|
|
287
|
+
`launched it. Remediation: start a lane machine for "${input.runReleaseId}" ` +
|
|
288
|
+
`(queue ${absurdQueueNameForRelease(input.runReleaseId)}).`,
|
|
289
|
+
);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Seconds a foreign, non-adoptable task is parked for before the owning lane
|
|
294
|
+
* gets another chance at it. Matches the historical release-guard deferral so
|
|
295
|
+
* the observable retry cadence is unchanged.
|
|
296
|
+
*/
|
|
297
|
+
export const ABSURD_LANE_ADOPTION_DEFER_SECONDS = 30;
|
|
298
|
+
|
|
299
|
+
/** Absurd worker-id suffix that opts a poll loop into cross-lane claims. */
|
|
300
|
+
export const ABSURD_CONTINUATION_WORKER_SUFFIX = ':continuations';
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Worker id for an adoption poll loop. The `:continuations` suffix is load
|
|
304
|
+
* bearing: vendored `claim_task` patch (g) admits such a worker only for runs
|
|
305
|
+
* still `sleeping` or carrying a non-null event-wake payload, so a fresh or
|
|
306
|
+
* ordinary retry-pending task on the adopted queue is never handed to it.
|
|
307
|
+
*/
|
|
308
|
+
export function absurdContinuationWorkerId(input: {
|
|
309
|
+
workerBase: string;
|
|
310
|
+
queue: string;
|
|
311
|
+
}): string {
|
|
312
|
+
return `${input.workerBase}:${input.queue}${ABSURD_CONTINUATION_WORKER_SUFFIX}`;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
export type AbsurdReleaseLaneDisposition =
|
|
316
|
+
/** The run belongs to this worker's own lane. */
|
|
317
|
+
| { kind: 'own' }
|
|
318
|
+
/** A retired lane's parked durable wait resumes on this newer release. */
|
|
319
|
+
| { kind: 'adopt'; adoptedReleaseId: string }
|
|
320
|
+
/** Foreign, not adoptable: park it without spending a task attempt. */
|
|
321
|
+
| { kind: 'defer'; reason: string; delaySeconds: number }
|
|
322
|
+
/** Foreign lane that still claims — operator error, fail loudly. */
|
|
323
|
+
| { kind: 'refuse' };
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Decide what this worker may do with a claimed run, given the durable lane
|
|
327
|
+
* facts read from the scheduler Postgres.
|
|
328
|
+
*
|
|
329
|
+
* This is the adapter half of the adoption rule (lane handoff). The engine's
|
|
330
|
+
* `:continuations` fence already stops most cross-lane claims; this decides the
|
|
331
|
+
* cases the engine cannot see, because `sleeping` in the engine covers durable
|
|
332
|
+
* suspensions, retry backoff, and capacity deferrals alike.
|
|
333
|
+
*
|
|
334
|
+
* `laneClaiming` and `parkedInAdoptableWait` come from
|
|
335
|
+
* `readPostgresSchedulerRunLaneState`, never from a timer or a machine count.
|
|
336
|
+
*/
|
|
337
|
+
export function resolveAbsurdReleaseLaneDisposition(input: {
|
|
338
|
+
runId: string;
|
|
339
|
+
runReleaseId: string;
|
|
340
|
+
workerReleaseId: string;
|
|
341
|
+
/** Does the run's OWN lane still claim work off its queue? */
|
|
342
|
+
laneClaiming: boolean;
|
|
343
|
+
/** Is the run parked in a sleep/integration-event wait right now? */
|
|
344
|
+
parkedInAdoptableWait: boolean;
|
|
345
|
+
}): AbsurdReleaseLaneDisposition {
|
|
346
|
+
if (
|
|
347
|
+
input.runReleaseId === input.workerReleaseId ||
|
|
348
|
+
absurdQueueNameForRelease(input.runReleaseId) ===
|
|
349
|
+
absurdQueueNameForRelease(input.workerReleaseId)
|
|
350
|
+
) {
|
|
351
|
+
return { kind: 'own' };
|
|
352
|
+
}
|
|
353
|
+
// A lane that still claims owns every one of its rows. Reaching this branch
|
|
354
|
+
// means a machine is polling a queue it does not serve; stay loud.
|
|
355
|
+
if (input.laneClaiming) return { kind: 'refuse' };
|
|
356
|
+
if (!input.parkedInAdoptableWait) {
|
|
357
|
+
return {
|
|
358
|
+
kind: 'defer',
|
|
359
|
+
reason:
|
|
360
|
+
`Run ${input.runId} rides retired release lane "${input.runReleaseId}" ` +
|
|
361
|
+
`but is not parked in an adoptable durable wait. Ordinary queued and ` +
|
|
362
|
+
`running work is never adopted; leaving it for its owning lane.`,
|
|
363
|
+
delaySeconds: ABSURD_LANE_ADOPTION_DEFER_SECONDS,
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
return { kind: 'adopt', adoptedReleaseId: input.runReleaseId };
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
/**
|
|
370
|
+
* Adoption target queues for a worker, given the release ids the scheduler
|
|
371
|
+
* reported as adoptable. Two release-id spellings of one lane collapse to one
|
|
372
|
+
* queue, and this worker's own queue is never attached twice — which is why a
|
|
373
|
+
* dev-collapse topology (every run stamped `dev`) attaches nothing at all.
|
|
374
|
+
*/
|
|
375
|
+
export function absurdAdoptionQueuesForReleases(input: {
|
|
376
|
+
workerReleaseId: string;
|
|
377
|
+
adoptableReleaseIds: readonly string[];
|
|
378
|
+
}): string[] {
|
|
379
|
+
const ownQueue = absurdQueueNameForRelease(input.workerReleaseId);
|
|
380
|
+
const queues = new Set<string>();
|
|
381
|
+
for (const releaseId of input.adoptableReleaseIds) {
|
|
382
|
+
const trimmed = releaseId?.trim();
|
|
383
|
+
if (!trimmed) continue;
|
|
384
|
+
const queue = absurdQueueNameForRelease(trimmed);
|
|
385
|
+
if (queue === ownQueue) continue;
|
|
386
|
+
queues.add(queue);
|
|
387
|
+
}
|
|
388
|
+
return [...queues].sort();
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
/**
|
|
392
|
+
* Env-driven queue suffix read (worker/dev:v2). Extracted so
|
|
393
|
+
* `currentAbsurdReleaseId` can take an explicit env for hermetic tests while
|
|
394
|
+
* `resolveAbsurdQueueSuffix` keeps its process.env signature.
|
|
395
|
+
*/
|
|
396
|
+
function resolveAbsurdQueueSuffixFromEnv(env: EnvMap): string {
|
|
397
|
+
return resolveAbsurdQueueSuffix(env);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
function normalizeQueueSuffixValue(raw: string): string {
|
|
401
|
+
return raw
|
|
402
|
+
.toLowerCase()
|
|
403
|
+
.replace(/[^a-z0-9_]+/g, '_')
|
|
404
|
+
.replace(/^_+|_+$/g, '')
|
|
405
|
+
.slice(0, 32);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
function absurdQueueName(base: string, suffix: string): string {
|
|
409
|
+
return suffix ? `${base}_${suffix}` : base;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/**
|
|
413
|
+
* Resolve the per-worktree/per-PR queue suffix for a submit/signal/cancel that
|
|
414
|
+
* carries an EXPLICIT per-request scheduler schema (threaded from the CLI via
|
|
415
|
+
* the runtime-scheduler-schema header → `createAbsurdSchedulerBackend({schema})`).
|
|
416
|
+
*
|
|
417
|
+
* Why this exists: the module-level `ABSURD_RUNS_QUEUE` constants read the
|
|
418
|
+
* `DEEPLINE_ABSURD_QUEUE_SUFFIX` env var ONCE at import. That works for the Fly
|
|
419
|
+
* worker (env set as a secret) and dev:v2 (env exported into every process),
|
|
420
|
+
* but NOT for the shared hosted preview app, which learns the per-PR schema
|
|
421
|
+
* only per-request and has no such env — so its enqueue would land on the base
|
|
422
|
+
* `runs` queue while the worker drains `runs_<suffix>`, and every run hangs.
|
|
423
|
+
*
|
|
424
|
+
* Precedence: the env suffix applies when the explicit schema is native to the
|
|
425
|
+
* process. A different explicit schema identifies a remote runtime, so derive
|
|
426
|
+
* the SAME suffix its worker uses — sha256(schema)[:12]. Prod-default or an
|
|
427
|
+
* unset schema retains the process suffix when present.
|
|
428
|
+
*/
|
|
429
|
+
/**
|
|
430
|
+
* Normalize an explicit queue suffix (from the request layer via
|
|
431
|
+
* `resolveAbsurdQueueSuffixForSchema`). An empty/undefined value falls back to
|
|
432
|
+
* the process env suffix — so the Fly worker and dev:v2 (env set) keep their
|
|
433
|
+
* suffixed queues, direct backend construction in unit tests (no suffix, env
|
|
434
|
+
* cleared) stays on base names, and the shared hosted app (env absent) uses the
|
|
435
|
+
* explicit per-request suffix threaded down from `lazy.ts`.
|
|
436
|
+
*/
|
|
437
|
+
function effectiveAbsurdQueueSuffix(explicit?: string | null): string {
|
|
438
|
+
const normalized = explicit?.trim();
|
|
439
|
+
if (normalized) return normalizeQueueSuffixValue(normalized);
|
|
440
|
+
return resolveAbsurdQueueSuffix();
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
/**
|
|
444
|
+
* Map an effective queue suffix to a release id under the dev-collapse rule.
|
|
445
|
+
* Empty suffix => the process default release (`dev`, or the baked deploy
|
|
446
|
+
* version on a Fly lane machine); a non-empty suffix => `dev-<suffix>`, which
|
|
447
|
+
* `absurdQueueNameForRelease` maps back to today's `runs_<suffix>` queue. This
|
|
448
|
+
* is how the shared hosted preview app (which threads an explicit per-PR suffix
|
|
449
|
+
* derived from the schema) stamps the SAME release id the Fly preview worker
|
|
450
|
+
* serves, without touching local-dev behavior.
|
|
451
|
+
*/
|
|
452
|
+
export function absurdReleaseIdForSuffix(
|
|
453
|
+
explicitSuffix?: string | null,
|
|
454
|
+
env: EnvMap = process.env,
|
|
455
|
+
): string {
|
|
456
|
+
const suffix = effectiveAbsurdQueueSuffix(explicitSuffix);
|
|
457
|
+
if (suffix) return absurdDevReleaseIdForQueueSuffix(suffix);
|
|
458
|
+
return currentAbsurdReleaseId(env);
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
/**
|
|
462
|
+
* Resolve the release lane a launch row belongs to. Enforce-when-exists /
|
|
463
|
+
* allow-when-absent: a run stamped with `absurdReleaseId` rides its lane; a
|
|
464
|
+
* pre-release launch row (no stamp — the only compat allowance in this PR) falls
|
|
465
|
+
* back to THIS process's dev-collapse id so preview/burn-in continuity holds.
|
|
466
|
+
*/
|
|
467
|
+
export function absurdReleaseIdForLaunch(
|
|
468
|
+
launch: { absurdReleaseId?: string | null },
|
|
469
|
+
env: EnvMap = process.env,
|
|
470
|
+
): string {
|
|
471
|
+
const stamped = launch.absurdReleaseId?.trim();
|
|
472
|
+
if (stamped) return stamped;
|
|
473
|
+
return currentAbsurdReleaseId(env);
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
/** The release lane queue a launch row rides for its whole lifecycle. */
|
|
477
|
+
export function absurdQueueNameForLaunch(
|
|
478
|
+
launch: { absurdReleaseId?: string | null },
|
|
479
|
+
env: EnvMap = process.env,
|
|
480
|
+
): string {
|
|
481
|
+
return absurdQueueNameForRelease(absurdReleaseIdForLaunch(launch, env));
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
/** Shared run queue for top-level plays and their parked resumptions. */
|
|
485
|
+
export function absurdRunsQueueForSuffix(suffix?: string | null): string {
|
|
486
|
+
return absurdQueueName(
|
|
487
|
+
ABSURD_RUNS_QUEUE_BASE,
|
|
488
|
+
effectiveAbsurdQueueSuffix(suffix),
|
|
489
|
+
);
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
/** Compatibility shape for admin callers that enumerate scheduler queues. */
|
|
493
|
+
export function absurdAllQueuesForSuffix(suffix?: string | null): string[] {
|
|
494
|
+
return [absurdRunsQueueForSuffix(suffix)];
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
export const ABSURD_RUNS_QUEUE = absurdRunsQueueForSuffix();
|
|
498
|
+
|
|
499
|
+
/** Durable task for one top-level play run. */
|
|
500
|
+
export {
|
|
501
|
+
ABSURD_PLAY_RUN_MAX_ATTEMPTS,
|
|
502
|
+
ABSURD_PLAY_RUN_RETRY_STRATEGY,
|
|
503
|
+
ABSURD_PLAY_RUN_TASK,
|
|
504
|
+
type AbsurdPlayRunTaskParams,
|
|
505
|
+
} from './absurd-play-run-task';
|
|
506
|
+
/** Worker-owned cleanup for a Daytona sandbox in this release lane. */
|
|
507
|
+
export const ABSURD_DAYTONA_SANDBOX_CLEANUP_TASK = 'daytona-sandbox-cleanup';
|
|
508
|
+
|
|
509
|
+
/** Terminal engine rows are diagnostic scratch state, not the durable run
|
|
510
|
+
* ledger. Keep a six-hour incident window, then let the scaler's bounded
|
|
511
|
+
* maintenance pass remove them so claims do not rescan an ever-growing table. */
|
|
512
|
+
export const ABSURD_RUNS_CLEANUP_TTL = '6 hours';
|
|
513
|
+
export const ABSURD_RUNS_CLEANUP_LIMIT = 1_000;
|
|
514
|
+
|
|
515
|
+
export type AbsurdDaytonaSandboxCleanupTaskParams = {
|
|
516
|
+
runId: string;
|
|
517
|
+
sandboxId: string;
|
|
518
|
+
};
|
|
519
|
+
|
|
520
|
+
/** Schema is `string | undefined` (never null) so it is directly assignable to
|
|
521
|
+
* the Postgres store's `Pick<PostgresSchedulerOptions, 'schema'>`. */
|
|
522
|
+
export type AbsurdSchedulerOptions = {
|
|
523
|
+
url?: string | null;
|
|
524
|
+
schema?: string;
|
|
525
|
+
};
|
|
526
|
+
|
|
527
|
+
export type AbsurdRuntimeResult = {
|
|
528
|
+
ok: true;
|
|
529
|
+
runId: string;
|
|
530
|
+
schedulerStatus: 'completed' | 'failed' | 'idle';
|
|
531
|
+
output?: unknown;
|
|
532
|
+
error?: string;
|
|
533
|
+
};
|
|
534
|
+
|
|
535
|
+
// All queues in one worker share this single process pool. Scheduler queries
|
|
536
|
+
// are short and batched; execution concurrency belongs to Daytona/Fly slots,
|
|
537
|
+
// not to a growing set of database clients. Keeping this at two makes a
|
|
538
|
+
// horizontally scaled fleet predictable against the shared PgBouncer budget.
|
|
539
|
+
export const ABSURD_RUNTIME_POOL_MAX_CLIENTS =
|
|
540
|
+
RUNTIME_SCHEDULER_CONTROL_PLANE_MAX_CLIENTS;
|
|
541
|
+
|
|
542
|
+
/**
|
|
543
|
+
* A Vercel invocation has its own module cache and pool. Letting every
|
|
544
|
+
* short-lived launch hold the worker-sized cap multiplies the shared
|
|
545
|
+
* PgBouncer budget under a burst of webhook deliveries. A launch submits one
|
|
546
|
+
* durable transaction at a time, so one checkout preserves correctness while
|
|
547
|
+
* the long-lived Fly worker keeps its two-client control-plane budget.
|
|
548
|
+
*/
|
|
549
|
+
export const ABSURD_SERVERLESS_POOL_MAX_CLIENTS = 1;
|
|
550
|
+
|
|
551
|
+
/**
|
|
552
|
+
* Resolve the runtime scheduler Postgres pool options for the absurd control
|
|
553
|
+
* plane. Loud failure over silent fallback: the absurd worker and scheduler
|
|
554
|
+
* only run under Node where `WORKFLOW_RUNTIME_POSTGRES` is configured.
|
|
555
|
+
*/
|
|
556
|
+
export function resolveAbsurdPoolOptions(
|
|
557
|
+
explicitUrl?: string | null,
|
|
558
|
+
): RuntimeSchedulerPoolOptions {
|
|
559
|
+
const url = explicitUrl?.trim() || resolveWorkflowRuntimePostgresUrl();
|
|
560
|
+
if (!url) {
|
|
561
|
+
throw new Error(
|
|
562
|
+
'absurd scheduler backend requires WORKFLOW_RUNTIME_POSTGRES (runtime ' +
|
|
563
|
+
'scheduler Postgres URL). Refusing to fall back to local execution.',
|
|
564
|
+
);
|
|
565
|
+
}
|
|
566
|
+
return {
|
|
567
|
+
url,
|
|
568
|
+
applicationName: 'deepline-absurd-runtime',
|
|
569
|
+
maxClients: process.env.VERCEL?.trim()
|
|
570
|
+
? ABSURD_SERVERLESS_POOL_MAX_CLIENTS
|
|
571
|
+
: ABSURD_RUNTIME_POOL_MAX_CLIENTS,
|
|
572
|
+
};
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
export function normalizeAbsurdSchedulerOptions(
|
|
576
|
+
options: { url?: string | null; schema?: string | null } = {},
|
|
577
|
+
): AbsurdSchedulerOptions {
|
|
578
|
+
const url = options.url?.trim() || null;
|
|
579
|
+
const schema = options.schema?.trim();
|
|
580
|
+
return {
|
|
581
|
+
...(url ? { url } : {}),
|
|
582
|
+
...(schema ? { schema } : {}),
|
|
583
|
+
};
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
const absurdLog: Log = {
|
|
587
|
+
log: (...args) => console.log('[absurd-runtime]', ...args),
|
|
588
|
+
info: (...args) => console.info('[absurd-runtime]', ...args),
|
|
589
|
+
warn: (...args) => console.warn('[absurd-runtime]', ...args),
|
|
590
|
+
error: (...args) => console.error('[absurd-runtime]', ...args),
|
|
591
|
+
};
|
|
592
|
+
|
|
593
|
+
/**
|
|
594
|
+
* Construct an Absurd client on the hardened runtime scheduler pool. The
|
|
595
|
+
* client's `queueName` is the queue its `startWorker` poll loop drains and the
|
|
596
|
+
* default target for `emitEvent`/`spawn` without an explicit queue; defaults to
|
|
597
|
+
* the root `runs` queue. Task registrations are attached by the worker.
|
|
598
|
+
*/
|
|
599
|
+
export function createAbsurdRuntimeClient(
|
|
600
|
+
poolOptions: RuntimeSchedulerPoolOptions,
|
|
601
|
+
queueName: string = ABSURD_RUNS_QUEUE,
|
|
602
|
+
): Absurd {
|
|
603
|
+
return createAbsurdForRuntimeScheduler(poolOptions, {
|
|
604
|
+
queueName,
|
|
605
|
+
log: absurdLog,
|
|
606
|
+
});
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
const absurdReadyPromises = new Map<string, Promise<void>>();
|
|
610
|
+
|
|
611
|
+
/**
|
|
612
|
+
* Ensure, once per process (keyed by pool url + schema): the vendored absurd
|
|
613
|
+
* schema and the shared queue with its
|
|
614
|
+
* policies. Idempotent and safe under concurrent submits — the memoized promise
|
|
615
|
+
* is shared; a failure evicts the cache so the next caller retries loudly.
|
|
616
|
+
*/
|
|
617
|
+
export function ensureAbsurdRuntimeReady(
|
|
618
|
+
poolOptions: RuntimeSchedulerPoolOptions,
|
|
619
|
+
schedulerOptions: AbsurdSchedulerOptions = {},
|
|
620
|
+
queueSuffix?: string | null,
|
|
621
|
+
releaseId?: string | null,
|
|
622
|
+
): Promise<void> {
|
|
623
|
+
const schema = normalizeAbsurdSchedulerOptions(schedulerOptions).schema ?? '';
|
|
624
|
+
// Queue name derivation: an explicit release id (from the launch envelope or
|
|
625
|
+
// the worker's own `currentAbsurdReleaseId()`) wins — that is the ONE lane the
|
|
626
|
+
// run rides. Otherwise fall back to the explicit per-request suffix (threaded
|
|
627
|
+
// from `lazy.ts`) so the shared hosted app still creates the SAME suffixed
|
|
628
|
+
// queue the Fly worker drains, and worker/dev:v2 with an env suffix are
|
|
629
|
+
// unchanged. Under dev-collapse the release id `dev-<suffix>` maps back to
|
|
630
|
+
// exactly `runs_<suffix>`, so this is byte-identical to today for local dev.
|
|
631
|
+
const runsQueue = releaseId?.trim()
|
|
632
|
+
? absurdQueueNameForRelease(releaseId)
|
|
633
|
+
: absurdRunsQueueForSuffix(queueSuffix);
|
|
634
|
+
const key = `${poolOptions.url}::${schema}::${runsQueue}`;
|
|
635
|
+
const existing = absurdReadyPromises.get(key);
|
|
636
|
+
if (existing) return existing;
|
|
637
|
+
const promise = (async () => {
|
|
638
|
+
await withRuntimeSchedulerClient(poolOptions, (client) =>
|
|
639
|
+
ensureAbsurdSchema(client),
|
|
640
|
+
);
|
|
641
|
+
// Hard-cutover cleanup: an old worker may have persisted a run waiting
|
|
642
|
+
// on a scheduled ctx.runPlay child. This runtime intentionally has no such
|
|
643
|
+
// scheduler anymore, so make the affected run visibly failed and emit
|
|
644
|
+
// its terminal outbox fact rather than leaving it parked forever.
|
|
645
|
+
await withRuntimeSchedulerClient(poolOptions, (client) =>
|
|
646
|
+
failPostgresSchedulerScheduledChildWaits(client, schedulerOptions),
|
|
647
|
+
);
|
|
648
|
+
const absurd = createAbsurdRuntimeClient(poolOptions, runsQueue);
|
|
649
|
+
const existingQueues = new Set(await absurd.listQueues());
|
|
650
|
+
if (!existingQueues.has(runsQueue)) {
|
|
651
|
+
await absurd.createQueue(runsQueue);
|
|
652
|
+
}
|
|
653
|
+
// Idempotent upsert: parked runs hold no slot; fair claim ordering chooses
|
|
654
|
+
// the next runnable work without imposing an account-level claim cap.
|
|
655
|
+
await absurd.setQueuePolicy(runsQueue, {
|
|
656
|
+
cleanupTtl: ABSURD_RUNS_CLEANUP_TTL,
|
|
657
|
+
cleanupLimit: ABSURD_RUNS_CLEANUP_LIMIT,
|
|
658
|
+
});
|
|
659
|
+
})().catch((error) => {
|
|
660
|
+
absurdReadyPromises.delete(key);
|
|
661
|
+
throw error;
|
|
662
|
+
});
|
|
663
|
+
absurdReadyPromises.set(key, promise);
|
|
664
|
+
return promise;
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
/**
|
|
668
|
+
* Absurd wakeup event name for an integration-event suspension. Scoped by
|
|
669
|
+
* runId so a broadcast emit only wakes the intended run (absurd events are
|
|
670
|
+
* queue-scoped and immutable/first-emit-wins, so an UNSCOPED name would both
|
|
671
|
+
* cross-wake unrelated runs and permanently pin one payload). The authoritative
|
|
672
|
+
* payload is NEVER read from this event — the worker claims the durable signal
|
|
673
|
+
* row (`claimPostgresSchedulerSignal`) for the real payload.
|
|
674
|
+
*/
|
|
675
|
+
export function absurdIntegrationEventName(
|
|
676
|
+
runId: string,
|
|
677
|
+
eventKey: string,
|
|
678
|
+
): string {
|
|
679
|
+
return `evt:${runId}:${eventKey}`;
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
/**
|
|
683
|
+
* Wake an attempt parked on a detached push-execution runner (B2-final). The
|
|
684
|
+
* name is ATTEMPT-scoped on purpose: absurd events are queue-scoped and
|
|
685
|
+
* first-emit-wins, so a stale attempt's late terminal emit names a key nobody
|
|
686
|
+
* is parked on and can never wake (or clobber) a newer generation. The terminal
|
|
687
|
+
* payload is read from scheduler Postgres (`runner_result_json` on the attempt
|
|
688
|
+
* row); the Absurd event is only a durable wakeup.
|
|
689
|
+
*/
|
|
690
|
+
export function absurdRunnerTerminalEventName(
|
|
691
|
+
runId: string,
|
|
692
|
+
runnerAttempt: number,
|
|
693
|
+
): string {
|
|
694
|
+
return `runner-terminal:${runId}:${runnerAttempt}`;
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
/** Build the worker→app runtime API context (ledger + billing callbacks). */
|
|
698
|
+
export function absurdRuntimeApiFromLaunch(
|
|
699
|
+
launch: PlaySchedulerSubmitInput,
|
|
700
|
+
): WorkerRuntimeApiContext {
|
|
701
|
+
return {
|
|
702
|
+
baseUrl: runtimeApiBaseUrlFromLaunch(launch),
|
|
703
|
+
executorToken: launch.executorToken,
|
|
704
|
+
integrationMode: launch.integrationMode ?? null,
|
|
705
|
+
vercelProtectionBypassToken: launch.vercelProtectionBypassToken ?? null,
|
|
706
|
+
runtimeTestFaultHeader: launch.runtimeTestFaultHeader ?? null,
|
|
707
|
+
};
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
/** Terminal run-ledger event for a settled attempt (source=worker). */
|
|
711
|
+
export function absurdTerminalLedgerEvent(
|
|
712
|
+
result: PostgresSchedulerWorkerOnceResult,
|
|
713
|
+
): PlayRunLedgerEvent | null {
|
|
714
|
+
const occurredAt = Date.now();
|
|
715
|
+
if (result.status === 'completed') {
|
|
716
|
+
return {
|
|
717
|
+
type: 'run.completed',
|
|
718
|
+
runId: result.runId,
|
|
719
|
+
occurredAt,
|
|
720
|
+
source: 'worker',
|
|
721
|
+
result: result.output,
|
|
722
|
+
};
|
|
723
|
+
}
|
|
724
|
+
if (result.status === 'failed') {
|
|
725
|
+
return {
|
|
726
|
+
type: 'run.failed',
|
|
727
|
+
runId: result.runId,
|
|
728
|
+
occurredAt,
|
|
729
|
+
source: 'worker',
|
|
730
|
+
error: result.error,
|
|
731
|
+
};
|
|
732
|
+
}
|
|
733
|
+
return null;
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
737
|
+
return Boolean(value && typeof value === 'object' && !Array.isArray(value));
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
/**
|
|
741
|
+
* Normalize an arbitrary runner checkpoint into the PlayCheckpoint envelope.
|
|
742
|
+
*
|
|
743
|
+
* Compatibility contract (`docs/runtime-compatibility-contract.md`): unknown
|
|
744
|
+
* TOP-LEVEL fields are PASSED THROUGH. This normalizer's result is written back
|
|
745
|
+
* to the durable checkpoint on every park/resume, so rebuilding the envelope
|
|
746
|
+
* from only the keys this build knows would ERASE state a newer build wrote —
|
|
747
|
+
* silently, and permanently, on the first wait a run takes. The known keys keep
|
|
748
|
+
* exactly the validation they had: they always override the passthrough.
|
|
749
|
+
*/
|
|
750
|
+
export function absurdCheckpointBase(checkpoint: unknown): PlayCheckpoint {
|
|
751
|
+
const base = isRecord(checkpoint) ? checkpoint : {};
|
|
752
|
+
const durableCallCacheEpochMs = base.durableCallCacheEpochMs;
|
|
753
|
+
const normalized = {
|
|
754
|
+
...base,
|
|
755
|
+
completedBatches:
|
|
756
|
+
(base.completedBatches as PlayCheckpoint['completedBatches']) ?? {},
|
|
757
|
+
completedToolBatches:
|
|
758
|
+
(base.completedToolBatches as PlayCheckpoint['completedToolBatches']) ??
|
|
759
|
+
{},
|
|
760
|
+
resolvedWaterfalls:
|
|
761
|
+
(base.resolvedWaterfalls as PlayCheckpoint['resolvedWaterfalls']) ?? {},
|
|
762
|
+
resolvedBoundaries:
|
|
763
|
+
(base.resolvedBoundaries as PlayCheckpoint['resolvedBoundaries']) ?? {},
|
|
764
|
+
} as PlayCheckpoint;
|
|
765
|
+
// Same strictness as before the passthrough: a non-numeric/negative epoch or a
|
|
766
|
+
// non-record mapFrames is DROPPED rather than carried forward by the spread.
|
|
767
|
+
if (
|
|
768
|
+
typeof durableCallCacheEpochMs === 'number' &&
|
|
769
|
+
Number.isFinite(durableCallCacheEpochMs) &&
|
|
770
|
+
durableCallCacheEpochMs >= 0
|
|
771
|
+
) {
|
|
772
|
+
normalized.durableCallCacheEpochMs = durableCallCacheEpochMs;
|
|
773
|
+
} else {
|
|
774
|
+
delete normalized.durableCallCacheEpochMs;
|
|
775
|
+
}
|
|
776
|
+
if (isRecord(base.mapFrames)) {
|
|
777
|
+
normalized.mapFrames = base.mapFrames as PlayCheckpoint['mapFrames'];
|
|
778
|
+
} else {
|
|
779
|
+
delete normalized.mapFrames;
|
|
780
|
+
}
|
|
781
|
+
return normalized;
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
function resolvedBoundaries(
|
|
785
|
+
checkpoint: PlayCheckpoint,
|
|
786
|
+
): NonNullable<PlayCheckpoint['resolvedBoundaries']> {
|
|
787
|
+
return checkpoint.resolvedBoundaries ?? {};
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
/** Boundary output object the play runtime reads when an integration event
|
|
791
|
+
* resolves (or times out). Mirrors the belt-and-suspenders envelope carried by
|
|
792
|
+
* the durable signal store today. */
|
|
793
|
+
export function absurdIntegrationEventBoundaryOutput(input: {
|
|
794
|
+
eventKey: string;
|
|
795
|
+
signalData: Record<string, unknown> | null;
|
|
796
|
+
messageRef?: { channel: string; ts: string } | null;
|
|
797
|
+
timedOut: boolean;
|
|
798
|
+
}): Record<string, unknown> {
|
|
799
|
+
if (input.timedOut) {
|
|
800
|
+
return {
|
|
801
|
+
resumed: false,
|
|
802
|
+
timed_out: true,
|
|
803
|
+
signal: input.eventKey,
|
|
804
|
+
event_key: input.eventKey,
|
|
805
|
+
message_ref: input.messageRef ?? null,
|
|
806
|
+
event_payload: null,
|
|
807
|
+
response: null,
|
|
808
|
+
};
|
|
809
|
+
}
|
|
810
|
+
const data = input.signalData ?? {};
|
|
811
|
+
return {
|
|
812
|
+
...data,
|
|
813
|
+
resumed: true,
|
|
814
|
+
timed_out: false,
|
|
815
|
+
signal: input.eventKey,
|
|
816
|
+
event_key: input.eventKey,
|
|
817
|
+
message_ref: input.messageRef ?? null,
|
|
818
|
+
event_payload: input.signalData ?? null,
|
|
819
|
+
response: { eventKey: input.eventKey, data: input.signalData ?? null },
|
|
820
|
+
};
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
/** Return a checkpoint with the sleep boundary marked complete. */
|
|
824
|
+
export function absurdCheckpointWithSleep(
|
|
825
|
+
checkpoint: unknown,
|
|
826
|
+
boundaryId: string,
|
|
827
|
+
delayMs: number,
|
|
828
|
+
): PlayCheckpoint {
|
|
829
|
+
const base = absurdCheckpointBase(checkpoint);
|
|
830
|
+
return {
|
|
831
|
+
...base,
|
|
832
|
+
resolvedBoundaries: {
|
|
833
|
+
...resolvedBoundaries(base),
|
|
834
|
+
[boundaryId]: { kind: 'sleep', delayMs, completedAt: Date.now() },
|
|
835
|
+
},
|
|
836
|
+
};
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
/** Return a checkpoint with the integration-event boundary resolved. */
|
|
840
|
+
export function absurdCheckpointWithIntegrationEvent(input: {
|
|
841
|
+
checkpoint: unknown;
|
|
842
|
+
boundaryId: string;
|
|
843
|
+
eventKey: string;
|
|
844
|
+
timeoutMs: number;
|
|
845
|
+
messageRef?: { channel: string; ts: string };
|
|
846
|
+
output: Record<string, unknown>;
|
|
847
|
+
}): PlayCheckpoint {
|
|
848
|
+
const base = absurdCheckpointBase(input.checkpoint);
|
|
849
|
+
const existingBoundary = resolvedBoundaries(base)[input.boundaryId];
|
|
850
|
+
// Resolving a wait replaces its output, not the semantics captured when it
|
|
851
|
+
// was armed. In particular, an optional timeout policy must survive through
|
|
852
|
+
// the timeout wake so a later deployment cannot reinterpret the result.
|
|
853
|
+
const existingIntegrationBoundary =
|
|
854
|
+
existingBoundary?.kind === 'integration_event' ? existingBoundary : {};
|
|
855
|
+
return {
|
|
856
|
+
...base,
|
|
857
|
+
resolvedBoundaries: {
|
|
858
|
+
...resolvedBoundaries(base),
|
|
859
|
+
[input.boundaryId]: {
|
|
860
|
+
...existingIntegrationBoundary,
|
|
861
|
+
kind: 'integration_event',
|
|
862
|
+
eventKey: input.eventKey,
|
|
863
|
+
timeoutMs: input.timeoutMs,
|
|
864
|
+
...(input.messageRef ? { messageRef: input.messageRef } : {}),
|
|
865
|
+
output: input.output,
|
|
866
|
+
completedAt: Date.now(),
|
|
867
|
+
},
|
|
868
|
+
},
|
|
869
|
+
};
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
/** True when a boundary id is already resolved in the checkpoint (idempotent
|
|
873
|
+
* replay guard).
|
|
874
|
+
*
|
|
875
|
+
* An entry whose `kind` this build does not know THROWS rather than reporting
|
|
876
|
+
* "resolved" for a boundary it cannot actually replay — see
|
|
877
|
+
* `assertKnownResolvedBoundary`. */
|
|
878
|
+
export function absurdBoundaryResolved(
|
|
879
|
+
checkpoint: unknown,
|
|
880
|
+
boundaryId: string,
|
|
881
|
+
): boolean {
|
|
882
|
+
const base = absurdCheckpointBase(checkpoint);
|
|
883
|
+
return Boolean(
|
|
884
|
+
assertKnownResolvedBoundary(
|
|
885
|
+
boundaryId,
|
|
886
|
+
resolvedBoundaries(base)[boundaryId],
|
|
887
|
+
),
|
|
888
|
+
);
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
/** Runner progress callbacks are a subset of the runner-backend contract; kept
|
|
892
|
+
* here so both the worker and its tests share one shape. */
|
|
893
|
+
export type AbsurdRunnerProgressCallbacks = {
|
|
894
|
+
onLog?: (event: unknown) => void;
|
|
895
|
+
onRowUpdate?: (update: PlayRowUpdate) => void;
|
|
896
|
+
onExecutionEvent?: (event: PlayExecutionEvent) => void;
|
|
897
|
+
};
|