@wardby/cli 0.5.3 → 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/.env.example +33 -0
- package/README.md +25 -14
- package/deploy/local/docker-compose.quickstart-coding.yml +13 -0
- package/dist/claude-coding-worker/context.d.ts +22 -0
- package/dist/claude-coding-worker/context.js +92 -0
- package/dist/claude-coding-worker/driver.d.ts +27 -3
- package/dist/claude-coding-worker/driver.js +161 -20
- package/dist/claude-coding-worker/main.js +34 -6
- package/dist/claude-coding-worker/sdk.js +25 -5
- 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 +6 -2
- package/dist/cli-native-mode.d.ts +9 -0
- package/dist/cli-native-mode.js +20 -0
- package/dist/cli.d.ts +3 -1
- package/dist/cli.js +161 -11
- package/dist/coding/claude-context.d.ts +45 -0
- package/dist/coding/claude-context.js +434 -0
- 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 +50 -118
- package/dist/coding/profile.js +51 -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 +292 -270
- package/dist/coding/protocol.js +322 -15
- 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 +16 -3
- package/dist/coding-worker/artifact.js +5 -3
- 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 +118 -38
- package/dist/coding-worker/errors.d.ts +1 -0
- package/dist/coding-worker/errors.js +32 -0
- package/dist/coding-worker/sdk.d.ts +6 -0
- package/dist/coding-worker/sdk.js +1 -0
- package/dist/coding-worker/skills.d.ts +23 -0
- package/dist/coding-worker/skills.js +117 -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 +13 -0
- package/dist/config/dependency-cache.d.ts +23 -0
- package/dist/config/dependency-cache.js +32 -0
- package/dist/config/github-allowed-accounts.d.ts +15 -0
- package/dist/config/github-allowed-accounts.js +36 -0
- package/dist/config/low-power.d.ts +12 -0
- package/dist/config/low-power.js +18 -0
- package/dist/config/mobile.d.ts +12 -0
- package/dist/config/mobile.js +18 -0
- package/dist/config/providers.d.ts +65 -3
- package/dist/config/providers.js +147 -11
- 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/db.d.ts +16 -6
- package/dist/core/db.js +12 -10
- 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 +53 -0
- package/dist/core/dispatch.js +167 -57
- package/dist/core/engine-native.js +10 -6
- 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 +67 -5
- package/dist/core/host-events.js +571 -37
- 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 +151 -20
- package/dist/core/issue-events.d.ts +1 -1
- package/dist/core/issue-events.js +16 -11
- package/dist/core/issue-status.d.ts +1 -1
- package/dist/core/issue-status.js +68 -29
- 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 +130 -0
- package/dist/core/merge-order-check.js +343 -0
- package/dist/core/notification-dispatcher.d.ts +70 -0
- package/dist/core/notification-dispatcher.js +368 -0
- package/dist/core/notification-templates.d.ts +19 -0
- package/dist/core/notification-templates.js +139 -0
- package/dist/core/notifications.d.ts +10 -0
- package/dist/core/notifications.js +38 -0
- package/dist/core/pg-auth.d.ts +22 -0
- package/dist/core/pg-auth.js +152 -0
- package/dist/core/power-composition.d.ts +16 -0
- package/dist/core/power-composition.js +37 -0
- package/dist/core/power-idle.d.ts +14 -0
- package/dist/core/power-idle.js +30 -0
- package/dist/core/power.d.ts +71 -0
- package/dist/core/power.js +202 -0
- package/dist/core/pull-request-state-sync.js +2 -2
- package/dist/core/reconciler.d.ts +8 -5
- package/dist/core/reconciler.js +37 -6
- package/dist/core/related-pull-requests.d.ts +59 -7
- package/dist/core/related-pull-requests.js +140 -25
- package/dist/core/reply-parts.d.ts +21 -0
- package/dist/core/reply-parts.js +73 -0
- package/dist/core/repo-access.d.ts +15 -0
- package/dist/core/repo-access.js +66 -0
- package/dist/core/review-fix.d.ts +38 -1
- package/dist/core/review-fix.js +209 -7
- package/dist/core/review-host-checks.d.ts +1 -1
- package/dist/core/review-host-tools.d.ts +23 -0
- package/dist/core/review-host-tools.js +156 -14
- 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 +384 -5
- package/dist/core/runner.js +1244 -372
- 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 +39 -0
- package/dist/core/secret-broker-config.js +112 -0
- 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/secrets.d.ts +39 -4
- package/dist/core/secrets.js +148 -40
- package/dist/core/stop-run.d.ts +46 -0
- package/dist/core/stop-run.js +61 -0
- package/dist/core/workflow-event-recorder.d.ts +14 -0
- package/dist/core/workflow-event-recorder.js +120 -0
- package/dist/core/workflow-events.d.ts +94 -0
- package/dist/core/workflow-events.js +48 -0
- package/dist/core/workflow-run-events.d.ts +18 -0
- package/dist/core/workflow-run-events.js +75 -0
- 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 +92 -3
- package/dist/generated/prisma/client.d.ts +92 -3
- package/dist/generated/prisma/commonInputTypes.d.ts +364 -54
- package/dist/generated/prisma/enums.d.ts +37 -0
- package/dist/generated/prisma/enums.js +31 -0
- package/dist/generated/prisma/internal/class.d.ts +132 -0
- package/dist/generated/prisma/internal/class.js +4 -4
- package/dist/generated/prisma/internal/prismaNamespace.d.ts +1332 -142
- package/dist/generated/prisma/internal/prismaNamespace.js +227 -5
- package/dist/generated/prisma/internal/prismaNamespaceBrowser.d.ts +234 -0
- package/dist/generated/prisma/internal/prismaNamespaceBrowser.js +227 -5
- package/dist/generated/prisma/models/Agent.d.ts +464 -7
- package/dist/generated/prisma/models/AgentRepository.d.ts +42 -1
- package/dist/generated/prisma/models/CodingAgentProfile.d.ts +301 -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 +1841 -1
- package/dist/generated/prisma/models/DeferredReview.d.ts +78 -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 +1713 -0
- package/dist/generated/prisma/models/NativeGatewayCall.js +1 -0
- package/dist/generated/prisma/models/NativeGatewaySession.d.ts +1551 -0
- package/dist/generated/prisma/models/NativeGatewaySession.js +1 -0
- package/dist/generated/prisma/models/NativeWarmWorker.d.ts +1296 -0
- package/dist/generated/prisma/models/NativeWarmWorker.js +1 -0
- package/dist/generated/prisma/models/NotificationChannel.d.ts +1543 -0
- package/dist/generated/prisma/models/NotificationChannel.js +1 -0
- package/dist/generated/prisma/models/NotificationDelivery.d.ts +1502 -0
- package/dist/generated/prisma/models/NotificationDelivery.js +1 -0
- package/dist/generated/prisma/models/NotificationThread.d.ts +1090 -0
- package/dist/generated/prisma/models/NotificationThread.js +1 -0
- 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 +1619 -58
- package/dist/generated/prisma/models/RunBudgetEvent.d.ts +1211 -0
- package/dist/generated/prisma/models/RunBudgetEvent.js +1 -0
- package/dist/generated/prisma/models/RunHostCheck.d.ts +36 -1
- package/dist/generated/prisma/models/RunTranscript.d.ts +1339 -0
- package/dist/generated/prisma/models/RunTranscript.js +1 -0
- package/dist/generated/prisma/models/Secret.d.ts +37 -1
- package/dist/generated/prisma/models/SecretBrokerChange.d.ts +1101 -0
- package/dist/generated/prisma/models/SecretBrokerChange.js +1 -0
- package/dist/generated/prisma/models/WorkflowEvent.d.ts +1422 -0
- package/dist/generated/prisma/models/WorkflowEvent.js +1 -0
- package/dist/generated/prisma/models.d.ts +12 -0
- package/dist/help/build.js +4 -2
- package/dist/help/catalog.d.ts +1 -1
- package/dist/help/catalog.js +31 -6
- package/dist/help-index.json +2584 -146
- package/dist/import/create.js +49 -3
- package/dist/import/neutral-schema.d.ts +50 -277
- package/dist/import/neutral-schema.js +7 -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 +11 -2
- package/dist/mcp/auth/access.js +15 -1
- package/dist/mcp/auth/ownership.d.ts +7 -7
- package/dist/mcp/auth/repo-authorization.js +14 -0
- package/dist/mcp/auth/resource-server.d.ts +2 -1
- package/dist/mcp/auth/resource-server.js +7 -2
- package/dist/mcp/auth/self-hosted/browser.d.ts +1 -1
- package/dist/mcp/auth/self-hosted/browser.js +19 -6
- 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/context.d.ts +6 -0
- package/dist/mcp/host-events/github-ingress.d.ts +25 -1
- package/dist/mcp/host-events/github-ingress.js +70 -1
- package/dist/mcp/index.d.ts +19 -0
- package/dist/mcp/index.js +117 -20
- 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 +277 -24
- 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.d.ts +2 -0
- package/dist/mcp/tools/notification-channels.js +293 -0
- package/dist/mcp/tools/packages.d.ts +2 -0
- package/dist/mcp/tools/packages.js +195 -0
- package/dist/mcp/tools/power.d.ts +2 -0
- package/dist/mcp/tools/power.js +77 -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.d.ts +0 -6
- package/dist/mcp/tools/runs.js +41 -14
- package/dist/mcp/tools/scheduling.js +3 -1
- package/dist/mcp/tools/secret-broker-compat.d.ts +12 -0
- package/dist/mcp/tools/secret-broker-compat.js +23 -0
- package/dist/mcp/tools/secret-elicitation-form.d.ts +22 -2
- package/dist/mcp/tools/secret-elicitation-form.js +205 -10
- package/dist/mcp/tools/secret-elicitation-server.d.ts +6 -0
- package/dist/mcp/tools/secret-elicitation-server.js +10 -54
- package/dist/mcp/tools/secret-elicitation.d.ts +38 -3
- package/dist/mcp/tools/secret-elicitation.js +110 -12
- package/dist/mcp/tools/secrets.js +186 -9
- 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 +2 -2
- package/dist/mcp/tools/tools.js +47 -11
- 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/composition.d.ts +23 -0
- package/dist/native-worker/composition.js +77 -0
- package/dist/native-worker/docker-isolation.d.ts +65 -0
- package/dist/native-worker/docker-isolation.js +183 -0
- package/dist/native-worker/docker-launcher.d.ts +110 -0
- package/dist/native-worker/docker-launcher.js +297 -0
- package/dist/native-worker/gateway-server.d.ts +77 -0
- package/dist/native-worker/gateway-server.js +412 -0
- package/dist/native-worker/gateway.d.ts +70 -0
- package/dist/native-worker/gateway.js +263 -0
- package/dist/native-worker/http-server.d.ts +24 -0
- package/dist/native-worker/http-server.js +113 -0
- package/dist/native-worker/http-transport.d.ts +17 -0
- package/dist/native-worker/http-transport.js +88 -0
- package/dist/native-worker/kubernetes-isolation.d.ts +90 -0
- package/dist/native-worker/kubernetes-isolation.js +218 -0
- package/dist/native-worker/kubernetes-launcher.d.ts +106 -0
- package/dist/native-worker/kubernetes-launcher.js +375 -0
- package/dist/native-worker/launch.d.ts +62 -0
- package/dist/native-worker/launch.js +49 -0
- package/dist/native-worker/ledger.d.ts +125 -0
- package/dist/native-worker/ledger.js +245 -0
- package/dist/native-worker/loopback.d.ts +12 -0
- package/dist/native-worker/loopback.js +44 -0
- package/dist/native-worker/main.d.ts +8 -0
- package/dist/native-worker/main.js +85 -0
- package/dist/native-worker/protocol.d.ts +296 -0
- package/dist/native-worker/protocol.js +246 -0
- package/dist/native-worker/sandbox-executor.d.ts +83 -0
- package/dist/native-worker/sandbox-executor.js +284 -0
- 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 +29 -0
- package/dist/native-worker/stdio.js +189 -0
- package/dist/native-worker/warm-delivery.d.ts +16 -0
- package/dist/native-worker/warm-delivery.js +33 -0
- package/dist/native-worker/warm-pool-ledger.d.ts +39 -0
- package/dist/native-worker/warm-pool-ledger.js +73 -0
- package/dist/native-worker/warm-pool.d.ts +107 -0
- package/dist/native-worker/warm-pool.js +283 -0
- package/dist/native-worker/worker.d.ts +17 -0
- package/dist/native-worker/worker.js +141 -0
- package/dist/observability/metrics.js +6 -1
- package/dist/providers/auth/authorization-server.d.ts +3 -0
- package/dist/providers/auth/index.js +2 -0
- package/dist/providers/auth/self-hosted.d.ts +14 -2
- package/dist/providers/auth/self-hosted.js +54 -4
- package/dist/providers/chat/fake.d.ts +46 -0
- package/dist/providers/chat/fake.js +39 -0
- package/dist/providers/chat/index.d.ts +4 -0
- package/dist/providers/chat/index.js +8 -0
- package/dist/providers/chat/slack.d.ts +37 -0
- package/dist/providers/chat/slack.js +105 -0
- package/dist/providers/chat/types.d.ts +42 -0
- package/dist/providers/chat/types.js +19 -0
- 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 +36 -2
- package/dist/providers/engine/types.js +39 -0
- package/dist/providers/executor/composition.d.ts +31 -1
- package/dist/providers/executor/composition.js +81 -8
- package/dist/providers/executor/container.d.ts +385 -9
- package/dist/providers/executor/container.js +1234 -99
- 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 +91 -0
- package/dist/providers/executor/dbos.js +319 -26
- package/dist/providers/executor/deferred.d.ts +44 -0
- package/dist/providers/executor/deferred.js +100 -0
- 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 +39 -10
- package/dist/providers/executor/routing.js +77 -32
- package/dist/providers/executor/types.d.ts +25 -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 +29 -4
- package/dist/providers/jobs/fake-kubernetes-api.d.ts +23 -2
- package/dist/providers/jobs/fake-kubernetes-api.js +45 -1
- package/dist/providers/jobs/kubernetes-api.d.ts +25 -1
- package/dist/providers/jobs/kubernetes-api.js +17 -0
- package/dist/providers/jobs/kubernetes-client.d.ts +10 -1
- package/dist/providers/jobs/kubernetes-client.js +61 -3
- package/dist/providers/jobs/kubernetes-dry-run-fixture.d.ts +6 -1
- package/dist/providers/jobs/kubernetes-dry-run-fixture.js +6 -3
- package/dist/providers/jobs/kubernetes-isolation.d.ts +5 -1
- package/dist/providers/jobs/kubernetes-isolation.js +23 -1
- package/dist/providers/jobs/kubernetes-platform.d.ts +4 -1
- package/dist/providers/jobs/kubernetes-platform.js +38 -1
- package/dist/providers/jobs/kubernetes-preflight.d.ts +2 -1
- package/dist/providers/jobs/kubernetes-preflight.js +4 -2
- package/dist/providers/jobs/kubernetes.d.ts +42 -2
- package/dist/providers/jobs/kubernetes.js +178 -19
- package/dist/providers/jobs/metadata-probes.d.ts +11 -0
- package/dist/providers/jobs/metadata-probes.js +13 -0
- package/dist/providers/jobs/types.d.ts +42 -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 +39 -2
- package/dist/providers/review-host/github.js +274 -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 +157 -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 +246 -12
- package/dist/providers/vcs/github.js +521 -61
- 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 +21 -3
- package/dist/quickstart/coding-seed.js +283 -3
- package/dist/quickstart/images.d.ts +2 -0
- package/dist/quickstart/images.js +7 -1
- 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/brokered-fetch.d.ts +23 -0
- package/dist/sandbox/brokered-fetch.js +37 -0
- 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 +35 -0
- package/dist/sandbox/host-functions.js +150 -91
- package/dist/sandbox/prelude.js +7 -1
- package/dist/sandbox/run-in-sandbox.d.ts +19 -0
- package/dist/sandbox/run-in-sandbox.js +28 -10
- package/dist/sandbox/safe-fetch.d.ts +2 -0
- package/dist/sandbox/safe-fetch.js +3 -1
- package/dist/sandbox/secret-broker-sigv4.d.ts +2 -0
- package/dist/sandbox/secret-broker-sigv4.js +49 -0
- package/dist/sandbox/secret-broker.d.ts +34 -0
- package/dist/sandbox/secret-broker.js +161 -0
- package/dist/sandbox/tool-capabilities.d.ts +10 -30
- package/dist/sandbox/user-tool.d.ts +14 -0
- package/dist/sandbox/user-tool.js +32 -0
- package/dist/sandbox/zod-params.d.ts +37 -5
- package/dist/sandbox/zod-params.js +181 -26
- package/dist/serve.js +25 -0
- package/dist/viewer/api-schema.d.ts +372 -2454
- package/dist/viewer/api-schema.js +29 -0
- package/dist/viewer/build-schemas.js +10 -3
- package/dist/viewer/event-bus.d.ts +6 -0
- package/dist/viewer/event-bus.js +16 -7
- package/dist/viewer/graph.d.ts +5 -2
- package/dist/viewer/graph.js +7 -2
- package/dist/viewer/http.d.ts +0 -1
- package/dist/viewer/http.js +2 -2
- package/dist/viewer/infra.js +25 -2
- package/dist/viewer/limits.d.ts +4 -0
- package/dist/viewer/limits.js +4 -0
- package/dist/viewer/openapi.d.ts +15 -0
- package/dist/viewer/openapi.js +170 -0
- package/dist/wardby-bin.js +10 -0
- package/docs/README.md +7 -0
- package/docs/agent-recipes.md +114 -27
- package/docs/architecture-runtime.md +16 -1
- package/docs/code-review-agents.md +689 -45
- package/docs/coding-agent-setup.md +602 -12
- package/docs/coding-packages.md +1037 -92
- package/docs/coding-services.md +30 -13
- package/docs/coding-worker-byo-images.md +218 -6
- package/docs/coding-worker-isolation.md +173 -44
- package/docs/getting-started-gke.md +332 -129
- package/docs/getting-started-identity-provider.md +31 -11
- package/docs/getting-started.md +14 -4
- package/docs/jira-agents.md +22 -9
- 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 +731 -0
- package/docs/observability.md +4 -0
- package/docs/release-verification.md +9 -0
- package/docs/security-deployment.md +146 -14
- package/docs/slack-notifications.md +268 -0
- package/docs/tool-secrets.md +284 -0
- package/docs/viewer-api.md +46 -5
- package/docs/viewer-api.openapi.yml +1404 -0
- package/help/admin-viewer.md +29 -3
- package/help/agent-recipes.md +27 -0
- package/help/architecture-agent.md +11 -6
- package/help/brokered-secrets.md +79 -0
- package/help/build-claude-code-image.md +131 -0
- package/help/build-worker-image.md +6 -7
- package/help/builder-agent.md +19 -6
- package/help/code-review-agents.md +243 -10
- 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 +23 -9
- package/help/deployment-targets.md +25 -14
- package/help/errors/budget-exhausted.md +187 -0
- package/help/errors/budget-group-exhausted.md +5 -1
- package/help/errors/coding-provider-not-configured.md +2 -2
- 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/database-auth-invalid.md +28 -0
- 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 +29 -0
- package/help/errors/graph-incomplete.md +43 -0
- package/help/errors/graph-limit.md +43 -0
- package/help/errors/low-power-disabled.md +29 -0
- package/help/errors/low-power-scale-forbidden.md +35 -0
- package/help/errors/low-power-wake-timeout.md +30 -0
- package/help/errors/merge-conflict.md +49 -0
- package/help/errors/mobile-disabled.md +31 -0
- package/help/errors/native-sandbox-capacity.md +22 -0
- package/help/errors/native-sandbox-deadline-exceeded.md +19 -0
- package/help/errors/native-sandbox-docker-failed.md +20 -0
- package/help/errors/native-sandbox-gateway-error.md +20 -0
- package/help/errors/native-sandbox-gateway-unavailable.md +21 -0
- package/help/errors/native-sandbox-image-not-pinned.md +19 -0
- package/help/errors/native-sandbox-isolation-mismatch.md +20 -0
- package/help/errors/native-sandbox-launch-abandoned.md +21 -0
- package/help/errors/native-sandbox-network-unenforced.md +22 -0
- package/help/errors/native-sandbox-requires-catalog.md +31 -0
- package/help/errors/native-sandbox-unavailable.md +19 -0
- package/help/errors/native-sandbox-warm-delivery-failed.md +21 -0
- package/help/errors/native-sandbox-worker-exited.md +21 -0
- package/help/errors/native-sandbox-worker-lost.md +20 -0
- package/help/errors/native-sandbox-worker-unready.md +22 -0
- 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-broker-body-invalid.md +26 -0
- package/help/errors/secret-broker-changed.md +20 -0
- package/help/errors/secret-broker-config-invalid.md +30 -0
- package/help/errors/secret-broker-conflict.md +27 -0
- package/help/errors/secret-broker-destination-denied.md +28 -0
- package/help/errors/secret-broker-value-invalid.md +28 -0
- package/help/errors/secret-brokered.md +24 -0
- package/help/errors/secret-in-value.md +69 -0
- package/help/errors/secret-not-brokered.md +25 -0
- package/help/errors/slack-auth-failed.md +48 -0
- package/help/errors/slack-channel-unreachable.md +39 -0
- package/help/errors/slack-not-configured.md +33 -0
- package/help/errors/target-merge.md +40 -0
- package/help/fix-merge-conflicts.md +73 -0
- package/help/getting-started.md +4 -5
- package/help/github-app-setup.md +132 -0
- package/help/github.md +40 -1
- package/help/identity-and-access.md +7 -1
- package/help/jira.md +23 -6
- package/help/knowledge.md +16 -7
- package/help/local-repositories.md +3 -4
- package/help/low-power-mode.md +131 -0
- package/help/merge-order-check.md +122 -0
- package/help/mobile-app.md +54 -0
- package/help/native-capabilities.md +29 -1
- package/help/native-sandbox.md +150 -0
- package/help/observability.md +8 -0
- package/help/operating-agents.md +5 -0
- package/help/package-audit-mode.md +91 -0
- package/help/related-pull-requests.md +80 -16
- package/help/repo-instructions-and-skills.md +91 -0
- package/help/resume-runs-locally.md +131 -0
- package/help/review-fix-rounds.md +43 -1
- package/help/sharing-agents.md +59 -0
- package/help/slack-notifications.md +76 -0
- 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 +99 -7
- package/package.json +27 -13
- package/prisma/migrations/20261009000003_native_execution_mode/migration.sql +12 -0
- package/prisma/migrations/20261009000004_native_gateway_ledger/migration.sql +65 -0
- package/prisma/migrations/20261009000005_native_gateway_call_child_agent/migration.sql +5 -0
- package/prisma/migrations/20261009000006_run_deferred_start_stop/migration.sql +14 -0
- package/prisma/migrations/20261009000007_native_gateway_network_ready/migration.sql +5 -0
- package/prisma/migrations/20261010000001_workflow_notifications/migration.sql +99 -0
- package/prisma/migrations/20261011000001_native_gateway_session_status_index/migration.sql +2 -0
- package/prisma/migrations/20261012000001_native_warm_worker/migration.sql +32 -0
- package/prisma/migrations/20261013000001_coding_run_merge_order/migration.sql +2 -0
- package/prisma/migrations/20261013000001_secret_broker/migration.sql +17 -0
- package/prisma/migrations/20261013000002_deferred_review_request/migration.sql +4 -0
- package/prisma/migrations/20261014000000_repo_skills/migration.sql +15 -0
- package/prisma/migrations/20261015000001_coding_run_merge_order_index/migration.sql +2 -0
- package/prisma/migrations/20261015000002_no_change_rereview/migration.sql +2 -0
- 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 +631 -146
- package/dist/sandbox/generated/zod-to-json-schema.bundle.js +0 -5
package/dist/core/host-events.js
CHANGED
|
@@ -1,12 +1,25 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Host-neutral routing of code-review host events (GitHub App webhooks today)
|
|
3
|
+
* to the native agents linked to that repository. Adapters normalise their
|
|
4
|
+
* payloads into HostEvent (providers/review-host/github-events.ts); this
|
|
5
|
+
* module decides which agents run. See
|
|
6
|
+
* docs/private/2026-09-25-code-review-host-design.md §6.3.
|
|
7
|
+
*/
|
|
8
|
+
import { Prisma } from "#prisma";
|
|
9
|
+
import { linkedPullRequestAttribution, openingRunAttribution, resolveWorkItem, RESPONSE_PATH_SNAPSHOT_BUDGET, } from "./attribution.js";
|
|
10
|
+
import { headStoppedAtBudget, pushingRunInFlight } from "./budget-stopped-head.js";
|
|
2
11
|
import { checkContinuation, dispatchRun } from "./dispatch.js";
|
|
3
12
|
import { mentionStatusRow, postMentionStatus } from "./host-status.js";
|
|
4
|
-
import { handlePullRequestClosed } from "./issue-bridge.js";
|
|
13
|
+
import { githubIssueKey, handlePullRequestClosed } from "./issue-bridge.js";
|
|
5
14
|
import { logger } from "./logger.js";
|
|
6
|
-
import {
|
|
15
|
+
import { withoutMarkdownCode } from "./markdown-code.js";
|
|
16
|
+
import { maybeMarkReady } from "./mark-ready.js";
|
|
17
|
+
import { syncMergeOrderChecksForPullRequest } from "./merge-order-check.js";
|
|
18
|
+
import { MAX_SIBLING_HINTS, openPullRequestHints, openSiblings, openSiblingsForIssue, runTreeRoot, SIBLING_GUIDANCE, } from "./related-pull-requests.js";
|
|
7
19
|
import { requiredLevel } from "./repo-access.js";
|
|
8
20
|
import { composeTaskOverride } from "./untrusted-content.js";
|
|
9
21
|
import { DEFAULT_KNOWLEDGE_BUNDLE_PATH, parseConcept, RESERVED_BUNDLE_FILES, } from "../knowledge/concept.js";
|
|
22
|
+
import { KNOWLEDGE_CONFIG_MAX_BYTES, KNOWLEDGE_CONFIG_PATH, resolveKnowledgeLocation } from "../knowledge/location.js";
|
|
10
23
|
import { driftScope } from "../knowledge/relevance.js";
|
|
11
24
|
import { spanHash } from "../knowledge/span-hash.js";
|
|
12
25
|
const log = logger.child({ module: "host-events" });
|
|
@@ -22,11 +35,14 @@ export const DEFERRED_REVIEW_BATCH = 50;
|
|
|
22
35
|
function escapeRegExp(value) {
|
|
23
36
|
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
24
37
|
}
|
|
38
|
+
/** A `@app review` outside code: quoting the command in code documents it. */
|
|
25
39
|
export function isReviewCommand(body, mentionHandle) {
|
|
26
|
-
return new RegExp(`(^|[^\\w-])@${escapeRegExp(mentionHandle)}\\s+review(?![\\w-])`, "i").test(body);
|
|
40
|
+
return new RegExp(`(^|[^\\w-])@${escapeRegExp(mentionHandle)}\\s+review(?![\\w-])`, "i").test(withoutMarkdownCode(body));
|
|
27
41
|
}
|
|
28
42
|
const MAX_BUNDLE_FILES = 200;
|
|
29
43
|
const BUNDLE_FILE_LINES = 2000;
|
|
44
|
+
/** `.wardby/knowledge.yaml` is one short key; a longer read is refused as too large. */
|
|
45
|
+
const KNOWLEDGE_CONFIG_MAX_LINES = 200;
|
|
30
46
|
const MAX_LISTED_CHANGES = 200;
|
|
31
47
|
/** The whole bundle load (listing plus reads) must finish inside GitHub's ~10 s webhook window. */
|
|
32
48
|
const BUNDLE_LOAD_DEADLINE_MS = 4000;
|
|
@@ -40,7 +56,7 @@ const BUNDLE_INCOMPLETE_TEXT = "The knowledge bundle could not be fully read; tr
|
|
|
40
56
|
* commits, so they and the affected concept paths travel as untrusted context.
|
|
41
57
|
* Commit messages and author names are never included.
|
|
42
58
|
*/
|
|
43
|
-
export function mergeTaskText(event, scope, bundleComplete = true, citations = new Map()) {
|
|
59
|
+
export function mergeTaskText(event, scope, bundleComplete = true, citations = new Map(), bundlePath = DEFAULT_KNOWLEDGE_BUNDLE_PATH) {
|
|
44
60
|
const statusOf = (concept) => citations.get(concept) ?? { state: "unverified" };
|
|
45
61
|
const changed = event.changedPathsComplete
|
|
46
62
|
? `Changed files:\n${[
|
|
@@ -52,7 +68,7 @@ export function mergeTaskText(event, scope, bundleComplete = true, citations = n
|
|
|
52
68
|
: "The changed-file list is incomplete (GitHub includes at most 2048 commits per push and the list is capped at 1000 paths); treat every knowledge concept as possibly affected.";
|
|
53
69
|
const listed = scope
|
|
54
70
|
? `Knowledge concepts whose citations, affects globs, or files changed (${scope.reason}):\n` +
|
|
55
|
-
scope.concepts.map((p) => `- ${
|
|
71
|
+
scope.concepts.map((p) => `- ${bundlePath}/${p} — ${describeStatus(statusOf(p))}`).join("\n")
|
|
56
72
|
: null;
|
|
57
73
|
let context;
|
|
58
74
|
if (!bundleComplete)
|
|
@@ -67,7 +83,7 @@ export function mergeTaskText(event, scope, bundleComplete = true, citations = n
|
|
|
67
83
|
if (scope && scope.concepts.length > 0) {
|
|
68
84
|
const states = scope.concepts.map((p) => statusOf(p).state);
|
|
69
85
|
const count = (state) => states.filter((x) => x === state).length;
|
|
70
|
-
const onlyKnowledge = event.changedPathsComplete && event.changedPaths.every((p) => p.startsWith(`${
|
|
86
|
+
const onlyKnowledge = event.changedPathsComplete && event.changedPaths.every((p) => p.startsWith(`${bundlePath}/`));
|
|
71
87
|
summary =
|
|
72
88
|
`Citation check at ${event.after.slice(0, 12)}: ${count("verified")} of ${states.length} affected concepts verified, ` +
|
|
73
89
|
`${count("stale")} stale, ${count("unverified")} not verified. Only knowledge files changed: ${onlyKnowledge ? "yes" : "no"}.`;
|
|
@@ -97,13 +113,36 @@ function startBudget(ms) {
|
|
|
97
113
|
});
|
|
98
114
|
return { promise, expired: () => expired, clear: () => clearTimeout(timer) };
|
|
99
115
|
}
|
|
116
|
+
/**
|
|
117
|
+
* `.wardby/knowledge.yaml` at the pushed commit, or null when it does not
|
|
118
|
+
* exist. Reads within the shared budget; a deadline or a host error throws,
|
|
119
|
+
* which resolveKnowledgeLocation turns into the default bundle with a warning.
|
|
120
|
+
*/
|
|
121
|
+
async function readKnowledgeConfig(host, event, budget) {
|
|
122
|
+
const read = await Promise.race([
|
|
123
|
+
host.readFile(event.repository, KNOWLEDGE_CONFIG_PATH, event.after, {
|
|
124
|
+
startLine: 1,
|
|
125
|
+
maxLines: KNOWLEDGE_CONFIG_MAX_LINES,
|
|
126
|
+
}),
|
|
127
|
+
budget.promise,
|
|
128
|
+
]);
|
|
129
|
+
if (read === "deadline")
|
|
130
|
+
throw new Error("the read hit its deadline");
|
|
131
|
+
if (read.kind === "not_found")
|
|
132
|
+
return null;
|
|
133
|
+
if (read.kind !== "file")
|
|
134
|
+
throw new Error("it is not a file");
|
|
135
|
+
if (read.truncated || Buffer.byteLength(read.text, "utf8") > KNOWLEDGE_CONFIG_MAX_BYTES)
|
|
136
|
+
throw new Error(`it is larger than ${KNOWLEDGE_CONFIG_MAX_BYTES} bytes`);
|
|
137
|
+
return read.text;
|
|
138
|
+
}
|
|
100
139
|
/**
|
|
101
140
|
* The knowledge bundle at the pushed commit, read with bounded concurrency
|
|
102
141
|
* under one overall deadline. A failure never fails the route; it yields an
|
|
103
142
|
* incomplete result so the watcher treats every concept as possibly affected.
|
|
104
143
|
*/
|
|
105
|
-
async function loadBundle(host, event, budget) {
|
|
106
|
-
const prefix = `${
|
|
144
|
+
async function loadBundle(host, event, budget, bundlePath) {
|
|
145
|
+
const prefix = `${bundlePath}/`;
|
|
107
146
|
const concepts = [];
|
|
108
147
|
const where = { repository: event.repository, after: event.after };
|
|
109
148
|
let filesRead = 0;
|
|
@@ -297,6 +336,12 @@ export function mentionTaskText(event, siblings = []) {
|
|
|
297
336
|
if (hints)
|
|
298
337
|
sections.push(hints);
|
|
299
338
|
}
|
|
339
|
+
else if (!event.isPullRequest) {
|
|
340
|
+
// An issue mention: the issue's open wardby pull requests, worded as a Jira issue's are.
|
|
341
|
+
const hints = openPullRequestHints(siblings);
|
|
342
|
+
if (hints)
|
|
343
|
+
sections.push(hints);
|
|
344
|
+
}
|
|
300
345
|
const title = event.subject?.title.replace(/\s+/g, " ").trim().slice(0, MAX_TITLE);
|
|
301
346
|
const description = event.subject?.body.trim() ? event.subject.body.slice(0, MAX_TASK_BODY) : "";
|
|
302
347
|
sections.push([
|
|
@@ -333,11 +378,54 @@ async function priorRunContinues(deps, event) {
|
|
|
333
378
|
const check = await checkContinuation(deps.db, event.priorRunId, event.repository);
|
|
334
379
|
return check.ok && check.root.pullRequestNumber === event.number;
|
|
335
380
|
}
|
|
381
|
+
/** The reply to a mention whose author can see the repository but cannot push to it. */
|
|
382
|
+
export const NO_WRITE_ACCESS_BODY = "😕 I can't act on this: a mention starts work only for people with write access to this repository. " +
|
|
383
|
+
"Ask someone with write access to mention me instead.";
|
|
384
|
+
/** Associations that mean the account holds a role on the repository, not just a view of a public one. */
|
|
385
|
+
const ROLE_ASSOCIATIONS = new Set(["OWNER", "MEMBER", "COLLABORATOR"]);
|
|
386
|
+
/**
|
|
387
|
+
* Whether a refused author should be told why. Triage is granted explicitly, so
|
|
388
|
+
* it always counts. GitHub reports `read` for every account on a public
|
|
389
|
+
* repository, so there `read` counts only with a role association; on a private
|
|
390
|
+
* repository `read` means the account was given access (a private organization
|
|
391
|
+
* member is labelled CONTRIBUTOR or NONE). Unknown facts stay silent.
|
|
392
|
+
*/
|
|
393
|
+
function explainsRefusal(level, event) {
|
|
394
|
+
if (level === "triage")
|
|
395
|
+
return true;
|
|
396
|
+
if (level !== "read")
|
|
397
|
+
return false;
|
|
398
|
+
return event.repositoryPrivate === true || ROLE_ASSOCIATIONS.has(event.authorAssociation ?? "");
|
|
399
|
+
}
|
|
336
400
|
export function unknownPriorRunBody(priorRunId) {
|
|
337
401
|
return (`❌ I can't continue this pull request. Its description names wardby run \`${priorRunId}\`, but this wardby ` +
|
|
338
402
|
"deployment has no record of that run opening this pull request (another wardby deployment may have opened it). " +
|
|
339
403
|
"Ask the deployment that opened it, or make the change on this branch by hand.");
|
|
340
404
|
}
|
|
405
|
+
/**
|
|
406
|
+
* Never names the App's handle: this comment is posted by the App, and a
|
|
407
|
+
* body that mentioned it could be matched as a mention of itself.
|
|
408
|
+
*/
|
|
409
|
+
export function mentionDeclinedBody(reason) {
|
|
410
|
+
switch (reason) {
|
|
411
|
+
case "no_responder":
|
|
412
|
+
return (`ℹ️ I didn't start a run: no agent on this repository is set up to answer mentions. ` +
|
|
413
|
+
'An agent owner can link one with the `mention` trigger (`link_repository`, `triggers: ["mention"]`).');
|
|
414
|
+
case "no_reviewer":
|
|
415
|
+
return (`ℹ️ I didn't start a review: no reviewer agent is linked to this repository. ` +
|
|
416
|
+
"An agent owner can link one with the `pull_request` trigger and a `checkName` (`link_repository`).");
|
|
417
|
+
case "agent_not_authorized":
|
|
418
|
+
return ("ℹ️ I didn't start a run: the agent linked here can no longer use this repository. " +
|
|
419
|
+
"Its owner's GitHub account needs access to it again; they can re-authorize it or re-link the agent.");
|
|
420
|
+
case "pull_request_not_reviewable":
|
|
421
|
+
return "ℹ️ I didn't start a review: this pull request is closed or comes from a fork, and wardby reviews only open pull requests from branches of this repository.";
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
/** A review run's task: the head to review, plus any extra context its trigger adds. */
|
|
425
|
+
export function reviewTaskText(repository, prNumber, headSha, context) {
|
|
426
|
+
const task = `Review pull request #${prNumber} in ${repository} (head ${headSha}).`;
|
|
427
|
+
return context ? composeTaskOverride(`${task}\n\n${context.task}`, context.untrustedContext) : task;
|
|
428
|
+
}
|
|
341
429
|
/** Run states that already cover a commit: a review of it is under way or done. */
|
|
342
430
|
const COVERING_REVIEW_STATUSES = ["pending", "running", "succeeded"];
|
|
343
431
|
/**
|
|
@@ -360,9 +448,16 @@ async function alreadyReviewed(deps, repository, prNumber, headSha, agentId) {
|
|
|
360
448
|
});
|
|
361
449
|
return prior !== null;
|
|
362
450
|
}
|
|
363
|
-
|
|
451
|
+
/** Starts one review run per target on a pull request head; returns the started run ids. */
|
|
452
|
+
export async function startReviews(deps, host, repository, prNumber, headSha, targets,
|
|
364
453
|
/** True for automatic triggers; an explicit re-run or `@wardby review` always runs. */
|
|
365
|
-
skipReviewedCommits = false
|
|
454
|
+
skipReviewedCommits = false,
|
|
455
|
+
/**
|
|
456
|
+
* Extra context for this review: `task` (written by wardby, trusted) is appended to the
|
|
457
|
+
* review task; `untrustedContext` (anything a model or a person wrote) travels separately,
|
|
458
|
+
* delivered as data.
|
|
459
|
+
*/
|
|
460
|
+
context) {
|
|
366
461
|
const runIds = [];
|
|
367
462
|
// One lookup per PR, not per reviewer, and none at all when every reviewer
|
|
368
463
|
// skips this commit: taken at the first actual dispatch.
|
|
@@ -404,7 +499,7 @@ skipReviewedCommits = false) {
|
|
|
404
499
|
selfDefects: { db: deps.db, issueTrackers: deps.issueTrackers },
|
|
405
500
|
agentId: target.agentId,
|
|
406
501
|
trigger: "host_event",
|
|
407
|
-
taskOverride:
|
|
502
|
+
taskOverride: reviewTaskText(repository, prNumber, headSha, context),
|
|
408
503
|
attribution: await linkedAttribution(),
|
|
409
504
|
afterPersist: checkId
|
|
410
505
|
? async (tx, run) => {
|
|
@@ -436,6 +531,59 @@ skipReviewedCommits = false) {
|
|
|
436
531
|
}
|
|
437
532
|
return runIds;
|
|
438
533
|
}
|
|
534
|
+
async function budgetDraftDecision(deps, host, repository, prNumber, headSha,
|
|
535
|
+
/** The pull request's origin, when the caller already reads it once per event. */
|
|
536
|
+
readOrigin) {
|
|
537
|
+
const review = { kind: "review" };
|
|
538
|
+
const read = readOrigin ?? (host.pullRequestOrigin ? () => host.pullRequestOrigin(repository, prNumber) : undefined);
|
|
539
|
+
if (!read)
|
|
540
|
+
return review;
|
|
541
|
+
try {
|
|
542
|
+
const origin = await read();
|
|
543
|
+
if (origin.draft !== true || !origin.markerRunId)
|
|
544
|
+
return review;
|
|
545
|
+
const runId = await pushingRunInFlight(deps.db, { markerRunId: origin.markerRunId, repository });
|
|
546
|
+
if (runId)
|
|
547
|
+
return { kind: "wait", runId: await pushingRunHold(deps.db, runId) };
|
|
548
|
+
const stopped = await headStoppedAtBudget(deps.db, {
|
|
549
|
+
markerRunId: origin.markerRunId,
|
|
550
|
+
repository,
|
|
551
|
+
prNumber,
|
|
552
|
+
headSha,
|
|
553
|
+
});
|
|
554
|
+
return stopped ? { kind: "skip" } : review;
|
|
555
|
+
}
|
|
556
|
+
catch (err) {
|
|
557
|
+
log.warn({ err, repository, prNumber }, "could not read the pull request's coding run; reviewing as usual");
|
|
558
|
+
return review;
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
/**
|
|
562
|
+
* Instead of reviewing a budget-stopped draft (budgetDraftDecision `skip`): completes a neutral
|
|
563
|
+
* check per reviewer. No review run, so no RunHostCheck and no fix round. A reviewer that already
|
|
564
|
+
* reviewed this commit (an explicit request) keeps that review's check: a neutral one over it
|
|
565
|
+
* would stand, unreviewed again, once the draft is marked ready (that automatic review skips a
|
|
566
|
+
* reviewed commit).
|
|
567
|
+
*/
|
|
568
|
+
async function postSkippedReviewChecks(deps, host, repository, prNumber, headSha, targets) {
|
|
569
|
+
log.info({ repository, prNumber, headSha, agentIds: targets.map((t) => t.agentId) }, "review skipped: the draft's last push stopped at its budget");
|
|
570
|
+
for (const target of targets) {
|
|
571
|
+
try {
|
|
572
|
+
if (await alreadyReviewed(deps, repository, prNumber, headSha, target.agentId))
|
|
573
|
+
continue;
|
|
574
|
+
const { checkId } = await host.startCheck(repository, { headSha, name: target.checkName });
|
|
575
|
+
await host.completeCheck(repository, {
|
|
576
|
+
checkId,
|
|
577
|
+
conclusion: "neutral",
|
|
578
|
+
title: "Skipped: stopped at its budget",
|
|
579
|
+
summary: "The last push came from a run that stopped at its budget before finishing. This draft is reviewed when the work continues or when it is marked ready for review.",
|
|
580
|
+
});
|
|
581
|
+
}
|
|
582
|
+
catch (err) {
|
|
583
|
+
log.warn({ err, repository, prNumber, agentId: target.agentId }, "could not post the skipped review check");
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
}
|
|
439
587
|
/**
|
|
440
588
|
* CI finished on a pull request's head: re-runs, once per head, each review
|
|
441
589
|
* that published only a COMMENT while CI there was still running or had not
|
|
@@ -471,6 +619,16 @@ async function rereviewAfterCi(deps, host, event, prNumber, reviewerLinks) {
|
|
|
471
619
|
// Only reviewers still authorized are claimed, so a denied one keeps its chance for later.
|
|
472
620
|
const allowed = await authorizedLinks(deps, event, reviewerLinks.filter((l) => waiting.some((r) => r.run.agentId === l.agentId)));
|
|
473
621
|
const allowedIds = new Set(allowed.map((l) => l.agentId));
|
|
622
|
+
// Decided before the claim, so a re-review not started here is not used up.
|
|
623
|
+
const decision = await budgetDraftDecision(deps, host, event.repository, prNumber, event.headSha);
|
|
624
|
+
if (decision.kind === "wait") {
|
|
625
|
+
log.info({ repository: event.repository, prNumber, headSha: event.headSha, runId: decision.runId }, "CI finished: re-review held while the pull request's coding run is still running");
|
|
626
|
+
return [];
|
|
627
|
+
}
|
|
628
|
+
if (decision.kind === "skip") {
|
|
629
|
+
await postSkippedReviewChecks(deps, host, event.repository, prNumber, event.headSha, reviewTargets(allowed));
|
|
630
|
+
return [];
|
|
631
|
+
}
|
|
474
632
|
// Claimed per row, so concurrent completions re-run each review only once.
|
|
475
633
|
const agentIds = new Set();
|
|
476
634
|
for (const row of waiting.filter((r) => allowedIds.has(r.run.agentId))) {
|
|
@@ -590,6 +748,7 @@ async function deferUntilCi(deps, host, key, links) {
|
|
|
590
748
|
prNumber: key.prNumber,
|
|
591
749
|
headSha: key.headSha,
|
|
592
750
|
agentId: { in: links.map((l) => l.agentId) },
|
|
751
|
+
reason: "ci",
|
|
593
752
|
},
|
|
594
753
|
select: { id: true, agentId: true, checkName: true },
|
|
595
754
|
});
|
|
@@ -602,12 +761,28 @@ async function deferUntilCi(deps, host, key, links) {
|
|
|
602
761
|
}
|
|
603
762
|
return { startNow: [], runIds };
|
|
604
763
|
}
|
|
764
|
+
/**
|
|
765
|
+
* Deferred rows whose pull request's coding run is still in flight (budgetDraftDecision `wait`):
|
|
766
|
+
* they now wait for that run, as for a delegating run (reason "request", leadRunId = the run),
|
|
767
|
+
* and are released when it finishes (startDeferredForRequest from its terminal hook, or the
|
|
768
|
+
* sweep). The run may have finished meanwhile, its release finding no row: released here then.
|
|
769
|
+
*/
|
|
770
|
+
async function waitForPushingRun(deps, key, rows, runId) {
|
|
771
|
+
const where = { repository: key.repository, prNumber: key.prNumber, headSha: key.headSha };
|
|
772
|
+
await deps.db.deferredReview.updateMany({
|
|
773
|
+
where: { id: { in: rows.map((r) => r.id) } },
|
|
774
|
+
data: { reason: "request", leadRunId: runId },
|
|
775
|
+
});
|
|
776
|
+
log.info({ ...where, runId }, "deferred review held while the pull request's coding run is still running");
|
|
777
|
+
const run = await deps.db.run.findUnique({ where: { id: runId }, select: { status: true } });
|
|
778
|
+
return !run || !RUN_NOT_FINISHED.has(run.status) ? releaseRequestRows(deps, runId, "request") : [];
|
|
779
|
+
}
|
|
605
780
|
/**
|
|
606
781
|
* Starts deferred reviews for one PR head. The head must still be that sha,
|
|
607
782
|
* open, and not a fork; otherwise every row is dropped (a newer push records
|
|
608
783
|
* its own). Each row is claimed by deleting it, so a concurrent ci_completed
|
|
609
|
-
* delivery or sweep starts each review only once. On `ci`, only
|
|
610
|
-
* still authorized are claimed (the rest wait for the sweep); on `sweep` (the
|
|
784
|
+
* delivery or sweep starts each review only once. On `ci` and `request`, only
|
|
785
|
+
* reviewers still authorized are claimed (the rest wait for the sweep); on `sweep` (the
|
|
611
786
|
* last attempt) every row is claimed and unauthorized or unlinked reviewers
|
|
612
787
|
* are dropped.
|
|
613
788
|
*/
|
|
@@ -619,11 +794,14 @@ async function startDeferred(deps, host, key, rows, reviewerLinks, mode, reason)
|
|
|
619
794
|
log.info({ ...where, state: head.state }, "deferred review dropped: the pull request moved on or closed");
|
|
620
795
|
return [];
|
|
621
796
|
}
|
|
797
|
+
const decision = await budgetDraftDecision(deps, host, key.repository, key.prNumber, key.headSha);
|
|
798
|
+
if (decision.kind === "wait")
|
|
799
|
+
return waitForPushingRun(deps, key, rows, decision.runId);
|
|
622
800
|
const allowed = await authorizedLinks(deps, key, reviewerLinks.filter((l) => rows.some((r) => r.agentId === l.agentId)));
|
|
623
801
|
const allowedIds = new Set(allowed.map((l) => l.agentId));
|
|
624
802
|
const targets = [];
|
|
625
803
|
for (const row of rows) {
|
|
626
|
-
if (mode
|
|
804
|
+
if (mode !== "sweep" && !allowedIds.has(row.agentId))
|
|
627
805
|
continue;
|
|
628
806
|
const claimed = await deps.db.deferredReview.deleteMany({ where: { id: row.id } });
|
|
629
807
|
if (claimed.count !== 1)
|
|
@@ -637,6 +815,10 @@ async function startDeferred(deps, host, key, rows, reviewerLinks, mode, reason)
|
|
|
637
815
|
return [];
|
|
638
816
|
log.info({ ...where, agentIds: targets.map((t) => t.agentId), reason }, "starting the deferred review");
|
|
639
817
|
try {
|
|
818
|
+
if (decision.kind === "skip") {
|
|
819
|
+
await postSkippedReviewChecks(deps, host, key.repository, key.prNumber, key.headSha, targets);
|
|
820
|
+
return [];
|
|
821
|
+
}
|
|
640
822
|
return await startReviews(deps, host, key.repository, key.prNumber, key.headSha, targets, true);
|
|
641
823
|
}
|
|
642
824
|
catch (err) {
|
|
@@ -647,14 +829,16 @@ async function startDeferred(deps, host, key, rows, reviewerLinks, mode, reason)
|
|
|
647
829
|
}
|
|
648
830
|
/**
|
|
649
831
|
* ci_completed: starts the reviews deferred on this head (see deferUntilCi),
|
|
650
|
-
* unless CI there is still pending (a later completion decides).
|
|
832
|
+
* unless CI there is still pending (a later completion decides). Rows still
|
|
833
|
+
* waiting for their delegating run (reason "request") are not CI's to start:
|
|
834
|
+
* startDeferredForRequest hands them over once that run is terminal. Never
|
|
651
835
|
* throws; a failure is logged and the rows stay for the reconciler sweep.
|
|
652
836
|
*/
|
|
653
837
|
async function startDeferredAfterCi(deps, host, event, prNumber, reviewerLinks) {
|
|
654
838
|
try {
|
|
655
839
|
const key = { provider: event.provider, repository: event.repository, prNumber, headSha: event.headSha };
|
|
656
840
|
const rows = await deps.db.deferredReview.findMany({
|
|
657
|
-
where: key,
|
|
841
|
+
where: { ...key, reason: "ci" },
|
|
658
842
|
select: { id: true, agentId: true, checkName: true },
|
|
659
843
|
});
|
|
660
844
|
if (rows.length === 0)
|
|
@@ -676,9 +860,21 @@ async function startDeferredAfterCi(deps, host, event, prNumber, reviewerLinks)
|
|
|
676
860
|
* DEFERRED_REVIEW_MAX_AGE_MS, and starts (regardless of CI) those waiting
|
|
677
861
|
* longer than DEFERRED_REVIEW_MAX_WAIT_MS, at most DEFERRED_REVIEW_BATCH per
|
|
678
862
|
* pass, oldest first. Covers CI that never reported, ran very long, or whose
|
|
679
|
-
* completion webhook was missed.
|
|
863
|
+
* completion webhook was missed. Rows waiting for a delegating run (reason
|
|
864
|
+
* "request") are released the same way once they are that old, but only when
|
|
865
|
+
* that run is terminal (or gone): a lead the reconciler marked lost never
|
|
866
|
+
* reaches its finalizer. Never throws; returns the runs started.
|
|
680
867
|
*/
|
|
681
868
|
export async function startDeferredReviews(deps, now = new Date()) {
|
|
869
|
+
const providers = Object.keys(deps.hosts);
|
|
870
|
+
const runIds = await sweepCiRows(deps, providers, now);
|
|
871
|
+
// Outside the CI half's try: a failure there never keeps request rows from being released.
|
|
872
|
+
if (providers.length > 0)
|
|
873
|
+
runIds.push(...(await sweepRequestRows(deps, providers, now)));
|
|
874
|
+
return runIds;
|
|
875
|
+
}
|
|
876
|
+
/** The sweep's half for expiry and CI rows: drops rows past the max age, then starts CI rows past the max wait. */
|
|
877
|
+
async function sweepCiRows(deps, providers, now) {
|
|
682
878
|
const runIds = [];
|
|
683
879
|
try {
|
|
684
880
|
const expired = await deps.db.deferredReview.deleteMany({
|
|
@@ -686,12 +882,12 @@ export async function startDeferredReviews(deps, now = new Date()) {
|
|
|
686
882
|
});
|
|
687
883
|
if (expired.count > 0)
|
|
688
884
|
log.info({ count: expired.count }, "dropped deferred reviews older than 24 h");
|
|
689
|
-
const providers = Object.keys(deps.hosts);
|
|
690
885
|
if (providers.length === 0)
|
|
691
886
|
return runIds;
|
|
692
887
|
const due = await deps.db.deferredReview.findMany({
|
|
693
888
|
where: {
|
|
694
889
|
provider: { in: providers },
|
|
890
|
+
reason: "ci",
|
|
695
891
|
createdAt: { lte: new Date(now.getTime() - DEFERRED_REVIEW_MAX_WAIT_MS) },
|
|
696
892
|
},
|
|
697
893
|
select: {
|
|
@@ -732,6 +928,246 @@ export async function startDeferredReviews(deps, now = new Date()) {
|
|
|
732
928
|
}
|
|
733
929
|
return runIds;
|
|
734
930
|
}
|
|
931
|
+
/** The sweep's half for request rows: releases those older than the max wait whose delegating run is terminal or gone. */
|
|
932
|
+
async function sweepRequestRows(deps, providers, now) {
|
|
933
|
+
const runIds = [];
|
|
934
|
+
try {
|
|
935
|
+
// Joined in the query, so leads still running never fill the batch ahead of finished ones.
|
|
936
|
+
const leads = await deps.db.$queryRaw `
|
|
937
|
+
SELECT DISTINCT d."leadRunId"
|
|
938
|
+
FROM "DeferredReview" d
|
|
939
|
+
LEFT JOIN "Run" r ON r."id" = d."leadRunId"
|
|
940
|
+
WHERE d."reason" = 'request'
|
|
941
|
+
AND d."leadRunId" IS NOT NULL
|
|
942
|
+
AND d."provider" IN (${Prisma.join(providers)})
|
|
943
|
+
AND d."createdAt" <= ${new Date(now.getTime() - DEFERRED_REVIEW_MAX_WAIT_MS)}
|
|
944
|
+
AND (r."id" IS NULL OR r."status"::text NOT IN ('pending', 'running'))
|
|
945
|
+
LIMIT ${DEFERRED_REVIEW_BATCH}`;
|
|
946
|
+
for (const { leadRunId } of leads)
|
|
947
|
+
runIds.push(...(await releaseRequestRows(deps, leadRunId, "sweep")));
|
|
948
|
+
}
|
|
949
|
+
catch (err) {
|
|
950
|
+
log.warn({ err }, "deferred review sweep failed for reviews waiting on a delegating run");
|
|
951
|
+
}
|
|
952
|
+
return runIds;
|
|
953
|
+
}
|
|
954
|
+
/**
|
|
955
|
+
* The run whose finish a review of this pull request waits for: the root of
|
|
956
|
+
* the run tree when the PR's opening coding run was delegated (it has a
|
|
957
|
+
* parent run) and that root is not terminal yet; otherwise null (a
|
|
958
|
+
* non-delegated coding PR, a finished request, or an unknown marker). A lead's
|
|
959
|
+
* delegate_to_* calls block until each child is terminal, so a terminal root
|
|
960
|
+
* means every sibling coding run of the request has finished and opened its
|
|
961
|
+
* pull request. A continuation's marker resolves to its root coding run, then
|
|
962
|
+
* up that run's parents. `repository`, when given, must be the coding run's.
|
|
963
|
+
*/
|
|
964
|
+
export async function requestLeadRunId(db, prMarkerRunId, repository) {
|
|
965
|
+
const root = await runTreeRoot(db, prMarkerRunId, repository);
|
|
966
|
+
if (!root || root.isOpener || !RUN_NOT_FINISHED.has(root.status))
|
|
967
|
+
return null;
|
|
968
|
+
return root.id;
|
|
969
|
+
}
|
|
970
|
+
/** Run statuses that mean the delegating run has not finished. */
|
|
971
|
+
const RUN_NOT_FINISHED = new Set(["pending", "running"]);
|
|
972
|
+
/**
|
|
973
|
+
* The run a draft's review waits for while its coding run `pushingRunId` may still be pushing:
|
|
974
|
+
* that run's top-level lead while the lead is pending or running, else the coding run itself.
|
|
975
|
+
* A follow-up's lead is not the one requestLeadRunId finds (that walks up from the pull
|
|
976
|
+
* request's opening run), yet the review should still wait for the whole request (#259); and
|
|
977
|
+
* the lead's finalizer releases it outside every workflow step, where the coding run's own
|
|
978
|
+
* terminal hook, inside the lead's delegation step, could not start a review run.
|
|
979
|
+
*/
|
|
980
|
+
async function pushingRunHold(db, pushingRunId) {
|
|
981
|
+
// UNION, not UNION ALL: a parentRunId cycle (never written, but not a constraint) can't recurse forever.
|
|
982
|
+
const rows = await db.$queryRaw `
|
|
983
|
+
WITH RECURSIVE up AS (
|
|
984
|
+
SELECT r."id", r."parentRunId", r."status"::text AS "status" FROM "Run" r WHERE r."id" = ${pushingRunId}
|
|
985
|
+
UNION
|
|
986
|
+
SELECT p."id", p."parentRunId", p."status"::text FROM "Run" p JOIN up u ON p."id" = u."parentRunId"
|
|
987
|
+
)
|
|
988
|
+
SELECT u."id", u."status" FROM up u WHERE u."parentRunId" IS NULL LIMIT 1`;
|
|
989
|
+
const root = rows[0];
|
|
990
|
+
return root && root.id !== pushingRunId && RUN_NOT_FINISHED.has(root.status) ? root.id : pushingRunId;
|
|
991
|
+
}
|
|
992
|
+
/**
|
|
993
|
+
* pr_updated for a pull request a delegated coding run opened, while the
|
|
994
|
+
* request's lead run is still running: records one DeferredReview row per
|
|
995
|
+
* reviewer (reason "request", with or without waitForCi) and starts nothing;
|
|
996
|
+
* the lead's finalizer (startDeferredForRequest) or the sweep starts them.
|
|
997
|
+
* Returns null, to review as without it, for any other pull request, or when
|
|
998
|
+
* the lead cannot be resolved or the deferral cannot be recorded. A draft whose
|
|
999
|
+
* own coding run (root or continuation) is still in flight waits for that run
|
|
1000
|
+
* the same way: it may have pushed this head before recording whether it
|
|
1001
|
+
* stopped at its budget (budgetDraftDecision); its terminal hook releases them.
|
|
1002
|
+
*/
|
|
1003
|
+
async function deferUntilRequest(deps, host, key, links,
|
|
1004
|
+
/** The pull request's origin, read at most once per event (shared with the merge order check). */
|
|
1005
|
+
readOrigin) {
|
|
1006
|
+
if (links.length === 0 || !readOrigin)
|
|
1007
|
+
return null;
|
|
1008
|
+
let leadRunId;
|
|
1009
|
+
try {
|
|
1010
|
+
const origin = await readOrigin();
|
|
1011
|
+
if (!origin.markerRunId)
|
|
1012
|
+
return null;
|
|
1013
|
+
leadRunId = await requestLeadRunId(deps.db, origin.markerRunId, key.repository);
|
|
1014
|
+
// A draft's own coding run still in flight may have pushed this head before recording its
|
|
1015
|
+
// result (a stop at its budget among them): wait for it the same way (budgetDraftDecision).
|
|
1016
|
+
if (!leadRunId && origin.draft === true) {
|
|
1017
|
+
const pushing = await pushingRunInFlight(deps.db, {
|
|
1018
|
+
markerRunId: origin.markerRunId,
|
|
1019
|
+
repository: key.repository,
|
|
1020
|
+
});
|
|
1021
|
+
leadRunId = pushing && (await pushingRunHold(deps.db, pushing));
|
|
1022
|
+
}
|
|
1023
|
+
}
|
|
1024
|
+
catch (err) {
|
|
1025
|
+
log.warn({ err, repository: key.repository, prNumber: key.prNumber }, "could not read the pull request's delegating run; reviewing as usual");
|
|
1026
|
+
return null;
|
|
1027
|
+
}
|
|
1028
|
+
if (!leadRunId)
|
|
1029
|
+
return null;
|
|
1030
|
+
const head = { provider: key.provider, repository: key.repository, prNumber: key.prNumber, headSha: key.headSha };
|
|
1031
|
+
try {
|
|
1032
|
+
// Idempotent per head and reviewer, like deferUntilCi.
|
|
1033
|
+
await deps.db.deferredReview.createMany({
|
|
1034
|
+
data: links.map((l) => ({ ...head, agentId: l.agentId, checkName: l.checkName, reason: "request", leadRunId })),
|
|
1035
|
+
skipDuplicates: true,
|
|
1036
|
+
});
|
|
1037
|
+
// A row this head already had for CI waits for the request too: released only when both are done.
|
|
1038
|
+
await deps.db.deferredReview.updateMany({
|
|
1039
|
+
where: { ...head, agentId: { in: links.map((l) => l.agentId) }, reason: "ci" },
|
|
1040
|
+
data: { reason: "request", leadRunId },
|
|
1041
|
+
});
|
|
1042
|
+
}
|
|
1043
|
+
catch (err) {
|
|
1044
|
+
log.warn({ err, repository: key.repository, prNumber: key.prNumber }, "could not record the deferred review; reviewing as usual");
|
|
1045
|
+
return null;
|
|
1046
|
+
}
|
|
1047
|
+
log.info({ ...head, leadRunId, agentIds: links.map((l) => l.agentId) }, "review deferred until the delegating run finishes");
|
|
1048
|
+
// The lead may have finished between the lookup and the insert, its
|
|
1049
|
+
// finalizer finding no row yet: check once more (rows are claimed by
|
|
1050
|
+
// delete, so a racing finalizer starts each review once). A failure leaves
|
|
1051
|
+
// the rows for the sweep.
|
|
1052
|
+
const runIds = [];
|
|
1053
|
+
try {
|
|
1054
|
+
const lead = await deps.db.run.findUnique({ where: { id: leadRunId }, select: { status: true } });
|
|
1055
|
+
if (!lead || !RUN_NOT_FINISHED.has(lead.status)) {
|
|
1056
|
+
runIds.push(...(await releaseRequestRows(deps, leadRunId, "request")));
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
catch (err) {
|
|
1060
|
+
log.warn({ err, repository: key.repository, prNumber: key.prNumber, leadRunId }, "could not re-check the delegating run after deferring; the sweep starts the review");
|
|
1061
|
+
}
|
|
1062
|
+
return { runIds };
|
|
1063
|
+
}
|
|
1064
|
+
/**
|
|
1065
|
+
* The delegating run `leadRunId` is terminal (it succeeded, failed, or was
|
|
1066
|
+
* cancelled): releases every review that waited for it. A reviewer linked
|
|
1067
|
+
* with waitForCi whose head's CI is still pending is handed to CI (the row
|
|
1068
|
+
* becomes reason "ci", its max wait counted from now); every other row is
|
|
1069
|
+
* started (claimed by delete, so concurrent releases start each review once).
|
|
1070
|
+
* Called from the lead's finalizer, after the related pull requests sections
|
|
1071
|
+
* were rewritten. Never throws.
|
|
1072
|
+
*/
|
|
1073
|
+
export async function startDeferredForRequest(deps, leadRunId) {
|
|
1074
|
+
try {
|
|
1075
|
+
await releaseRequestRows(deps, leadRunId, "request");
|
|
1076
|
+
}
|
|
1077
|
+
catch (err) {
|
|
1078
|
+
log.warn({ err, leadRunId }, "could not start the reviews waiting for the delegating run");
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
async function releaseRequestRows(deps, leadRunId, mode) {
|
|
1082
|
+
const rows = await deps.db.deferredReview.findMany({
|
|
1083
|
+
where: { leadRunId, reason: "request" },
|
|
1084
|
+
select: {
|
|
1085
|
+
id: true,
|
|
1086
|
+
provider: true,
|
|
1087
|
+
repository: true,
|
|
1088
|
+
prNumber: true,
|
|
1089
|
+
headSha: true,
|
|
1090
|
+
agentId: true,
|
|
1091
|
+
checkName: true,
|
|
1092
|
+
},
|
|
1093
|
+
orderBy: { createdAt: "asc" },
|
|
1094
|
+
});
|
|
1095
|
+
const groups = new Map();
|
|
1096
|
+
for (const row of rows) {
|
|
1097
|
+
const id = JSON.stringify([row.provider, row.repository, row.prNumber, row.headSha]);
|
|
1098
|
+
groups.set(id, [...(groups.get(id) ?? []), row]);
|
|
1099
|
+
}
|
|
1100
|
+
const runIds = [];
|
|
1101
|
+
for (const group of groups.values()) {
|
|
1102
|
+
const first = group[0];
|
|
1103
|
+
const provider = first.provider;
|
|
1104
|
+
const key = { provider, repository: first.repository, prNumber: first.prNumber, headSha: first.headSha };
|
|
1105
|
+
const host = deps.hosts[provider];
|
|
1106
|
+
if (!host)
|
|
1107
|
+
continue;
|
|
1108
|
+
try {
|
|
1109
|
+
const reviewerLinks = (await loadLinks(deps, provider, key.repository)).filter(isReviewerLink);
|
|
1110
|
+
runIds.push(...(await releaseRequestHead(deps, host, key, group, reviewerLinks, mode)));
|
|
1111
|
+
}
|
|
1112
|
+
catch (err) {
|
|
1113
|
+
log.warn({ err, repository: key.repository, prNumber: key.prNumber, leadRunId }, "could not start a review waiting for the delegating run");
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1116
|
+
return runIds;
|
|
1117
|
+
}
|
|
1118
|
+
/** One head's request rows: hands waitForCi rows with CI still pending to CI, starts the rest. */
|
|
1119
|
+
async function releaseRequestHead(deps, host, key, rows, reviewerLinks, mode) {
|
|
1120
|
+
const waitsForCi = new Set(reviewerLinks.filter((l) => l.waitForCi).map((l) => l.agentId));
|
|
1121
|
+
let ciRows = rows.filter((r) => waitsForCi.has(r.agentId));
|
|
1122
|
+
if (ciRows.length > 0 && host.readCi) {
|
|
1123
|
+
let state;
|
|
1124
|
+
try {
|
|
1125
|
+
state = (await host.readCi(key.repository, key.headSha)).state;
|
|
1126
|
+
}
|
|
1127
|
+
catch (err) {
|
|
1128
|
+
log.warn({ err, repository: key.repository, prNumber: key.prNumber }, "could not read CI; reviewing now");
|
|
1129
|
+
}
|
|
1130
|
+
if (state === undefined || !CI_NOT_FINISHED_ON_PUSH.has(state))
|
|
1131
|
+
ciRows = [];
|
|
1132
|
+
}
|
|
1133
|
+
else
|
|
1134
|
+
ciRows = [];
|
|
1135
|
+
const startNow = rows.filter((r) => !ciRows.includes(r));
|
|
1136
|
+
const runIds = startNow.length > 0
|
|
1137
|
+
? await startDeferred(deps, host, key, startNow, reviewerLinks, mode, "the delegating run finished")
|
|
1138
|
+
: [];
|
|
1139
|
+
if (ciRows.length === 0 || !host.readCi)
|
|
1140
|
+
return runIds;
|
|
1141
|
+
const ids = ciRows.map((r) => r.id);
|
|
1142
|
+
// Only a row still waiting for the request flips: a concurrent release that claimed it already started it.
|
|
1143
|
+
await deps.db.deferredReview.updateMany({
|
|
1144
|
+
where: { id: { in: ids }, reason: "request" },
|
|
1145
|
+
data: { reason: "ci", createdAt: new Date() },
|
|
1146
|
+
});
|
|
1147
|
+
log.info({
|
|
1148
|
+
repository: key.repository,
|
|
1149
|
+
prNumber: key.prNumber,
|
|
1150
|
+
headSha: key.headSha,
|
|
1151
|
+
agentIds: ciRows.map((r) => r.agentId),
|
|
1152
|
+
}, "the delegating run finished; review deferred until CI finishes");
|
|
1153
|
+
// As in deferUntilCi: CI may have finished before the flip, its ci_completed skipping the request row.
|
|
1154
|
+
try {
|
|
1155
|
+
const again = (await host.readCi(key.repository, key.headSha)).state;
|
|
1156
|
+
if (!CI_NOT_FINISHED_ON_PUSH.has(again)) {
|
|
1157
|
+
const handed = await deps.db.deferredReview.findMany({
|
|
1158
|
+
where: { id: { in: ids }, reason: "ci" },
|
|
1159
|
+
select: { id: true, agentId: true, checkName: true },
|
|
1160
|
+
});
|
|
1161
|
+
if (handed.length > 0) {
|
|
1162
|
+
runIds.push(...(await startDeferred(deps, host, key, handed, reviewerLinks, "ci", "CI finished")));
|
|
1163
|
+
}
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1166
|
+
catch (err) {
|
|
1167
|
+
log.warn({ err, repository: key.repository, prNumber: key.prNumber }, "could not re-read CI after the delegating run finished; the sweep starts the review");
|
|
1168
|
+
}
|
|
1169
|
+
return runIds;
|
|
1170
|
+
}
|
|
735
1171
|
export async function routeHostEvent(event, deps) {
|
|
736
1172
|
const none = { runIds: [], followUps: [] };
|
|
737
1173
|
if (event.kind === "pr_closed") {
|
|
@@ -742,7 +1178,18 @@ export async function routeHostEvent(event, deps) {
|
|
|
742
1178
|
number: event.prNumber,
|
|
743
1179
|
merged: event.merged,
|
|
744
1180
|
});
|
|
745
|
-
|
|
1181
|
+
// The merge order checks of the sets this PR is a step of, after the response. Never throws.
|
|
1182
|
+
if (!deps.hosts[event.provider]?.upsertNamedCheck)
|
|
1183
|
+
return none;
|
|
1184
|
+
const pr = { repository: event.repository, number: event.prNumber };
|
|
1185
|
+
return {
|
|
1186
|
+
runIds: [],
|
|
1187
|
+
followUps: [
|
|
1188
|
+
() => syncMergeOrderChecksForPullRequest(deps, pr, {
|
|
1189
|
+
closed: { ...pr, merged: event.merged },
|
|
1190
|
+
}),
|
|
1191
|
+
],
|
|
1192
|
+
};
|
|
746
1193
|
}
|
|
747
1194
|
const host = deps.hosts[event.provider];
|
|
748
1195
|
if (!host)
|
|
@@ -767,11 +1214,14 @@ export async function routeHostEvent(event, deps) {
|
|
|
767
1214
|
const budget = startBudget(BUNDLE_LOAD_DEADLINE_MS);
|
|
768
1215
|
let taskOverride;
|
|
769
1216
|
try {
|
|
770
|
-
const {
|
|
1217
|
+
const { bundlePath, warning } = await resolveKnowledgeLocation(() => readKnowledgeConfig(host, event, budget));
|
|
1218
|
+
if (warning)
|
|
1219
|
+
log.warn({ repository: event.repository, after: event.after }, warning);
|
|
1220
|
+
const { concepts, complete } = await loadBundle(host, event, budget, bundlePath);
|
|
771
1221
|
const scope = driftScope({
|
|
772
1222
|
changedPaths: event.changedPaths,
|
|
773
1223
|
changedPathsComplete: event.changedPathsComplete,
|
|
774
|
-
bundlePath
|
|
1224
|
+
bundlePath,
|
|
775
1225
|
concepts,
|
|
776
1226
|
});
|
|
777
1227
|
// Only the concepts the merge affects are checked, and only against a bundle that was fully read.
|
|
@@ -779,7 +1229,7 @@ export async function routeHostEvent(event, deps) {
|
|
|
779
1229
|
const citations = complete
|
|
780
1230
|
? await verifyCitations(host, event, concepts.filter((c) => affected.has(c.path)), budget)
|
|
781
1231
|
: new Map();
|
|
782
|
-
taskOverride = mergeTaskText(event, scope, complete, citations);
|
|
1232
|
+
taskOverride = mergeTaskText(event, scope, complete, citations, bundlePath);
|
|
783
1233
|
}
|
|
784
1234
|
finally {
|
|
785
1235
|
budget.clear();
|
|
@@ -816,30 +1266,62 @@ export async function routeHostEvent(event, deps) {
|
|
|
816
1266
|
return { runIds, followUps: [] };
|
|
817
1267
|
}
|
|
818
1268
|
case "pr_updated": {
|
|
819
|
-
if (event.isFork
|
|
1269
|
+
if (event.isFork)
|
|
820
1270
|
return none;
|
|
1271
|
+
// Read at most once per event, by the request deferral and the merge order check alike.
|
|
1272
|
+
let origin;
|
|
1273
|
+
const readOrigin = host.pullRequestOrigin
|
|
1274
|
+
? () => (origin ??= host.pullRequestOrigin(event.repository, event.prNumber))
|
|
1275
|
+
: undefined;
|
|
1276
|
+
// A new head needs the merge order check re-posted (check runs are per commit), on every
|
|
1277
|
+
// path below, a review held for the delegating run included. After the response; never throws.
|
|
1278
|
+
const followUps = host.upsertNamedCheck && readOrigin
|
|
1279
|
+
? [
|
|
1280
|
+
() => syncMergeOrderChecksForPullRequest(deps, { repository: event.repository, number: event.prNumber }, { origin: { repository: event.repository, number: event.prNumber, read: readOrigin } }),
|
|
1281
|
+
]
|
|
1282
|
+
: [];
|
|
1283
|
+
if (reviewerLinks.length === 0)
|
|
1284
|
+
return { runIds: [], followUps };
|
|
821
1285
|
const allowed = await authorizedLinks(deps, event, reviewerLinks);
|
|
822
1286
|
if (allowed.length === 0)
|
|
823
|
-
return
|
|
1287
|
+
return { runIds: [], followUps };
|
|
1288
|
+
// A PR a delegated coding run opened is reviewed once the whole request
|
|
1289
|
+
// finished, so its sibling PRs exist and the related section is current (#259).
|
|
1290
|
+
const request = await deferUntilRequest(deps, host, event, allowed, readOrigin);
|
|
1291
|
+
if (request)
|
|
1292
|
+
return { runIds: request.runIds, followUps };
|
|
824
1293
|
// Reviewers linked with waitForCi review this head only once its CI finished.
|
|
825
1294
|
const deferral = await deferUntilCi(deps, host, event, allowed.filter((l) => l.waitForCi));
|
|
826
1295
|
const startNow = new Set(deferral.startNow);
|
|
827
1296
|
const reviewers = reviewTargets(allowed.filter((l) => !l.waitForCi || startNow.has(l)));
|
|
828
1297
|
const runIds = [...deferral.runIds];
|
|
829
1298
|
if (reviewers.length > 0) {
|
|
830
|
-
|
|
1299
|
+
// A coding run still in flight was already waited for (deferUntilRequest); should
|
|
1300
|
+
// recording that have failed, the head is reviewed as usual.
|
|
1301
|
+
const decision = await budgetDraftDecision(deps, host, event.repository, event.prNumber, event.headSha, readOrigin);
|
|
1302
|
+
if (decision.kind === "skip") {
|
|
1303
|
+
await postSkippedReviewChecks(deps, host, event.repository, event.prNumber, event.headSha, reviewers);
|
|
1304
|
+
}
|
|
1305
|
+
else {
|
|
1306
|
+
runIds.push(...(await startReviews(deps, host, event.repository, event.prNumber, event.headSha, reviewers, true)));
|
|
1307
|
+
}
|
|
831
1308
|
}
|
|
832
|
-
return { runIds, followUps
|
|
1309
|
+
return { runIds, followUps };
|
|
833
1310
|
}
|
|
834
1311
|
case "ci_completed": {
|
|
835
1312
|
if (reviewerLinks.length === 0)
|
|
836
1313
|
return none;
|
|
837
1314
|
const runIds = [];
|
|
1315
|
+
// CI going green may be the last thing a pull request waited for (#287); evaluated
|
|
1316
|
+
// after the response (a host read per PR could otherwise exceed the webhook timeout).
|
|
1317
|
+
const followUps = event.prNumbers.map((prNumber) => async () => {
|
|
1318
|
+
await maybeMarkReady({ db: deps.db, hosts: deps.hosts, log }, event.repository, prNumber, event.provider);
|
|
1319
|
+
});
|
|
838
1320
|
for (const prNumber of event.prNumbers) {
|
|
839
1321
|
runIds.push(...(await rereviewAfterCi(deps, host, event, prNumber, reviewerLinks)));
|
|
840
1322
|
runIds.push(...(await startDeferredAfterCi(deps, host, event, prNumber, reviewerLinks)));
|
|
841
1323
|
}
|
|
842
|
-
return { runIds, followUps
|
|
1324
|
+
return { runIds, followUps };
|
|
843
1325
|
}
|
|
844
1326
|
case "check_rerun": {
|
|
845
1327
|
const owners = reviewerLinks.filter((l) => l.checkName === event.checkName);
|
|
@@ -862,12 +1344,15 @@ export async function routeHostEvent(event, deps) {
|
|
|
862
1344
|
const reviewCommand = event.isPullRequest && isReviewCommand(event.body, deps.mentionHandle);
|
|
863
1345
|
const responderLink = links.find((l) => l.access === "write" && l.triggers.includes("mention"));
|
|
864
1346
|
const candidates = reviewCommand ? reviewerLinks : responderLink ? [responderLink] : [];
|
|
865
|
-
|
|
1347
|
+
// Explanations off and nothing to start: say nothing, and skip the author lookup.
|
|
1348
|
+
if (candidates.length === 0 && deps.mentionExplanations === false)
|
|
866
1349
|
return none;
|
|
867
1350
|
// H5-3: a mention drives a write-capable agent holding its owner's
|
|
868
1351
|
// tools and secrets, so its author needs real push access to the
|
|
869
1352
|
// repository (author_association, checked upstream, is only a
|
|
870
|
-
// pre-filter). Checked by the author's immutable id.
|
|
1353
|
+
// pre-filter). Checked by the author's immutable id. Checked before
|
|
1354
|
+
// anything is said back: someone without write access gets no reply at
|
|
1355
|
+
// all, so a mention can never be used to probe what is linked here.
|
|
871
1356
|
const author = await deps.repoAccess.authorizeHostUser({
|
|
872
1357
|
provider: event.provider,
|
|
873
1358
|
repository: event.repository,
|
|
@@ -876,15 +1361,54 @@ export async function routeHostEvent(event, deps) {
|
|
|
876
1361
|
});
|
|
877
1362
|
if (!author.ok) {
|
|
878
1363
|
log.info({ repository: event.repository, number: event.number, reason: author.reason, level: author.level }, "mention ignored: its author lacks write access");
|
|
879
|
-
|
|
1364
|
+
// Someone who can read the repository gets told why; a stranger on a public repository (GitHub reports `read` for everyone) learns nothing.
|
|
1365
|
+
if (author.reason !== "insufficient_permission" || !explainsRefusal(author.level, event)) {
|
|
1366
|
+
return none;
|
|
1367
|
+
}
|
|
1368
|
+
const refuse = async () => {
|
|
1369
|
+
await host.acknowledge(event.repository, event.comment, "refused").catch((err) => {
|
|
1370
|
+
log.warn({ err, repository: event.repository, number: event.number }, "could not add the reaction");
|
|
1371
|
+
});
|
|
1372
|
+
await host
|
|
1373
|
+
.comment(event.repository, {
|
|
1374
|
+
number: event.number,
|
|
1375
|
+
body: NO_WRITE_ACCESS_BODY,
|
|
1376
|
+
...(event.replyToReviewCommentId ? { replyToReviewCommentId: event.replyToReviewCommentId } : {}),
|
|
1377
|
+
})
|
|
1378
|
+
.catch((err) => {
|
|
1379
|
+
log.warn({ err, repository: event.repository, number: event.number }, "could not post the refusal");
|
|
1380
|
+
});
|
|
1381
|
+
};
|
|
1382
|
+
return { runIds: [], followUps: [refuse] };
|
|
880
1383
|
}
|
|
1384
|
+
// The author may use wardby here, so tell them why nothing started
|
|
1385
|
+
// instead of leaving the mention unanswered.
|
|
1386
|
+
const decline = (reason) => {
|
|
1387
|
+
log.info({ repository: event.repository, number: event.number, reason }, "mention declined");
|
|
1388
|
+
if (deps.mentionExplanations === false)
|
|
1389
|
+
return none;
|
|
1390
|
+
const reply = async () => {
|
|
1391
|
+
await host
|
|
1392
|
+
.comment(event.repository, {
|
|
1393
|
+
number: event.number,
|
|
1394
|
+
body: mentionDeclinedBody(reason),
|
|
1395
|
+
...(event.replyToReviewCommentId ? { replyToReviewCommentId: event.replyToReviewCommentId } : {}),
|
|
1396
|
+
})
|
|
1397
|
+
.catch((err) => {
|
|
1398
|
+
log.warn({ err, repository: event.repository, number: event.number }, "could not post the explanation");
|
|
1399
|
+
});
|
|
1400
|
+
};
|
|
1401
|
+
return { runIds: [], followUps: [reply] };
|
|
1402
|
+
};
|
|
1403
|
+
if (candidates.length === 0)
|
|
1404
|
+
return decline(reviewCommand ? "no_reviewer" : "no_responder");
|
|
881
1405
|
const allowed = await authorizedLinks(deps, event, candidates);
|
|
882
1406
|
if (allowed.length === 0)
|
|
883
|
-
return
|
|
1407
|
+
return decline("agent_not_authorized");
|
|
884
1408
|
if (reviewCommand) {
|
|
885
1409
|
const head = await host.pullRequestHead(event.repository, event.number);
|
|
886
1410
|
if (head.isFork || head.state !== "open")
|
|
887
|
-
return
|
|
1411
|
+
return decline("pull_request_not_reviewable");
|
|
888
1412
|
return {
|
|
889
1413
|
runIds: await startReviews(deps, host, event.repository, event.number, head.headSha, reviewTargets(allowed)),
|
|
890
1414
|
followUps: [react],
|
|
@@ -909,9 +1433,15 @@ export async function routeHostEvent(event, deps) {
|
|
|
909
1433
|
return { runIds: [], followUps: [react, reply] };
|
|
910
1434
|
}
|
|
911
1435
|
// Seeded from the PR's validated marker run: the request it belongs to. Stored state only.
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
1436
|
+
// An issue mention is seeded from the issue's recorded pull requests instead.
|
|
1437
|
+
const siblings = event.isPullRequest
|
|
1438
|
+
? event.priorRunId
|
|
1439
|
+
? await openSiblings(deps.db, event.priorRunId, { repository: event.repository, number: event.number })
|
|
1440
|
+
: []
|
|
1441
|
+
: await openSiblingsForIssue(deps.db, {
|
|
1442
|
+
provider: "github",
|
|
1443
|
+
key: githubIssueKey(event.repository, event.number),
|
|
1444
|
+
});
|
|
915
1445
|
const dispatched = await dispatchRun({
|
|
916
1446
|
db: deps.db,
|
|
917
1447
|
executor: deps.executor,
|
|
@@ -921,7 +1451,11 @@ export async function routeHostEvent(event, deps) {
|
|
|
921
1451
|
taskOverride: mentionTaskText(event, siblings),
|
|
922
1452
|
attribution: event.isPullRequest
|
|
923
1453
|
? await linkedPullRequestAttribution(deps.db, deps.issueTrackers, { codeProvider: host.provider, repository: event.repository, number: event.number }, RESPONSE_PATH_SNAPSHOT_BUDGET)
|
|
924
|
-
:
|
|
1454
|
+
: // A mention on an issue is that issue's own run tree, like a Jira issue event's.
|
|
1455
|
+
{
|
|
1456
|
+
source: "issue_event",
|
|
1457
|
+
item: await resolveWorkItem(deps.db, deps.issueTrackers, "github", githubIssueKey(event.repository, event.number), RESPONSE_PATH_SNAPSHOT_BUDGET),
|
|
1458
|
+
},
|
|
925
1459
|
// Where to report, written with the run: even if this instance dies
|
|
926
1460
|
// before the follow-up below, the run's outcome still gets a comment.
|
|
927
1461
|
afterPersist: async (tx, run) => {
|