@wardby/cli 0.6.0 → 0.7.0
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/deploy/local/docker-compose.quickstart-coding.yml +13 -0
- package/dist/claude-coding-worker/driver.d.ts +21 -3
- package/dist/claude-coding-worker/driver.js +125 -17
- package/dist/claude-coding-worker/main.js +34 -6
- package/dist/claude-coding-worker/sdk.js +12 -1
- package/dist/claude-coding-worker/tool-socket.d.ts +27 -0
- package/dist/claude-coding-worker/tool-socket.js +111 -0
- package/dist/claude-coding-worker/transcript.d.ts +18 -0
- package/dist/claude-coding-worker/transcript.js +77 -0
- package/dist/cli-help.d.ts +1 -1
- package/dist/cli-help.js +1 -0
- package/dist/cli.js +24 -10
- package/dist/coding/continuation-wording.d.ts +35 -0
- package/dist/coding/continuation-wording.js +73 -0
- package/dist/coding/linked-issue-note.d.ts +23 -0
- package/dist/coding/linked-issue-note.js +47 -0
- package/dist/coding/merge-wording.d.ts +28 -0
- package/dist/coding/merge-wording.js +78 -0
- package/dist/coding/pr-description-note.d.ts +9 -0
- package/dist/coding/pr-description-note.js +32 -0
- package/dist/coding/profile.d.ts +46 -126
- package/dist/coding/profile.js +45 -4
- package/dist/coding/protected-path-wording.d.ts +8 -0
- package/dist/coding/protected-path-wording.js +1 -1
- package/dist/coding/protocol.d.ts +264 -346
- package/dist/coding/protocol.js +235 -12
- package/dist/coding/redact.d.ts +3 -0
- package/dist/coding/redact.js +12 -0
- package/dist/coding/registry/allowlist.d.ts +12 -0
- package/dist/coding/registry/allowlist.js +14 -1
- package/dist/coding/registry/audit-mode.d.ts +153 -0
- package/dist/coding/registry/audit-mode.js +304 -0
- package/dist/coding/registry/lockfile-shared.d.ts +36 -0
- package/dist/coding/registry/lockfile-shared.js +128 -0
- package/dist/coding/registry/lockfiles.js +1 -1
- package/dist/coding/registry/npm-lockfile-formats.d.ts +11 -0
- package/dist/coding/registry/npm-lockfile-formats.js +41 -0
- package/dist/coding/registry/npm-lockfile.d.ts +8 -4
- package/dist/coding/registry/npm-lockfile.js +4 -6
- package/dist/coding/registry/npm.js +78 -8
- package/dist/coding/registry/pnpm-lockfile.d.ts +17 -0
- package/dist/coding/registry/pnpm-lockfile.js +189 -0
- package/dist/coding/registry/pypi-plan.d.ts +50 -0
- package/dist/coding/registry/pypi-plan.js +406 -0
- package/dist/coding/registry/pypi-requirements.d.ts +58 -0
- package/dist/coding/registry/pypi-requirements.js +155 -0
- package/dist/coding/registry/pypi.d.ts +3 -27
- package/dist/coding/registry/pypi.js +40 -154
- package/dist/coding/registry/refusal-hints.d.ts +20 -0
- package/dist/coding/registry/refusal-hints.js +61 -0
- package/dist/coding/registry/refusal-wording.d.ts +11 -0
- package/dist/coding/registry/refusal-wording.js +56 -0
- package/dist/coding/registry/report.d.ts +70 -8
- package/dist/coding/registry/report.js +112 -5
- package/dist/coding/registry/types.d.ts +58 -8
- package/dist/coding/registry/yarn-lockfile.d.ts +30 -0
- package/dist/coding/registry/yarn-lockfile.js +367 -0
- package/dist/coding/services/catalog.d.ts +37 -351
- package/dist/coding/transcript/claude-code.d.ts +13 -0
- package/dist/coding/transcript/claude-code.js +273 -0
- package/dist/coding/transcript/download-protocol.d.ts +30 -0
- package/dist/coding/transcript/download-protocol.js +18 -0
- package/dist/coding/transcript/format.d.ts +20 -0
- package/dist/coding/transcript/format.js +32 -0
- package/dist/coding/transcript/recorder.d.ts +51 -0
- package/dist/coding/transcript/recorder.js +225 -0
- package/dist/coding/transcript/resume/checkout.d.ts +31 -0
- package/dist/coding/transcript/resume/checkout.js +203 -0
- package/dist/coding/transcript/resume/command.d.ts +17 -0
- package/dist/coding/transcript/resume/command.js +176 -0
- package/dist/coding/transcript/resume/download.d.ts +20 -0
- package/dist/coding/transcript/resume/download.js +153 -0
- package/dist/coding/transcript/resume/launch.d.ts +38 -0
- package/dist/coding/transcript/resume/launch.js +128 -0
- package/dist/coding/transcript/resume/session-file.d.ts +13 -0
- package/dist/coding/transcript/resume/session-file.js +79 -0
- package/dist/coding/transcript/resume/stand-in.d.ts +11 -0
- package/dist/coding/transcript/resume/stand-in.js +49 -0
- package/dist/coding-worker/artifact.d.ts +8 -3
- package/dist/coding-worker/artifact.js +3 -1
- package/dist/coding-worker/debug-trace.d.ts +6 -5
- package/dist/coding-worker/debug-trace.js +13 -18
- package/dist/coding-worker/dependency-cache.d.mts +229 -0
- package/dist/coding-worker/dependency-cache.mjs +2123 -0
- package/dist/coding-worker/driver.d.ts +4 -3
- package/dist/coding-worker/driver.js +116 -38
- package/dist/coding-worker/errors.d.ts +1 -0
- package/dist/coding-worker/errors.js +32 -0
- package/dist/coding-worker/task-note.d.ts +11 -0
- package/dist/coding-worker/task-note.js +13 -0
- package/dist/coding-worker/types.d.ts +11 -0
- package/dist/config/dependency-cache.d.ts +23 -0
- package/dist/config/dependency-cache.js +32 -0
- package/dist/config/providers.d.ts +12 -3
- package/dist/config/providers.js +55 -5
- package/dist/config/test-simulators.d.ts +82 -0
- package/dist/config/test-simulators.js +305 -0
- package/dist/core/async-context.d.ts +2 -0
- package/dist/core/async-context.js +12 -0
- package/dist/core/budget-events.d.ts +108 -0
- package/dist/core/budget-events.js +346 -0
- package/dist/core/budget-groups.d.ts +50 -1
- package/dist/core/budget-groups.js +236 -27
- package/dist/core/budget-stopped-head.d.ts +46 -0
- package/dist/core/budget-stopped-head.js +81 -0
- package/dist/core/budget-topup.d.ts +51 -0
- package/dist/core/budget-topup.js +156 -0
- package/dist/core/budget-wording.d.ts +16 -2
- package/dist/core/budget-wording.js +40 -2
- package/dist/core/coding-queue.d.ts +2 -1
- package/dist/core/coding-queue.js +7 -0
- package/dist/core/cron.d.ts +19 -1
- package/dist/core/cron.js +17 -0
- package/dist/core/datastores.js +1 -1
- package/dist/core/deferred-coding.d.ts +14 -0
- package/dist/core/deferred-coding.js +62 -0
- package/dist/core/delegation-child.d.ts +25 -0
- package/dist/core/delegation-child.js +37 -0
- package/dist/core/dispatch.d.ts +42 -0
- package/dist/core/dispatch.js +131 -56
- package/dist/core/engine-native.js +1 -1
- package/dist/core/error-code.d.ts +38 -0
- package/dist/core/error-code.js +168 -0
- package/dist/core/grants.d.ts +19 -6
- package/dist/core/grants.js +55 -6
- package/dist/core/host-events.d.ts +18 -2
- package/dist/core/host-events.js +284 -24
- package/dist/core/host-status.d.ts +85 -3
- package/dist/core/host-status.js +298 -20
- package/dist/core/issue-bridge.d.ts +50 -6
- package/dist/core/issue-bridge.js +124 -33
- package/dist/core/issue-events.d.ts +1 -1
- package/dist/core/issue-events.js +4 -10
- package/dist/core/issue-status.d.ts +1 -1
- package/dist/core/issue-status.js +54 -26
- package/dist/core/logger.d.ts +5 -0
- package/dist/core/logger.js +14 -1
- package/dist/core/lost-run-children.d.ts +6 -0
- package/dist/core/lost-run-children.js +32 -0
- package/dist/core/mark-ready.d.ts +34 -0
- package/dist/core/mark-ready.js +152 -0
- package/dist/core/markdown-code.d.ts +8 -0
- package/dist/core/markdown-code.js +107 -0
- package/dist/core/memory-tools.js +7 -0
- package/dist/core/merge-order-check.d.ts +2 -1
- package/dist/core/merge-order-check.js +8 -1
- package/dist/core/notification-templates.d.ts +1 -7
- package/dist/core/notification-templates.js +36 -2
- package/dist/core/pull-request-state-sync.js +2 -2
- package/dist/core/reconciler.d.ts +4 -3
- package/dist/core/reconciler.js +8 -1
- package/dist/core/related-pull-requests.d.ts +11 -3
- package/dist/core/related-pull-requests.js +27 -14
- package/dist/core/reply-parts.d.ts +21 -0
- package/dist/core/reply-parts.js +73 -0
- package/dist/core/review-fix.d.ts +1 -1
- package/dist/core/review-fix.js +2 -1
- package/dist/core/review-host-checks.d.ts +1 -1
- package/dist/core/review-host-tools.d.ts +3 -0
- package/dist/core/review-host-tools.js +99 -13
- package/dist/core/review-reads.d.ts +55 -0
- package/dist/core/review-reads.js +233 -0
- package/dist/core/run-status.d.ts +12 -0
- package/dist/core/run-status.js +12 -0
- package/dist/core/runner.d.ts +80 -34
- package/dist/core/runner.js +439 -144
- package/dist/core/schedule-start.d.ts +12 -0
- package/dist/core/schedule-start.js +14 -0
- package/dist/core/secret-broker-config.d.ts +10 -74
- package/dist/core/secret-guard.d.ts +46 -0
- package/dist/core/secret-guard.js +104 -0
- package/dist/core/secret-guarded-stores.d.ts +12 -0
- package/dist/core/secret-guarded-stores.js +41 -0
- package/dist/core/secret-spans.d.ts +50 -0
- package/dist/core/secret-spans.js +94 -0
- package/dist/core/stop-run.d.ts +46 -0
- package/dist/core/stop-run.js +61 -0
- package/dist/core/workflow-events.d.ts +14 -2
- package/dist/core/workflow-events.js +4 -0
- package/dist/core/workflow-run-events.d.ts +5 -3
- package/dist/core/workflow-run-events.js +43 -8
- package/dist/core/zod-json-schema.d.ts +4 -0
- package/dist/core/zod-json-schema.js +39 -0
- package/dist/core/zod3-window.d.ts +35 -0
- package/dist/core/zod3-window.js +124 -0
- package/dist/generated/prisma/browser.d.ts +34 -3
- package/dist/generated/prisma/client.d.ts +34 -3
- package/dist/generated/prisma/commonInputTypes.d.ts +220 -60
- package/dist/generated/prisma/enums.d.ts +10 -0
- package/dist/generated/prisma/enums.js +8 -0
- package/dist/generated/prisma/internal/class.d.ts +44 -0
- package/dist/generated/prisma/internal/class.js +4 -4
- package/dist/generated/prisma/internal/prismaNamespace.d.ts +428 -9
- package/dist/generated/prisma/internal/prismaNamespace.js +92 -1
- package/dist/generated/prisma/internal/prismaNamespaceBrowser.d.ts +95 -0
- package/dist/generated/prisma/internal/prismaNamespaceBrowser.js +92 -1
- package/dist/generated/prisma/models/Agent.d.ts +106 -8
- package/dist/generated/prisma/models/AgentRepository.d.ts +42 -1
- package/dist/generated/prisma/models/CodingAgentProfile.d.ts +229 -1
- package/dist/generated/prisma/models/CodingProxyRequest.d.ts +207 -3
- package/dist/generated/prisma/models/CodingProxySession.d.ts +114 -1
- package/dist/generated/prisma/models/CodingRun.d.ts +1637 -1
- package/dist/generated/prisma/models/DependencySnapshot.d.ts +1668 -0
- package/dist/generated/prisma/models/DependencySnapshot.js +1 -0
- package/dist/generated/prisma/models/IssuePullRequest.d.ts +6 -2
- package/dist/generated/prisma/models/LocalReview.d.ts +40 -1
- package/dist/generated/prisma/models/NativeGatewayCall.d.ts +39 -1
- package/dist/generated/prisma/models/NativeGatewaySession.d.ts +35 -1
- package/dist/generated/prisma/models/RegistryAllowance.d.ts +41 -1
- package/dist/generated/prisma/models/RegistryApprovedVersion.d.ts +75 -1
- package/dist/generated/prisma/models/RegistryAuditRecord.d.ts +1364 -0
- package/dist/generated/prisma/models/RegistryAuditRecord.js +1 -0
- package/dist/generated/prisma/models/Run.d.ts +738 -2
- package/dist/generated/prisma/models/RunBudgetEvent.d.ts +1211 -0
- package/dist/generated/prisma/models/RunBudgetEvent.js +1 -0
- package/dist/generated/prisma/models/RunTranscript.d.ts +1339 -0
- package/dist/generated/prisma/models/RunTranscript.js +1 -0
- package/dist/generated/prisma/models.d.ts +4 -0
- package/dist/help-index.json +1320 -69
- package/dist/import/create.js +32 -3
- package/dist/import/neutral-schema.d.ts +50 -281
- package/dist/import/neutral-schema.js +5 -4
- package/dist/knowledge/check.d.ts +1 -1
- package/dist/knowledge/cli.d.ts +11 -1
- package/dist/knowledge/cli.js +37 -4
- package/dist/knowledge/concept.d.ts +3 -19
- package/dist/knowledge/location.d.ts +31 -0
- package/dist/knowledge/location.js +78 -0
- package/dist/mcp/auth/access.d.ts +10 -2
- package/dist/mcp/auth/access.js +15 -1
- package/dist/mcp/auth/ownership.d.ts +7 -7
- package/dist/mcp/auth/self-hosted/credentials.d.ts +3 -3
- package/dist/mcp/auth/self-hosted/session.d.ts +2 -2
- package/dist/mcp/host-events/github-ingress.d.ts +2 -0
- package/dist/mcp/host-events/github-ingress.js +2 -1
- package/dist/mcp/index.d.ts +6 -0
- package/dist/mcp/index.js +58 -10
- package/dist/mcp/server.d.ts +13 -0
- package/dist/mcp/server.js +33 -14
- package/dist/mcp/tools/agents.d.ts +4 -0
- package/dist/mcp/tools/agents.js +198 -16
- package/dist/mcp/tools/datastore.js +17 -8
- package/dist/mcp/tools/grants.js +9 -3
- package/dist/mcp/tools/memory.js +11 -1
- package/dist/mcp/tools/notification-channels.js +2 -2
- package/dist/mcp/tools/packages.d.ts +2 -0
- package/dist/mcp/tools/packages.js +195 -0
- package/dist/mcp/tools/repositories.js +18 -5
- package/dist/mcp/tools/run-access.d.ts +16 -0
- package/dist/mcp/tools/run-access.js +25 -0
- package/dist/mcp/tools/run-transcript-download.d.ts +30 -0
- package/dist/mcp/tools/run-transcript-download.js +129 -0
- package/dist/mcp/tools/run-transcript.d.ts +19 -0
- package/dist/mcp/tools/run-transcript.js +70 -0
- package/dist/mcp/tools/runs.js +34 -12
- package/dist/mcp/tools/scheduling.js +3 -1
- package/dist/mcp/tools/secret-elicitation-form.js +5 -0
- package/dist/mcp/tools/secret-elicitation-server.d.ts +6 -0
- package/dist/mcp/tools/secret-elicitation-server.js +10 -61
- package/dist/mcp/tools/secrets.js +2 -2
- package/dist/mcp/tools/stop-run.d.ts +2 -0
- package/dist/mcp/tools/stop-run.js +82 -0
- package/dist/mcp/tools/subagents.js +2 -2
- package/dist/mcp/tools/tools.d.ts +1 -1
- package/dist/mcp/tools/tools.js +25 -9
- package/dist/mcp/transport/stdio-link-host.d.ts +20 -0
- package/dist/mcp/transport/stdio-link-host.js +67 -0
- package/dist/mcp/transport/streamable-http.d.ts +13 -0
- package/dist/mcp/transport/streamable-http.js +41 -1
- package/dist/native-worker/docker-launcher.d.ts +38 -2
- package/dist/native-worker/docker-launcher.js +65 -5
- package/dist/native-worker/gateway-server.d.ts +41 -3
- package/dist/native-worker/gateway-server.js +124 -27
- package/dist/native-worker/gateway.d.ts +27 -1
- package/dist/native-worker/gateway.js +70 -4
- package/dist/native-worker/http-server.d.ts +3 -1
- package/dist/native-worker/http-server.js +11 -7
- package/dist/native-worker/http-transport.js +1 -1
- package/dist/native-worker/kubernetes-launcher.d.ts +7 -3
- package/dist/native-worker/kubernetes-launcher.js +43 -3
- package/dist/native-worker/launch.d.ts +18 -1
- package/dist/native-worker/launch.js +6 -1
- package/dist/native-worker/ledger.d.ts +18 -2
- package/dist/native-worker/ledger.js +57 -4
- package/dist/native-worker/loopback.js +3 -8
- package/dist/native-worker/main.d.ts +2 -1
- package/dist/native-worker/main.js +14 -3
- package/dist/native-worker/protocol.d.ts +129 -577
- package/dist/native-worker/protocol.js +47 -1
- package/dist/native-worker/sandbox-executor.d.ts +17 -2
- package/dist/native-worker/sandbox-executor.js +78 -4
- package/dist/native-worker/stderr-redaction.d.ts +77 -0
- package/dist/native-worker/stderr-redaction.js +237 -0
- package/dist/native-worker/stdio.d.ts +1 -1
- package/dist/native-worker/stdio.js +6 -5
- package/dist/native-worker/warm-pool.d.ts +2 -2
- package/dist/native-worker/warm-pool.js +4 -4
- package/dist/native-worker/worker.js +28 -2
- package/dist/observability/metrics.js +6 -1
- package/dist/providers/auth/self-hosted.d.ts +1 -1
- package/dist/providers/coding-proxy/dependency-cache/eviction.d.ts +29 -0
- package/dist/providers/coding-proxy/dependency-cache/eviction.js +87 -0
- package/dist/providers/coding-proxy/dependency-cache/key.d.ts +33 -0
- package/dist/providers/coding-proxy/dependency-cache/key.js +86 -0
- package/dist/providers/coding-proxy/dependency-cache/model-request-seal.d.ts +18 -0
- package/dist/providers/coding-proxy/dependency-cache/model-request-seal.js +40 -0
- package/dist/providers/coding-proxy/dependency-cache/prisma-store.d.ts +25 -0
- package/dist/providers/coding-proxy/dependency-cache/prisma-store.js +131 -0
- package/dist/providers/coding-proxy/dependency-cache/service.d.ts +70 -0
- package/dist/providers/coding-proxy/dependency-cache/service.js +409 -0
- package/dist/providers/coding-proxy/dependency-cache/store.d.ts +85 -0
- package/dist/providers/coding-proxy/dependency-cache/store.js +95 -0
- package/dist/providers/coding-proxy/hold.d.ts +32 -0
- package/dist/providers/coding-proxy/hold.js +86 -0
- package/dist/providers/coding-proxy/interrupted.d.ts +70 -0
- package/dist/providers/coding-proxy/interrupted.js +104 -0
- package/dist/providers/coding-proxy/memory-ledger.d.ts +15 -2
- package/dist/providers/coding-proxy/memory-ledger.js +84 -8
- package/dist/providers/coding-proxy/metering.d.ts +16 -0
- package/dist/providers/coding-proxy/metering.js +27 -4
- package/dist/providers/coding-proxy/prisma-ledger.d.ts +15 -2
- package/dist/providers/coding-proxy/prisma-ledger.js +179 -67
- package/dist/providers/coding-proxy/proxy.d.ts +14 -1
- package/dist/providers/coding-proxy/proxy.js +166 -14
- package/dist/providers/coding-proxy/registry/audit.d.ts +53 -10
- package/dist/providers/coding-proxy/registry/audit.js +94 -17
- package/dist/providers/coding-proxy/registry/lockfile-parse-worker.d.ts +36 -0
- package/dist/providers/coding-proxy/registry/lockfile-parse-worker.js +67 -0
- package/dist/providers/coding-proxy/registry/lockfile-parse.d.ts +16 -0
- package/dist/providers/coding-proxy/registry/lockfile-parse.js +110 -0
- package/dist/providers/coding-proxy/registry/plan.d.ts +9 -0
- package/dist/providers/coding-proxy/registry/plan.js +201 -48
- package/dist/providers/coding-proxy/registry/prisma-store.d.ts +11 -5
- package/dist/providers/coding-proxy/registry/prisma-store.js +120 -17
- package/dist/providers/coding-proxy/registry/service.d.ts +97 -16
- package/dist/providers/coding-proxy/registry/service.js +579 -112
- package/dist/providers/coding-proxy/registry/store.d.ts +84 -9
- package/dist/providers/coding-proxy/registry/store.js +54 -1
- package/dist/providers/coding-proxy/runtime.d.ts +4 -0
- package/dist/providers/coding-proxy/runtime.js +78 -16
- package/dist/providers/coding-proxy/server.d.ts +6 -1
- package/dist/providers/coding-proxy/server.js +45 -1
- package/dist/providers/coding-proxy/types.d.ts +65 -1
- package/dist/providers/datastore/types.d.ts +5 -0
- package/dist/providers/engine/types.d.ts +17 -2
- package/dist/providers/engine/types.js +17 -0
- package/dist/providers/executor/composition.d.ts +10 -3
- package/dist/providers/executor/composition.js +47 -7
- package/dist/providers/executor/container.d.ts +361 -8
- package/dist/providers/executor/container.js +1136 -91
- package/dist/providers/executor/dbos-status.d.ts +31 -0
- package/dist/providers/executor/dbos-status.js +64 -0
- package/dist/providers/executor/dbos.d.ts +50 -1
- package/dist/providers/executor/dbos.js +207 -20
- package/dist/providers/executor/deferred.d.ts +12 -24
- package/dist/providers/executor/deferred.js +10 -29
- package/dist/providers/executor/in-process.d.ts +9 -1
- package/dist/providers/executor/in-process.js +14 -4
- package/dist/providers/executor/linked-issue-reader.d.ts +16 -0
- package/dist/providers/executor/linked-issue-reader.js +30 -0
- package/dist/providers/executor/routing.d.ts +2 -0
- package/dist/providers/executor/routing.js +4 -0
- package/dist/providers/executor/types.d.ts +13 -0
- package/dist/providers/index.d.ts +2 -1
- package/dist/providers/issue-tracker/types.d.ts +17 -0
- package/dist/providers/issue-tracker/types.js +39 -1
- package/dist/providers/jobs/claude-tool-setup.d.ts +4 -0
- package/dist/providers/jobs/claude-tool-setup.js +5 -2
- package/dist/providers/jobs/docker-isolation.d.ts +8 -0
- package/dist/providers/jobs/docker-isolation.js +17 -0
- package/dist/providers/jobs/docker.d.ts +1 -0
- package/dist/providers/jobs/docker.js +27 -2
- package/dist/providers/jobs/fake-kubernetes-api.d.ts +2 -0
- package/dist/providers/jobs/fake-kubernetes-api.js +3 -0
- package/dist/providers/jobs/kubernetes-api.d.ts +8 -0
- package/dist/providers/jobs/kubernetes-api.js +10 -0
- package/dist/providers/jobs/kubernetes-client.js +10 -1
- package/dist/providers/jobs/kubernetes-isolation.d.ts +2 -0
- package/dist/providers/jobs/kubernetes-isolation.js +17 -1
- package/dist/providers/jobs/kubernetes.d.ts +6 -1
- package/dist/providers/jobs/kubernetes.js +105 -16
- package/dist/providers/jobs/types.d.ts +33 -0
- package/dist/providers/jobs/types.js +13 -1
- package/dist/providers/llm/anthropic.js +5 -1
- package/dist/providers/llm/bedrock.d.ts +0 -12
- package/dist/providers/llm/bedrock.js +4 -0
- package/dist/providers/llm/claude-messages.d.ts +3 -1
- package/dist/providers/llm/claude-messages.js +4 -1
- package/dist/providers/llm/claude-provider.d.ts +2 -2
- package/dist/providers/llm/claude-provider.js +2 -2
- package/dist/providers/llm/openai.d.ts +2 -2
- package/dist/providers/llm/openai.js +6 -2
- package/dist/providers/llm/routing.d.ts +2 -2
- package/dist/providers/llm/routing.js +2 -2
- package/dist/providers/llm/types.d.ts +12 -2
- package/dist/providers/review-host/github-events.d.ts +9 -1
- package/dist/providers/review-host/github-events.js +34 -12
- package/dist/providers/review-host/github.d.ts +29 -2
- package/dist/providers/review-host/github.js +224 -29
- package/dist/providers/review-host/local.d.ts +8 -4
- package/dist/providers/review-host/local.js +49 -31
- package/dist/providers/review-host/patch-pages.d.ts +41 -0
- package/dist/providers/review-host/patch-pages.js +176 -0
- package/dist/providers/review-host/review-format.d.ts +19 -1
- package/dist/providers/review-host/review-format.js +38 -7
- package/dist/providers/review-host/types.d.ts +134 -2
- package/dist/providers/review-host/types.js +8 -0
- package/dist/providers/storage/gcs.d.ts +63 -0
- package/dist/providers/storage/gcs.js +69 -0
- package/dist/providers/storage/index.d.ts +15 -0
- package/dist/providers/storage/index.js +14 -0
- package/dist/providers/storage/local.d.ts +24 -0
- package/dist/providers/storage/local.js +92 -0
- package/dist/providers/storage/meter.d.ts +14 -0
- package/dist/providers/storage/meter.js +34 -0
- package/dist/providers/storage/s3.d.ts +41 -0
- package/dist/providers/storage/s3.js +107 -0
- package/dist/providers/storage/types.d.ts +37 -3
- package/dist/providers/storage/types.js +21 -4
- package/dist/providers/vcs/git.d.ts +33 -2
- package/dist/providers/vcs/git.js +324 -22
- package/dist/providers/vcs/github-remote.d.ts +26 -3
- package/dist/providers/vcs/github-remote.js +124 -14
- package/dist/providers/vcs/github.d.ts +214 -6
- package/dist/providers/vcs/github.js +447 -45
- package/dist/providers/vcs/index.js +8 -1
- package/dist/providers/vcs/pr-description.d.ts +127 -0
- package/dist/providers/vcs/pr-description.js +270 -0
- package/dist/providers/vcs/remote.d.ts +33 -1
- package/dist/providers/vcs/routing.d.ts +4 -2
- package/dist/providers/vcs/routing.js +6 -0
- package/dist/providers/vcs/types.d.ts +80 -3
- package/dist/quickstart/coding-seed.d.ts +14 -3
- package/dist/quickstart/coding-seed.js +213 -4
- package/dist/quickstart/reviewer-prompt.d.ts +1 -1
- package/dist/quickstart/reviewer-prompt.js +6 -6
- package/dist/quickstart-images.json +1 -1
- package/dist/sandbox/generated/zod.bundle.js +62 -1
- package/dist/sandbox/generated/zod3.bundle.js +5 -0
- package/dist/sandbox/host-functions.d.ts +1 -0
- package/dist/sandbox/host-functions.js +6 -11
- package/dist/sandbox/secret-broker.js +3 -4
- package/dist/sandbox/tool-capabilities.d.ts +10 -30
- package/dist/sandbox/zod-params.d.ts +37 -5
- package/dist/sandbox/zod-params.js +181 -26
- package/dist/serve.js +8 -1
- package/dist/viewer/api-schema.d.ts +349 -2627
- package/dist/viewer/api-schema.js +7 -0
- package/dist/viewer/build-schemas.js +6 -2
- package/dist/viewer/openapi.js +2 -4
- package/dist/wardby-bin.js +10 -0
- package/docs/agent-recipes.md +58 -20
- package/docs/code-review-agents.md +551 -41
- package/docs/coding-agent-setup.md +489 -7
- package/docs/coding-packages.md +1033 -90
- package/docs/coding-services.md +27 -12
- package/docs/coding-worker-byo-images.md +20 -0
- package/docs/coding-worker-isolation.md +105 -30
- package/docs/getting-started-gke.md +124 -137
- package/docs/getting-started-identity-provider.md +10 -1
- package/docs/getting-started.md +9 -0
- package/docs/jira-agents.md +17 -8
- package/docs/knowledge/blob-not-found-semantics.md +41 -0
- package/docs/knowledge/bounded-file-descriptor.md +52 -0
- package/docs/knowledge/delegation-admission-slots.md +52 -0
- package/docs/knowledge/http-dispatcher-bootstrap.md +61 -0
- package/docs/knowledge/import-rename-target.md +52 -0
- package/docs/knowledge/index.md +57 -0
- package/docs/knowledge/log.md +9 -0
- package/docs/knowledge/mcp-cancelled-task-precedence.md +41 -0
- package/docs/knowledge/mcp-http-prompt-discovery.md +41 -0
- package/docs/knowledge/mcp-schema-module-boundary.md +41 -0
- package/docs/knowledge/nullable-tool-uniqueness.md +52 -0
- package/docs/knowledge/overview-cli.md +74 -0
- package/docs/knowledge/overview-coding-proxy.md +72 -0
- package/docs/knowledge/overview-coding.md +72 -0
- package/docs/knowledge/overview-config.md +60 -0
- package/docs/knowledge/overview-core.md +72 -0
- package/docs/knowledge/overview-deploy.md +83 -0
- package/docs/knowledge/overview-mcp.md +61 -0
- package/docs/knowledge/overview-prisma.md +72 -0
- package/docs/knowledge/overview-providers.md +89 -0
- package/docs/knowledge/overview-sandbox.md +83 -0
- package/docs/knowledge/overview-surfaces.md +83 -0
- package/docs/knowledge/private-datastore-shared-boundary.md +54 -0
- package/docs/knowledge/proxy-settlement-projections.md +41 -0
- package/docs/knowledge/run-pricing-first-writer.md +43 -0
- package/docs/knowledge/schedule-claim-transaction.md +41 -0
- package/docs/knowledge/secret-entry-replica-contract.md +52 -0
- package/docs/knowledge/terminal-run-first-writer.md +41 -0
- package/docs/knowledge/transactional-agent-authorization.md +52 -0
- package/docs/knowledge/viewer-notify-write-isolation.md +30 -0
- package/docs/knowledge/warm-input-publication.md +30 -0
- package/docs/knowledge/workspace-staging-failure.md +54 -0
- package/docs/knowledge.md +54 -14
- package/docs/native-sandbox.md +119 -23
- package/docs/observability.md +4 -0
- package/docs/security-deployment.md +33 -2
- package/docs/slack-notifications.md +35 -19
- package/docs/tool-secrets.md +42 -10
- package/docs/viewer-api.openapi.yml +1 -1
- package/help/admin-viewer.md +24 -2
- package/help/architecture-agent.md +11 -6
- package/help/build-worker-image.md +3 -1
- package/help/builder-agent.md +19 -6
- package/help/code-review-agents.md +177 -6
- package/help/coding-packages.md +156 -6
- package/help/cost-attribution.md +9 -5
- package/help/creating-agents.md +14 -1
- package/help/dependency-cache.md +143 -0
- package/help/deploy-gke.md +15 -8
- package/help/errors/budget-exhausted.md +187 -0
- package/help/errors/budget-group-exhausted.md +5 -1
- package/help/errors/coding-stream-quota-exceeded.md +33 -0
- package/help/errors/coding-workspace-disk-exceeds-limit.md +39 -0
- package/help/errors/continuation-closed.md +9 -12
- package/help/errors/delegation-conflict.md +24 -0
- package/help/errors/file-list-truncated.md +45 -0
- package/help/errors/full-read-required.md +49 -0
- package/help/errors/github-account-not-allowed.md +4 -0
- package/help/errors/graph-incomplete.md +43 -0
- package/help/errors/graph-limit.md +43 -0
- package/help/errors/merge-conflict.md +49 -0
- package/help/errors/native-sandbox-gateway-error.md +20 -0
- package/help/errors/native-sandbox-launch-abandoned.md +21 -0
- package/help/errors/native-sandbox-worker-exited.md +3 -2
- package/help/errors/package-not-allowed-pending.md +52 -0
- package/help/errors/package-not-allowed.md +51 -0
- package/help/errors/package-refusals.md +43 -0
- package/help/errors/patch-cursor-malformed.md +31 -0
- package/help/errors/run-lost.md +39 -0
- package/help/errors/run-transcript-download.md +44 -0
- package/help/errors/secret-in-value.md +69 -0
- package/help/errors/target-merge.md +40 -0
- package/help/fix-merge-conflicts.md +73 -0
- package/help/github-app-setup.md +132 -0
- package/help/github.md +22 -0
- package/help/identity-and-access.md +2 -0
- package/help/jira.md +23 -6
- package/help/knowledge.md +16 -7
- package/help/local-repositories.md +1 -1
- package/help/low-power-mode.md +7 -1
- package/help/merge-order-check.md +5 -0
- package/help/native-capabilities.md +29 -1
- package/help/native-sandbox.md +15 -1
- package/help/operating-agents.md +5 -0
- package/help/package-audit-mode.md +91 -0
- package/help/related-pull-requests.md +10 -7
- package/help/resume-runs-locally.md +131 -0
- package/help/review-fix-rounds.md +33 -1
- package/help/sharing-agents.md +59 -0
- package/help/slack-notifications.md +19 -5
- package/help/status-comments.md +31 -0
- package/help/stop-run.md +58 -0
- package/help/troubleshooting/budgets.md +28 -0
- package/help/troubleshooting/coding-workers.md +78 -7
- package/package.json +17 -10
- package/prisma/migrations/20261016000000_registry_approved_accepted_advisories/migration.sql +3 -0
- package/prisma/migrations/20261016000001_mark_ready_when_passing/migration.sql +3 -0
- package/prisma/migrations/20261017000000_debug_trace_max_mb/migration.sql +4 -0
- package/prisma/migrations/20261018000000_dependency_cache/migration.sql +53 -0
- package/prisma/migrations/20261019000000_claude_max_output_tokens/migration.sql +5 -0
- package/prisma/migrations/20261021000000_pr_description_hash/migration.sql +4 -0
- package/prisma/migrations/20261022000000_dependency_snapshot_validation_metadata/migration.sql +4 -0
- package/prisma/migrations/20261023000000_package_audit_mode/migration.sql +27 -0
- package/prisma/migrations/20261024000000_budget_events/migration.sql +26 -0
- package/prisma/migrations/20261025000000_cache_aware_holds/migration.sql +9 -0
- package/prisma/migrations/20261026000000_budget_overrun/migration.sql +4 -0
- package/prisma/migrations/20261027000000_coding_run_request/migration.sql +1 -0
- package/prisma/migrations/20261028000000_native_gateway_review_reads/migration.sql +6 -0
- package/prisma/migrations/20261029000000_lead_survives_deploy/migration.sql +6 -0
- package/prisma/migrations/20261030000000_local_review_covered_sha/migration.sql +2 -0
- package/prisma/migrations/20261031000000_coding_run_budget_limit/migration.sql +9 -0
- package/prisma/migrations/20261101000000_run_transcripts/migration.sql +22 -0
- package/prisma/schema.prisma +311 -97
- package/dist/sandbox/generated/zod-to-json-schema.bundle.js +0 -5
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* Pure decision table for reconciling a stale `Run` that a DBOS workflow
|
|
3
3
|
* owns. Kept free of DBOS imports so it is unit-testable without Postgres.
|
|
4
4
|
*/
|
|
5
|
+
import { STEPS_CHANGED_REASON } from "../engine/types.js";
|
|
5
6
|
export interface WorkflowStatusLike {
|
|
6
7
|
status: string;
|
|
7
8
|
executorId?: string;
|
|
@@ -48,3 +49,33 @@ export type RecoveryDecision = {
|
|
|
48
49
|
reason: string;
|
|
49
50
|
};
|
|
50
51
|
export declare function decideRecovery(status: WorkflowStatusLike | null, runIsTerminal: boolean, selfExecutorId: string): RecoveryDecision;
|
|
52
|
+
/** One recorded step of a workflow, as `DBOS.listWorkflowSteps` reports it. */
|
|
53
|
+
export interface TakeoverStep {
|
|
54
|
+
functionID: number;
|
|
55
|
+
name: string;
|
|
56
|
+
output: unknown;
|
|
57
|
+
}
|
|
58
|
+
/** A still-drivable child of the lead being taken over. */
|
|
59
|
+
export interface TakeoverChild {
|
|
60
|
+
status: string;
|
|
61
|
+
kind: "coding" | "native";
|
|
62
|
+
nativeExecutionMode: string | null;
|
|
63
|
+
}
|
|
64
|
+
export type TakeoverDecision = {
|
|
65
|
+
action: "fork";
|
|
66
|
+
startStep: number;
|
|
67
|
+
} | {
|
|
68
|
+
action: "lost";
|
|
69
|
+
reason: string;
|
|
70
|
+
};
|
|
71
|
+
/** Why a forked run ends lost when its replay meets a step the new code no longer takes (defined with StepReplayMismatchError). */
|
|
72
|
+
export { STEPS_CHANGED_REASON };
|
|
73
|
+
/**
|
|
74
|
+
* Whether a workflow recorded under another application version can be forked under this one
|
|
75
|
+
* (design §1): only when it is waiting inside a `delegate_to_*` call of its last recorded turn and
|
|
76
|
+
* every child it may be waiting on is durable on its own (a coding run, or a sandbox-mode native
|
|
77
|
+
* run). The fork starts at the first function id with no record, so every recorded step (the
|
|
78
|
+
* turn's model reply above all) replays from its record and the delegate step re-attaches to its
|
|
79
|
+
* child. Anything else is `lost` with the mismatch reason, as before.
|
|
80
|
+
*/
|
|
81
|
+
export declare function decideTakeover(steps: TakeoverStep[], children: TakeoverChild[], mismatchReason: string): TakeoverDecision;
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
* Pure decision table for reconciling a stale `Run` that a DBOS workflow
|
|
3
3
|
* owns. Kept free of DBOS imports so it is unit-testable without Postgres.
|
|
4
4
|
*/
|
|
5
|
+
import { DELEGATE_TOOL_PREFIX } from "../../core/tool-names.js";
|
|
6
|
+
import { STEPS_CHANGED_REASON } from "../engine/types.js";
|
|
5
7
|
/**
|
|
6
8
|
* How many times the reconciler will try to adopt (resume) one orphaned
|
|
7
9
|
* workflow before declaring the run lost.
|
|
@@ -62,3 +64,65 @@ export function decideRecovery(status, runIsTerminal, selfExecutorId) {
|
|
|
62
64
|
}
|
|
63
65
|
return { action: "lost", reason: `Durable workflow is in unrecognised status "${status.status}".` };
|
|
64
66
|
}
|
|
67
|
+
/** Why a forked run ends lost when its replay meets a step the new code no longer takes (defined with StepReplayMismatchError). */
|
|
68
|
+
export { STEPS_CHANGED_REASON };
|
|
69
|
+
const LLM_STEP = /^turn:(\d+):llm$/;
|
|
70
|
+
/**
|
|
71
|
+
* The tool-call names of a recorded `turn:N:llm` step. Its output is the engine's TurnResult
|
|
72
|
+
* (engine-native.ts) as DBOS deserialized it: `{ usage, text, toolCalls: [{ id, name, argsJson }],
|
|
73
|
+
* budgetExceededMidStream, error? }`. Anything else yields undefined (the shape changed: lost).
|
|
74
|
+
*/
|
|
75
|
+
function recordedToolCallNames(output) {
|
|
76
|
+
if (typeof output !== "object" || output === null || !("toolCalls" in output))
|
|
77
|
+
return undefined;
|
|
78
|
+
const calls = output.toolCalls;
|
|
79
|
+
if (!Array.isArray(calls))
|
|
80
|
+
return undefined;
|
|
81
|
+
const names = [];
|
|
82
|
+
for (const call of calls) {
|
|
83
|
+
if (typeof call !== "object" || call === null || !("name" in call) || typeof call.name !== "string") {
|
|
84
|
+
return undefined;
|
|
85
|
+
}
|
|
86
|
+
names.push(call.name);
|
|
87
|
+
}
|
|
88
|
+
return names;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Whether a workflow recorded under another application version can be forked under this one
|
|
92
|
+
* (design §1): only when it is waiting inside a `delegate_to_*` call of its last recorded turn and
|
|
93
|
+
* every child it may be waiting on is durable on its own (a coding run, or a sandbox-mode native
|
|
94
|
+
* run). The fork starts at the first function id with no record, so every recorded step (the
|
|
95
|
+
* turn's model reply above all) replays from its record and the delegate step re-attaches to its
|
|
96
|
+
* child. Anything else is `lost` with the mismatch reason, as before.
|
|
97
|
+
*/
|
|
98
|
+
export function decideTakeover(steps, children, mismatchReason) {
|
|
99
|
+
const lost = { action: "lost", reason: mismatchReason };
|
|
100
|
+
const sorted = [...steps].sort((a, b) => a.functionID - b.functionID);
|
|
101
|
+
const llmStep = sorted.filter((s) => LLM_STEP.test(s.name)).at(-1);
|
|
102
|
+
if (!llmStep)
|
|
103
|
+
return lost;
|
|
104
|
+
const turn = LLM_STEP.exec(llmStep.name)[1];
|
|
105
|
+
const names = recordedToolCallNames(llmStep.output);
|
|
106
|
+
if (!names)
|
|
107
|
+
return lost;
|
|
108
|
+
const recorded = new Set(sorted.map((s) => s.name));
|
|
109
|
+
const firstPending = names.findIndex((_, i) => !recorded.has(`turn:${turn}:tool:${i}`));
|
|
110
|
+
// Not waiting in a tool call at all, or waiting in one that is not a delegation (re-running it
|
|
111
|
+
// under new code would repeat its effect).
|
|
112
|
+
if (firstPending < 0 || !names[firstPending].startsWith(DELEGATE_TOOL_PREFIX))
|
|
113
|
+
return lost;
|
|
114
|
+
const durable = (child) => child.kind === "coding" || child.nativeExecutionMode === "sandbox";
|
|
115
|
+
if (!children.every(durable))
|
|
116
|
+
return lost;
|
|
117
|
+
let startStep = 0;
|
|
118
|
+
for (const step of sorted) {
|
|
119
|
+
if (step.functionID > startStep)
|
|
120
|
+
break;
|
|
121
|
+
if (step.functionID === startStep)
|
|
122
|
+
startStep += 1;
|
|
123
|
+
}
|
|
124
|
+
// A gap at or before the recorded reply would re-run the model turn: never fork into that.
|
|
125
|
+
if (startStep <= llmStep.functionID)
|
|
126
|
+
return lost;
|
|
127
|
+
return { action: "fork", startStep };
|
|
128
|
+
}
|
|
@@ -15,7 +15,10 @@
|
|
|
15
15
|
* A stalled-but-alive owner can overlap an adopted attempt of the same run:
|
|
16
16
|
* whichever records a step second loses ownership, stops at that step
|
|
17
17
|
* without writing the run, and hands the SDK's conflict back so DBOS parks
|
|
18
|
-
* it as a duplicate execution (see dbosStep and runInsideWorkflow).
|
|
18
|
+
* it as a duplicate execution (see dbosStep and runInsideWorkflow). After a
|
|
19
|
+
* takeover the fork has its own workflow id, so the two never share a step:
|
|
20
|
+
* the original stops the same way when it meets the takeover's cancel
|
|
21
|
+
* (dbosStepFor), and its start() handle writes nothing.
|
|
19
22
|
*/
|
|
20
23
|
import pg from "pg";
|
|
21
24
|
import type { PrismaClient } from "#prisma";
|
|
@@ -44,6 +47,20 @@ export declare function dbosLaunchConfig(config: DbosConfig, executorId: string,
|
|
|
44
47
|
logLevel: "warn";
|
|
45
48
|
systemDatabasePool?: pg.Pool;
|
|
46
49
|
};
|
|
50
|
+
/**
|
|
51
|
+
* The id a takeover gives the fork of workflow `from` under application `version`. Deterministic, so
|
|
52
|
+
* a takeover that forked but never recorded the fork finishes it rather than forking again; and
|
|
53
|
+
* descendants share their ancestor's id as a prefix, which isForkOf relies on.
|
|
54
|
+
*/
|
|
55
|
+
export declare function forkWorkflowId(from: string, version: string): string;
|
|
56
|
+
/** Whether workflow `id` was forked from `from`, directly or through further forks (forkWorkflowId). */
|
|
57
|
+
export declare function isForkOf(id: string, from: string): boolean;
|
|
58
|
+
/**
|
|
59
|
+
* The DBOS workflow id that drives a run: `Run.executionWorkflowId` once the
|
|
60
|
+
* run's workflow was forked under a new application version, otherwise the
|
|
61
|
+
* run id (a run's first workflow is started with its run id).
|
|
62
|
+
*/
|
|
63
|
+
export declare function workflowIdOf(db: Pick<PrismaClient, "run">, runId: string): Promise<string>;
|
|
47
64
|
/**
|
|
48
65
|
* Bind the engine's step boundary to a DBOS checkpointed step. Never
|
|
49
66
|
* retried: a retry would re-spend.
|
|
@@ -59,8 +76,34 @@ export declare function dbosLaunchConfig(config: DbosConfig, executorId: string,
|
|
|
59
76
|
* discarded step's usage, if any, for the log) so executeRun's backstop stops
|
|
60
77
|
* this attempt without writing the run; runInsideWorkflow turns it back into
|
|
61
78
|
* the SDK error.
|
|
79
|
+
*
|
|
80
|
+
* DBOS signals that a replay reached a step whose recorded name differs (the
|
|
81
|
+
* run was recorded by a release whose steps differ, e.g. after a takeover
|
|
82
|
+
* fork) by throwing `DBOSUnexpectedStepError`. Translated into a
|
|
83
|
+
* `StepReplayMismatchError`, which executeRun's backstop settles as `lost`
|
|
84
|
+
* (runInsideWorkflow does it for a mismatch at the load step, which is outside
|
|
85
|
+
* that backstop).
|
|
62
86
|
*/
|
|
63
87
|
export declare const dbosStep: StepRunner;
|
|
88
|
+
/**
|
|
89
|
+
* dbosStep for one run's workflow body. A cancellation of a workflow the run has moved off (its
|
|
90
|
+
* `executionWorkflowId` names a fork of it) is not a stop: recover() took the run over (forked it) and cancelled this,
|
|
91
|
+
* the forked-from workflow, while its owner was still alive. That attempt stops like one that lost
|
|
92
|
+
* a step's checkpoint (RunOwnershipLostError: no terminal write), so the fork, not a reasonless
|
|
93
|
+
* "cancelled" from the original, settles the run. takeOver records the fork's id before it
|
|
94
|
+
* cancels, so the original always reads the new id by the time it sees the cancel.
|
|
95
|
+
*/
|
|
96
|
+
export declare function dbosStepFor(db: Pick<PrismaClient, "run">, runId: string): StepRunner;
|
|
97
|
+
/**
|
|
98
|
+
* dbosStepFor behind the same per-step row read as inline runs (stopAwareStep), failing open. DBOS
|
|
99
|
+
* stops a workflow it was told to cancel, but stop() can cancel only the workflow the run names, and
|
|
100
|
+
* a takeover's fork is named only once recorded: a fork already running before that (or whose
|
|
101
|
+
* record failed) would otherwise keep paying for turns under a run stop() already made terminal.
|
|
102
|
+
* The same read fences out a taken-over original as soon as the run names its fork, before the
|
|
103
|
+
* takeover's cancel lands, and even when that cancel failed: it stops as an ownership loss, without
|
|
104
|
+
* a terminal write or the settle side effects (host checks, status comments) the fork owns.
|
|
105
|
+
*/
|
|
106
|
+
export declare function stopAwareDbosStep(db: Pick<PrismaClient, "run">, runId: string): StepRunner;
|
|
64
107
|
export declare class DbosExecutor implements Executor {
|
|
65
108
|
private readonly providers;
|
|
66
109
|
private readonly config;
|
|
@@ -90,6 +133,12 @@ export declare class DbosExecutor implements Executor {
|
|
|
90
133
|
* the same thing launch() does for this executor's own workflows.
|
|
91
134
|
*/
|
|
92
135
|
recover(handle: PersistedExecutionHandle): Promise<ExecutionRecoveryResult>;
|
|
136
|
+
/**
|
|
137
|
+
* A workflow recorded under another application version: fork it under this one when it is only
|
|
138
|
+
* waiting on durable children (decideTakeover), otherwise answer lost with the mismatch. The
|
|
139
|
+
* fork counts as an adoption attempt, so repeated failed takeovers of one run end lost too.
|
|
140
|
+
*/
|
|
141
|
+
private takeOver;
|
|
93
142
|
/** Workflow body. Public only so the module-level registration can reach it. */
|
|
94
143
|
runInsideWorkflow(runId: string): Promise<void>;
|
|
95
144
|
}
|
|
@@ -15,18 +15,22 @@
|
|
|
15
15
|
* A stalled-but-alive owner can overlap an adopted attempt of the same run:
|
|
16
16
|
* whichever records a step second loses ownership, stops at that step
|
|
17
17
|
* without writing the run, and hands the SDK's conflict back so DBOS parks
|
|
18
|
-
* it as a duplicate execution (see dbosStep and runInsideWorkflow).
|
|
18
|
+
* it as a duplicate execution (see dbosStep and runInsideWorkflow). After a
|
|
19
|
+
* takeover the fork has its own workflow id, so the two never share a step:
|
|
20
|
+
* the original stops the same way when it meets the takeover's cancel
|
|
21
|
+
* (dbosStepFor), and its start() handle writes nothing.
|
|
19
22
|
*/
|
|
20
23
|
import { DBOS, Error as DbosErrors } from "@dbos-inc/dbos-sdk";
|
|
21
24
|
import pg from "pg";
|
|
22
|
-
import { RunOwnershipLostError } from "../engine/types.js";
|
|
23
|
-
import { executeRun, RunCancelledError } from "../../core/runner.js";
|
|
25
|
+
import { RunOwnershipLostError, StepReplayMismatchError } from "../engine/types.js";
|
|
26
|
+
import { executeRun, failNativeRun, loseNativeRun, nativeRunIntegrations, RunCancelledError, stopAwareStep, } from "../../core/runner.js";
|
|
24
27
|
import { markRunFailedFromExecutorError } from "../../core/dispatch.js";
|
|
25
28
|
import { prisma as defaultDb } from "../../core/db.js";
|
|
26
29
|
import { HEARTBEAT_INTERVAL_MS } from "../../core/timing.js";
|
|
30
|
+
import { DRIVABLE } from "../../core/run-status.js";
|
|
27
31
|
import { logger } from "../../core/logger.js";
|
|
28
32
|
import { databaseAuthFromEnv, pgConnectionConfig } from "../../core/pg-auth.js";
|
|
29
|
-
import { ADOPTION_EXHAUSTED_REASON, decideRecovery, shouldGiveUpAdoption, versionMismatchReason, } from "./dbos-status.js";
|
|
33
|
+
import { ADOPTION_EXHAUSTED_REASON, STEPS_CHANGED_REASON, decideRecovery, decideTakeover, shouldGiveUpAdoption, versionMismatchReason, } from "./dbos-status.js";
|
|
30
34
|
export const DBOS_BACKEND = "dbos";
|
|
31
35
|
const dbosLog = logger.child({ module: "dbos-executor" });
|
|
32
36
|
/** DBOS's own system pool size (DEFAULT_POOL_SIZE in the SDK's system_database.js); kept for ours. */
|
|
@@ -87,6 +91,27 @@ function setActiveExecutor(executor) {
|
|
|
87
91
|
* `runInsideWorkflow`'s finally.
|
|
88
92
|
*/
|
|
89
93
|
const cancellationReasons = new Map();
|
|
94
|
+
/**
|
|
95
|
+
* The id a takeover gives the fork of workflow `from` under application `version`. Deterministic, so
|
|
96
|
+
* a takeover that forked but never recorded the fork finishes it rather than forking again; and
|
|
97
|
+
* descendants share their ancestor's id as a prefix, which isForkOf relies on.
|
|
98
|
+
*/
|
|
99
|
+
export function forkWorkflowId(from, version) {
|
|
100
|
+
return `${from}@${version}`;
|
|
101
|
+
}
|
|
102
|
+
/** Whether workflow `id` was forked from `from`, directly or through further forks (forkWorkflowId). */
|
|
103
|
+
export function isForkOf(id, from) {
|
|
104
|
+
return id.startsWith(`${from}@`);
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* The DBOS workflow id that drives a run: `Run.executionWorkflowId` once the
|
|
108
|
+
* run's workflow was forked under a new application version, otherwise the
|
|
109
|
+
* run id (a run's first workflow is started with its run id).
|
|
110
|
+
*/
|
|
111
|
+
export async function workflowIdOf(db, runId) {
|
|
112
|
+
const row = await db.run.findUnique({ where: { id: runId }, select: { executionWorkflowId: true } });
|
|
113
|
+
return row?.executionWorkflowId ?? runId;
|
|
114
|
+
}
|
|
90
115
|
/**
|
|
91
116
|
* Bind the engine's step boundary to a DBOS checkpointed step. Never
|
|
92
117
|
* retried: a retry would re-spend.
|
|
@@ -102,8 +127,47 @@ const cancellationReasons = new Map();
|
|
|
102
127
|
* discarded step's usage, if any, for the log) so executeRun's backstop stops
|
|
103
128
|
* this attempt without writing the run; runInsideWorkflow turns it back into
|
|
104
129
|
* the SDK error.
|
|
130
|
+
*
|
|
131
|
+
* DBOS signals that a replay reached a step whose recorded name differs (the
|
|
132
|
+
* run was recorded by a release whose steps differ, e.g. after a takeover
|
|
133
|
+
* fork) by throwing `DBOSUnexpectedStepError`. Translated into a
|
|
134
|
+
* `StepReplayMismatchError`, which executeRun's backstop settles as `lost`
|
|
135
|
+
* (runInsideWorkflow does it for a mismatch at the load step, which is outside
|
|
136
|
+
* that backstop).
|
|
137
|
+
*/
|
|
138
|
+
export const dbosStep = (name, fn) => runDbosStep(name, fn);
|
|
139
|
+
/**
|
|
140
|
+
* dbosStep for one run's workflow body. A cancellation of a workflow the run has moved off (its
|
|
141
|
+
* `executionWorkflowId` names a fork of it) is not a stop: recover() took the run over (forked it) and cancelled this,
|
|
142
|
+
* the forked-from workflow, while its owner was still alive. That attempt stops like one that lost
|
|
143
|
+
* a step's checkpoint (RunOwnershipLostError: no terminal write), so the fork, not a reasonless
|
|
144
|
+
* "cancelled" from the original, settles the run. takeOver records the fork's id before it
|
|
145
|
+
* cancels, so the original always reads the new id by the time it sees the cancel.
|
|
146
|
+
*/
|
|
147
|
+
export function dbosStepFor(db, runId) {
|
|
148
|
+
const supersedes = async (cancelledWorkflowId) => {
|
|
149
|
+
// A failed read falls back to an ordinary cancellation, as before this check existed.
|
|
150
|
+
const current = await workflowIdOf(db, runId).catch(() => cancelledWorkflowId);
|
|
151
|
+
return isForkOf(current, cancelledWorkflowId);
|
|
152
|
+
};
|
|
153
|
+
return (name, fn) => runDbosStep(name, fn, supersedes);
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* dbosStepFor behind the same per-step row read as inline runs (stopAwareStep), failing open. DBOS
|
|
157
|
+
* stops a workflow it was told to cancel, but stop() can cancel only the workflow the run names, and
|
|
158
|
+
* a takeover's fork is named only once recorded: a fork already running before that (or whose
|
|
159
|
+
* record failed) would otherwise keep paying for turns under a run stop() already made terminal.
|
|
160
|
+
* The same read fences out a taken-over original as soon as the run names its fork, before the
|
|
161
|
+
* takeover's cancel lands, and even when that cancel failed: it stops as an ownership loss, without
|
|
162
|
+
* a terminal write or the settle side effects (host checks, status comments) the fork owns.
|
|
105
163
|
*/
|
|
106
|
-
export
|
|
164
|
+
export function stopAwareDbosStep(db, runId) {
|
|
165
|
+
return stopAwareStep(db, runId, dbosStepFor(db, runId), {
|
|
166
|
+
failOpen: true,
|
|
167
|
+
ownership: { workflowId: () => DBOS.workflowID, forkedFrom: isForkOf },
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
async function runDbosStep(name, fn, supersededBy) {
|
|
107
171
|
let ran;
|
|
108
172
|
try {
|
|
109
173
|
return await DBOS.runStep(async () => {
|
|
@@ -114,6 +178,8 @@ export const dbosStep = async (name, fn) => {
|
|
|
114
178
|
}
|
|
115
179
|
catch (err) {
|
|
116
180
|
if (err instanceof DbosErrors.DBOSWorkflowCancelledError) {
|
|
181
|
+
if (await supersededBy?.(err.workflowID))
|
|
182
|
+
throw new RunOwnershipLostError(name, { cause: err });
|
|
117
183
|
const reason = cancellationReasons.get(err.workflowID);
|
|
118
184
|
throw new RunCancelledError(`Run cancelled: ${reason ?? "no reason given"}`);
|
|
119
185
|
}
|
|
@@ -121,9 +187,11 @@ export const dbosStep = async (name, fn) => {
|
|
|
121
187
|
const discardedUsage = usageOf(ran?.value);
|
|
122
188
|
throw new RunOwnershipLostError(name, { cause: err, ...(discardedUsage ? { discardedUsage } : {}) });
|
|
123
189
|
}
|
|
190
|
+
if (err instanceof DbosErrors.DBOSUnexpectedStepError)
|
|
191
|
+
throw new StepReplayMismatchError(name, { cause: err });
|
|
124
192
|
throw err;
|
|
125
193
|
}
|
|
126
|
-
}
|
|
194
|
+
}
|
|
127
195
|
/**
|
|
128
196
|
* The numeric `usage` fields of a step result, if it has any (an LLM turn's
|
|
129
197
|
* token counts and cost). Tool steps return a string and yield nothing.
|
|
@@ -141,6 +209,19 @@ function usageOf(value) {
|
|
|
141
209
|
* Registered once at module load — DBOS requires registration before
|
|
142
210
|
* launch(). The body must be deterministic between steps; everything
|
|
143
211
|
* non-deterministic in executeRun/engine is inside a runStep.
|
|
212
|
+
*
|
|
213
|
+
* Replay constraints. A run's workflow is replayed both by a same-version
|
|
214
|
+
* resume and, when recover() takes a waiting lead over after a deploy, by a
|
|
215
|
+
* fork that runs this body under the *new* release against steps the *old*
|
|
216
|
+
* release recorded (see decideTakeover). So:
|
|
217
|
+
* - Code between steps must stay deterministic and safe to repeat: it runs
|
|
218
|
+
* again on every replay, now possibly under newer code.
|
|
219
|
+
* - Step names and their order must only change in ways a replay can still
|
|
220
|
+
* follow; a replay that meets a different recorded name ends the run lost
|
|
221
|
+
* with STEPS_CHANGED_REASON (executeRun's backstop, or runInsideWorkflow).
|
|
222
|
+
* - New code must accept step outputs recorded by the previous release (the
|
|
223
|
+
* `turn:N:llm` TurnResult, tool results). A release that changes a recorded
|
|
224
|
+
* shape keeps reading the old one, or a takeover fails at that step (lost).
|
|
144
225
|
*/
|
|
145
226
|
const runWorkflow = DBOS.registerWorkflow(async (runId) => {
|
|
146
227
|
const self = activeExecutor;
|
|
@@ -218,12 +299,16 @@ export class DbosExecutor {
|
|
|
218
299
|
if (!this.launched)
|
|
219
300
|
return;
|
|
220
301
|
this.launched = false;
|
|
221
|
-
if (activeExecutor === this)
|
|
222
|
-
setActiveExecutor(undefined);
|
|
223
302
|
try {
|
|
224
303
|
await DBOS.shutdown({ workflowCompletionTimeoutMS: 5_000 });
|
|
225
304
|
}
|
|
226
305
|
finally {
|
|
306
|
+
// Only now: shutdown first lets the queue runner finish the dispatch it is in, so a workflow
|
|
307
|
+
// the queue claimed just before (a takeover's fork, an adoption) still starts. Without its
|
|
308
|
+
// executor its body would throw, and DBOS could record that as the workflow's outcome: the
|
|
309
|
+
// next process would then mark a run failed that was only waiting to be taken over.
|
|
310
|
+
if (activeExecutor === this)
|
|
311
|
+
setActiveExecutor(undefined);
|
|
227
312
|
await endSystemPool();
|
|
228
313
|
}
|
|
229
314
|
}
|
|
@@ -263,6 +348,15 @@ export class DbosExecutor {
|
|
|
263
348
|
// the same failure — dispatchRun's own `.catch` around `start()` calls
|
|
264
349
|
// it too — which is harmless precisely because it is conditional and
|
|
265
350
|
// idempotent: the second call matches zero rows.
|
|
351
|
+
//
|
|
352
|
+
// A workflow that recover() took over (forked, then cancelled) no longer drives the run: the
|
|
353
|
+
// fork does, and settles it. Its rejection is the takeover's cancel, not the run's failure.
|
|
354
|
+
if ((await workflowIdOf(this.db, runId).catch(() => runId)) !== runId) {
|
|
355
|
+
// The error's name only: this path adds no message text to any output.
|
|
356
|
+
const error = err instanceof Error ? err.name : typeof err;
|
|
357
|
+
dbosLog.info({ runId, error }, "the run's original workflow ended after a takeover; its fork drives the run");
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
266
360
|
await markRunFailedFromExecutorError(this.db, runId, err, this.selfDefects);
|
|
267
361
|
throw err;
|
|
268
362
|
}
|
|
@@ -274,11 +368,43 @@ export class DbosExecutor {
|
|
|
274
368
|
if (!this.launched)
|
|
275
369
|
await this.launch();
|
|
276
370
|
dbosLog.info({ runId, reason }, "cancelling durable run");
|
|
371
|
+
// The terminal write comes first and is conditional (the first writer wins), so the run is
|
|
372
|
+
// cancelled with the caller's reason even when its workflow runs in another process (where the
|
|
373
|
+
// reason map below is empty) or has no workflow at all yet (cancelWorkflow then matches
|
|
374
|
+
// nothing). The workflow's next step then throws, and its own terminal write matches no row.
|
|
375
|
+
// The cancel is still attempted when that settle throws (a host or issue update failing after
|
|
376
|
+
// the write) or the database is unreachable: a cancelled row whose workflow kept going would
|
|
377
|
+
// keep spending. The settle's error, if any, is the one rethrown.
|
|
378
|
+
let settleFailed;
|
|
379
|
+
try {
|
|
380
|
+
const row = await this.db.run.findUnique({ where: { id: runId }, select: { status: true } });
|
|
381
|
+
if (row && (row.status === "pending" || row.status === "running")) {
|
|
382
|
+
await failNativeRun({ runId, db: this.db, providers: this.providers, ...nativeRunIntegrations(this.providers, this.db) }, new RunCancelledError(reason ?? "The run was cancelled."));
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
catch (err) {
|
|
386
|
+
settleFailed = { err };
|
|
387
|
+
}
|
|
277
388
|
// Recorded before the cancel so the workflow's next step boundary can
|
|
278
389
|
// find it: DBOS only reports "cancelled", never why.
|
|
279
|
-
|
|
390
|
+
// Keyed by both ids: dbosStep looks it up by the cancelled workflow's id, which is the forked
|
|
391
|
+
// id for a forked run. A failed read targets the run id, the workflow of any run never forked.
|
|
392
|
+
const workflowId = await workflowIdOf(this.db, runId).catch(() => runId);
|
|
393
|
+
if (reason) {
|
|
280
394
|
cancellationReasons.set(runId, reason);
|
|
281
|
-
|
|
395
|
+
cancellationReasons.set(workflowId, reason);
|
|
396
|
+
}
|
|
397
|
+
try {
|
|
398
|
+
await DBOS.cancelWorkflow(workflowId);
|
|
399
|
+
}
|
|
400
|
+
catch (err) {
|
|
401
|
+
if (!settleFailed)
|
|
402
|
+
throw err;
|
|
403
|
+
// The error's name only: this path adds no message text to any output.
|
|
404
|
+
dbosLog.warn({ runId, error: err instanceof Error ? err.name : typeof err }, "could not cancel the workflow");
|
|
405
|
+
}
|
|
406
|
+
if (settleFailed)
|
|
407
|
+
throw settleFailed.err;
|
|
282
408
|
}
|
|
283
409
|
/**
|
|
284
410
|
* Answer the reconciler for a stale run. Never relaunches: `resume`
|
|
@@ -307,13 +433,12 @@ export class DbosExecutor {
|
|
|
307
433
|
// DBOS gates recovery and dequeue on application_version. A workflow
|
|
308
434
|
// recorded under a different version is never dequeued here, so
|
|
309
435
|
// resuming it would be an unbounded loop that holds the run
|
|
310
|
-
// `running` forever
|
|
436
|
+
// `running` forever. Instead it is forked under this version when it
|
|
437
|
+
// is only waiting on durable children, and otherwise given up at once
|
|
438
|
+
// so the reconciler reaps it (takeOver).
|
|
311
439
|
const mismatch = versionMismatchReason(status?.applicationVersion, DBOS.applicationVersion);
|
|
312
|
-
if (mismatch)
|
|
313
|
-
this.
|
|
314
|
-
dbosLog.warn({ runId: handle.runId, workflowVersion: status?.applicationVersion }, mismatch);
|
|
315
|
-
return { state: "lost", reason: mismatch };
|
|
316
|
-
}
|
|
440
|
+
if (mismatch)
|
|
441
|
+
return this.takeOver(handle, mismatch, status?.applicationVersion);
|
|
317
442
|
// Belt and braces for every other reason a resume can fail to take
|
|
318
443
|
// (an id we can't dequeue for, a queue that never drains): bound the
|
|
319
444
|
// number of adoptions of one run so the reconciler stops re-driving
|
|
@@ -347,6 +472,56 @@ export class DbosExecutor {
|
|
|
347
472
|
return { state: "lost", reason: decision.reason };
|
|
348
473
|
}
|
|
349
474
|
}
|
|
475
|
+
/**
|
|
476
|
+
* A workflow recorded under another application version: fork it under this one when it is only
|
|
477
|
+
* waiting on durable children (decideTakeover), otherwise answer lost with the mismatch. The
|
|
478
|
+
* fork counts as an adoption attempt, so repeated failed takeovers of one run end lost too.
|
|
479
|
+
*/
|
|
480
|
+
async takeOver(handle, mismatch, workflowVersion) {
|
|
481
|
+
const steps = (await DBOS.listWorkflowSteps(handle.id)) ?? [];
|
|
482
|
+
const children = await this.db.run.findMany({
|
|
483
|
+
where: { parentRunId: handle.runId, status: { in: [...DRIVABLE] } },
|
|
484
|
+
select: { status: true, nativeExecutionMode: true, agent: { select: { kind: true } } },
|
|
485
|
+
});
|
|
486
|
+
const decision = decideTakeover(steps.map((s) => ({ functionID: s.functionID, name: s.name, output: s.output })), children.map((c) => ({ status: c.status, kind: c.agent.kind, nativeExecutionMode: c.nativeExecutionMode })), mismatch);
|
|
487
|
+
if (decision.action === "lost") {
|
|
488
|
+
this.resumeAttempts.delete(handle.runId);
|
|
489
|
+
dbosLog.warn({ runId: handle.runId, workflowVersion }, mismatch);
|
|
490
|
+
return { state: "lost", reason: decision.reason };
|
|
491
|
+
}
|
|
492
|
+
const attempts = this.resumeAttempts.get(handle.runId) ?? 0;
|
|
493
|
+
if (shouldGiveUpAdoption(attempts)) {
|
|
494
|
+
this.resumeAttempts.delete(handle.runId);
|
|
495
|
+
dbosLog.warn({ runId: handle.runId, attempts }, "giving up on taking over durable run");
|
|
496
|
+
return { state: "lost", reason: ADOPTION_EXHAUSTED_REASON };
|
|
497
|
+
}
|
|
498
|
+
this.resumeAttempts.set(handle.runId, attempts + 1);
|
|
499
|
+
// Deterministic, so a takeover that forked but never recorded the fork (the run write below
|
|
500
|
+
// failed, or the process died) is finished by the next pass rather than forked a second time:
|
|
501
|
+
// two workflows driving one run would each spend on every turn. DBOS inserts a fork's row with
|
|
502
|
+
// a plain INSERT (bulkForkWorkflows in the SDK's system_database.js), so a repeated id would
|
|
503
|
+
// fail on the key: look first, and treat a lost race the same way.
|
|
504
|
+
const forkedId = forkWorkflowId(handle.id, DBOS.applicationVersion);
|
|
505
|
+
if (!(await DBOS.getWorkflowStatus(forkedId))) {
|
|
506
|
+
try {
|
|
507
|
+
await DBOS.forkWorkflow(handle.id, decision.startStep, {
|
|
508
|
+
newWorkflowID: forkedId,
|
|
509
|
+
applicationVersion: DBOS.applicationVersion,
|
|
510
|
+
});
|
|
511
|
+
}
|
|
512
|
+
catch (err) {
|
|
513
|
+
if (!(await DBOS.getWorkflowStatus(forkedId)))
|
|
514
|
+
throw err;
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
const forked = DBOS.retrieveWorkflow(forkedId);
|
|
518
|
+
await this.db.run.update({ where: { id: handle.runId }, data: { executionWorkflowId: forkedId } });
|
|
519
|
+
// So nothing can dequeue the original again (it stays recorded as forked from).
|
|
520
|
+
await DBOS.cancelWorkflow(handle.id).catch((err) => dbosLog.warn({ err, runId: handle.runId }, "could not cancel the forked-from workflow"));
|
|
521
|
+
dbosLog.info({ runId: handle.runId, from: handle.id, to: forkedId, startStep: decision.startStep, workflowVersion }, "run taken over under this application version");
|
|
522
|
+
void forked.getResult().catch((err) => dbosLog.error({ err, runId: handle.runId }, "taken-over run failed"));
|
|
523
|
+
return { state: "active" };
|
|
524
|
+
}
|
|
350
525
|
/** Workflow body. Public only so the module-level registration can reach it. */
|
|
351
526
|
async runInsideWorkflow(runId) {
|
|
352
527
|
const beat = () => this.db.run.update({ where: { id: runId }, data: { heartbeatAt: new Date() } }).catch(() => {
|
|
@@ -355,23 +530,35 @@ export class DbosExecutor {
|
|
|
355
530
|
await beat();
|
|
356
531
|
const timer = setInterval(() => void beat(), this.heartbeatIntervalMs);
|
|
357
532
|
try {
|
|
358
|
-
await executeRun(runId, this.providers, this.db, undefined,
|
|
533
|
+
await executeRun(runId, this.providers, this.db, undefined, stopAwareDbosStep(this.db, runId));
|
|
359
534
|
}
|
|
360
535
|
catch (err) {
|
|
361
536
|
// An ownership loss leaves the workflow body as the SDK's own conflict error, so DBOS takes
|
|
362
537
|
// its duplicate-execution path: it records nothing and parks this attempt until the winner
|
|
363
538
|
// records the outcome. Any other error escaping here would be recorded as the workflow's
|
|
364
|
-
// outcome while the winner still runs. The runner already logged the loss.
|
|
539
|
+
// outcome while the winner still runs. The runner already logged the loss. A forked-from
|
|
540
|
+
// workflow's own cancellation (dbosStepFor) goes back as itself: DBOS parks that too.
|
|
365
541
|
if (err instanceof RunOwnershipLostError) {
|
|
366
|
-
throw err.cause instanceof DbosErrors.DBOSWorkflowConflictError
|
|
542
|
+
throw err.cause instanceof DbosErrors.DBOSWorkflowConflictError ||
|
|
543
|
+
err.cause instanceof DbosErrors.DBOSWorkflowCancelledError
|
|
367
544
|
? err.cause
|
|
368
|
-
: new DbosErrors.DBOSWorkflowConflictError(runId);
|
|
545
|
+
: new DbosErrors.DBOSWorkflowConflictError(DBOS.workflowID ?? runId);
|
|
546
|
+
}
|
|
547
|
+
// A replay met a step the recorded run does not have at the load step, ahead of executeRun's
|
|
548
|
+
// backstop (which settles a mismatch in any later step itself).
|
|
549
|
+
if (err instanceof StepReplayMismatchError) {
|
|
550
|
+
dbosLog.warn({ err, runId }, "replay no longer matches the recorded run");
|
|
551
|
+
// The same settle as executeRun's backstop: lost (first writer wins), its children stopped.
|
|
552
|
+
await loseNativeRun({ runId, db: this.db, providers: this.providers, ...nativeRunIntegrations(this.providers, this.db) }, STEPS_CHANGED_REASON);
|
|
553
|
+
return;
|
|
369
554
|
}
|
|
370
555
|
throw err;
|
|
371
556
|
}
|
|
372
557
|
finally {
|
|
373
558
|
clearInterval(timer);
|
|
374
559
|
cancellationReasons.delete(runId);
|
|
560
|
+
if (DBOS.workflowID)
|
|
561
|
+
cancellationReasons.delete(DBOS.workflowID);
|
|
375
562
|
}
|
|
376
563
|
}
|
|
377
564
|
}
|
|
@@ -6,43 +6,31 @@
|
|
|
6
6
|
* stop it needs only requested (Run.stopRequestedAt). The scheduler leader
|
|
7
7
|
* carries both out through the server's own executor (`drainDeferredRuns`).
|
|
8
8
|
*
|
|
9
|
-
* Coding dispatch
|
|
10
|
-
* declaration and knowledge files
|
|
11
|
-
*
|
|
12
|
-
* never called here.
|
|
9
|
+
* Coding dispatch is recorded unresolved (`defersCodingResolution`): the worker image,
|
|
10
|
+
* service declaration and knowledge files are resolved by the process that starts the run
|
|
11
|
+
* (the drain's `beforeStart` hook), not here.
|
|
13
12
|
*/
|
|
14
13
|
import type { PrismaClient } from "#prisma";
|
|
15
|
-
import type {
|
|
16
|
-
import type { CodingProvider } from "../../coding/provider.js";
|
|
14
|
+
import type { Executor } from "./types.js";
|
|
17
15
|
export type DeferredExecutorDb = Pick<PrismaClient, "run">;
|
|
18
16
|
export declare class DeferredExecutor implements Executor {
|
|
19
17
|
private readonly db;
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
constructor(db: DeferredExecutorDb,
|
|
23
|
-
/** Coding resolution only (image, service declaration, repository files); never started or stopped. */
|
|
24
|
-
resolver?: Executor | undefined);
|
|
18
|
+
readonly defersCodingResolution = true;
|
|
19
|
+
constructor(db: DeferredExecutorDb);
|
|
25
20
|
start(runId: string): Promise<void>;
|
|
26
21
|
stop(runId: string, reason?: string): Promise<void>;
|
|
27
|
-
resolveCodingWorkerImage(selector: CodingImageSelector): string;
|
|
28
|
-
resolveCodingToolImage(selector: CodingImageSelector): string | null;
|
|
29
|
-
readCodingServiceDeclaration(input: {
|
|
30
|
-
repository: string;
|
|
31
|
-
baseRef: string;
|
|
32
|
-
}): Promise<string | null>;
|
|
33
|
-
readCodingRepositoryFile(input: {
|
|
34
|
-
repository: string;
|
|
35
|
-
baseRef: string;
|
|
36
|
-
path: string;
|
|
37
|
-
maxBytes: number;
|
|
38
|
-
}): Promise<string | null>;
|
|
39
|
-
supportsCodingServices(provider: CodingProvider): boolean;
|
|
40
22
|
}
|
|
41
23
|
export interface DrainDeferredRunsOptions {
|
|
42
24
|
db: Pick<PrismaClient, "run">;
|
|
43
25
|
executor: Executor;
|
|
44
26
|
/** Where a start failure is recorded; defaults to marking the run failed with the error. */
|
|
45
27
|
onStartFailed?: (runId: string, err: unknown) => Promise<void>;
|
|
28
|
+
/**
|
|
29
|
+
* Runs after a row is claimed and before `executor.start`. Resolves inputs the deferring process
|
|
30
|
+
* could not; false means the hook ended the run itself so it must not start. A throw is handled
|
|
31
|
+
* like a start failure.
|
|
32
|
+
*/
|
|
33
|
+
beforeStart?: (runId: string) => Promise<boolean>;
|
|
46
34
|
limit?: number;
|
|
47
35
|
}
|
|
48
36
|
/**
|
|
@@ -6,21 +6,17 @@
|
|
|
6
6
|
* stop it needs only requested (Run.stopRequestedAt). The scheduler leader
|
|
7
7
|
* carries both out through the server's own executor (`drainDeferredRuns`).
|
|
8
8
|
*
|
|
9
|
-
* Coding dispatch
|
|
10
|
-
* declaration and knowledge files
|
|
11
|
-
*
|
|
12
|
-
* never called here.
|
|
9
|
+
* Coding dispatch is recorded unresolved (`defersCodingResolution`): the worker image,
|
|
10
|
+
* service declaration and knowledge files are resolved by the process that starts the run
|
|
11
|
+
* (the drain's `beforeStart` hook), not here.
|
|
13
12
|
*/
|
|
14
13
|
import { logger } from "../../core/logger.js";
|
|
15
14
|
const deferredLog = logger.child({ module: "deferred-executor" });
|
|
16
15
|
export class DeferredExecutor {
|
|
17
16
|
db;
|
|
18
|
-
|
|
19
|
-
constructor(db
|
|
20
|
-
/** Coding resolution only (image, service declaration, repository files); never started or stopped. */
|
|
21
|
-
resolver) {
|
|
17
|
+
defersCodingResolution = true;
|
|
18
|
+
constructor(db) {
|
|
22
19
|
this.db = db;
|
|
23
|
-
this.resolver = resolver;
|
|
24
20
|
}
|
|
25
21
|
async start(runId) {
|
|
26
22
|
await this.db.run.updateMany({ where: { id: runId, status: "pending" }, data: { startDeferredAt: new Date() } });
|
|
@@ -43,25 +39,6 @@ export class DeferredExecutor {
|
|
|
43
39
|
data: { stopRequestedAt: new Date(), stopRequestReason: reason ?? null },
|
|
44
40
|
});
|
|
45
41
|
}
|
|
46
|
-
resolveCodingWorkerImage(selector) {
|
|
47
|
-
if (!this.resolver?.resolveCodingWorkerImage)
|
|
48
|
-
throw new Error("coding_execution_not_configured");
|
|
49
|
-
return this.resolver.resolveCodingWorkerImage(selector);
|
|
50
|
-
}
|
|
51
|
-
resolveCodingToolImage(selector) {
|
|
52
|
-
if (!this.resolver?.resolveCodingToolImage)
|
|
53
|
-
throw new Error("coding_execution_not_configured");
|
|
54
|
-
return this.resolver.resolveCodingToolImage(selector);
|
|
55
|
-
}
|
|
56
|
-
async readCodingServiceDeclaration(input) {
|
|
57
|
-
return this.resolver?.readCodingServiceDeclaration?.(input) ?? null;
|
|
58
|
-
}
|
|
59
|
-
async readCodingRepositoryFile(input) {
|
|
60
|
-
return this.resolver?.readCodingRepositoryFile?.(input) ?? null;
|
|
61
|
-
}
|
|
62
|
-
supportsCodingServices(provider) {
|
|
63
|
-
return this.resolver?.supportsCodingServices?.(provider) === true;
|
|
64
|
-
}
|
|
65
42
|
}
|
|
66
43
|
/**
|
|
67
44
|
* Starts runs a deferred executor dispatched, and stops runs it asked to stop, through the
|
|
@@ -88,7 +65,11 @@ export async function drainDeferredRuns(options) {
|
|
|
88
65
|
continue;
|
|
89
66
|
started += 1;
|
|
90
67
|
// Not awaited: an in-process executor's start runs the whole run.
|
|
91
|
-
void
|
|
68
|
+
void (async () => {
|
|
69
|
+
if (options.beforeStart && !(await options.beforeStart(id)))
|
|
70
|
+
return;
|
|
71
|
+
await executor.start(id);
|
|
72
|
+
})().catch(async (err) => {
|
|
92
73
|
deferredLog.error({ err, runId: id }, "deferred run failed to start");
|
|
93
74
|
await (options.onStartFailed?.(id, err) ??
|
|
94
75
|
db.run.updateMany({
|