@wardby/cli 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/deploy/local/docker-compose.quickstart-coding.yml +13 -0
- package/dist/claude-coding-worker/driver.d.ts +21 -3
- package/dist/claude-coding-worker/driver.js +125 -17
- package/dist/claude-coding-worker/main.js +34 -6
- package/dist/claude-coding-worker/sdk.js +12 -1
- package/dist/claude-coding-worker/tool-socket.d.ts +27 -0
- package/dist/claude-coding-worker/tool-socket.js +111 -0
- package/dist/claude-coding-worker/transcript.d.ts +18 -0
- package/dist/claude-coding-worker/transcript.js +77 -0
- package/dist/cli-help.d.ts +1 -1
- package/dist/cli-help.js +1 -0
- package/dist/cli.js +24 -10
- package/dist/coding/continuation-wording.d.ts +35 -0
- package/dist/coding/continuation-wording.js +73 -0
- package/dist/coding/linked-issue-note.d.ts +23 -0
- package/dist/coding/linked-issue-note.js +47 -0
- package/dist/coding/merge-wording.d.ts +28 -0
- package/dist/coding/merge-wording.js +78 -0
- package/dist/coding/pr-description-note.d.ts +9 -0
- package/dist/coding/pr-description-note.js +32 -0
- package/dist/coding/profile.d.ts +46 -126
- package/dist/coding/profile.js +45 -4
- package/dist/coding/protected-path-wording.d.ts +8 -0
- package/dist/coding/protected-path-wording.js +1 -1
- package/dist/coding/protocol.d.ts +264 -346
- package/dist/coding/protocol.js +235 -12
- package/dist/coding/redact.d.ts +3 -0
- package/dist/coding/redact.js +12 -0
- package/dist/coding/registry/allowlist.d.ts +12 -0
- package/dist/coding/registry/allowlist.js +14 -1
- package/dist/coding/registry/audit-mode.d.ts +153 -0
- package/dist/coding/registry/audit-mode.js +304 -0
- package/dist/coding/registry/lockfile-shared.d.ts +36 -0
- package/dist/coding/registry/lockfile-shared.js +128 -0
- package/dist/coding/registry/lockfiles.js +1 -1
- package/dist/coding/registry/npm-lockfile-formats.d.ts +11 -0
- package/dist/coding/registry/npm-lockfile-formats.js +41 -0
- package/dist/coding/registry/npm-lockfile.d.ts +8 -4
- package/dist/coding/registry/npm-lockfile.js +4 -6
- package/dist/coding/registry/npm.js +78 -8
- package/dist/coding/registry/pnpm-lockfile.d.ts +17 -0
- package/dist/coding/registry/pnpm-lockfile.js +189 -0
- package/dist/coding/registry/pypi-plan.d.ts +50 -0
- package/dist/coding/registry/pypi-plan.js +406 -0
- package/dist/coding/registry/pypi-requirements.d.ts +58 -0
- package/dist/coding/registry/pypi-requirements.js +155 -0
- package/dist/coding/registry/pypi.d.ts +3 -27
- package/dist/coding/registry/pypi.js +40 -154
- package/dist/coding/registry/refusal-hints.d.ts +20 -0
- package/dist/coding/registry/refusal-hints.js +61 -0
- package/dist/coding/registry/refusal-wording.d.ts +11 -0
- package/dist/coding/registry/refusal-wording.js +56 -0
- package/dist/coding/registry/report.d.ts +70 -8
- package/dist/coding/registry/report.js +112 -5
- package/dist/coding/registry/types.d.ts +58 -8
- package/dist/coding/registry/yarn-lockfile.d.ts +30 -0
- package/dist/coding/registry/yarn-lockfile.js +367 -0
- package/dist/coding/services/catalog.d.ts +37 -351
- package/dist/coding/transcript/claude-code.d.ts +13 -0
- package/dist/coding/transcript/claude-code.js +273 -0
- package/dist/coding/transcript/download-protocol.d.ts +30 -0
- package/dist/coding/transcript/download-protocol.js +18 -0
- package/dist/coding/transcript/format.d.ts +20 -0
- package/dist/coding/transcript/format.js +32 -0
- package/dist/coding/transcript/recorder.d.ts +51 -0
- package/dist/coding/transcript/recorder.js +225 -0
- package/dist/coding/transcript/resume/checkout.d.ts +31 -0
- package/dist/coding/transcript/resume/checkout.js +203 -0
- package/dist/coding/transcript/resume/command.d.ts +17 -0
- package/dist/coding/transcript/resume/command.js +176 -0
- package/dist/coding/transcript/resume/download.d.ts +20 -0
- package/dist/coding/transcript/resume/download.js +153 -0
- package/dist/coding/transcript/resume/launch.d.ts +38 -0
- package/dist/coding/transcript/resume/launch.js +128 -0
- package/dist/coding/transcript/resume/session-file.d.ts +13 -0
- package/dist/coding/transcript/resume/session-file.js +79 -0
- package/dist/coding/transcript/resume/stand-in.d.ts +11 -0
- package/dist/coding/transcript/resume/stand-in.js +49 -0
- package/dist/coding-worker/artifact.d.ts +8 -3
- package/dist/coding-worker/artifact.js +3 -1
- package/dist/coding-worker/debug-trace.d.ts +6 -5
- package/dist/coding-worker/debug-trace.js +13 -18
- package/dist/coding-worker/dependency-cache.d.mts +229 -0
- package/dist/coding-worker/dependency-cache.mjs +2123 -0
- package/dist/coding-worker/driver.d.ts +4 -3
- package/dist/coding-worker/driver.js +116 -38
- package/dist/coding-worker/errors.d.ts +1 -0
- package/dist/coding-worker/errors.js +32 -0
- package/dist/coding-worker/task-note.d.ts +11 -0
- package/dist/coding-worker/task-note.js +13 -0
- package/dist/coding-worker/types.d.ts +11 -0
- package/dist/config/dependency-cache.d.ts +23 -0
- package/dist/config/dependency-cache.js +32 -0
- package/dist/config/providers.d.ts +12 -3
- package/dist/config/providers.js +55 -5
- package/dist/config/test-simulators.d.ts +82 -0
- package/dist/config/test-simulators.js +305 -0
- package/dist/core/async-context.d.ts +2 -0
- package/dist/core/async-context.js +12 -0
- package/dist/core/budget-events.d.ts +108 -0
- package/dist/core/budget-events.js +346 -0
- package/dist/core/budget-groups.d.ts +50 -1
- package/dist/core/budget-groups.js +236 -27
- package/dist/core/budget-stopped-head.d.ts +46 -0
- package/dist/core/budget-stopped-head.js +81 -0
- package/dist/core/budget-topup.d.ts +51 -0
- package/dist/core/budget-topup.js +156 -0
- package/dist/core/budget-wording.d.ts +16 -2
- package/dist/core/budget-wording.js +40 -2
- package/dist/core/coding-queue.d.ts +2 -1
- package/dist/core/coding-queue.js +7 -0
- package/dist/core/cron.d.ts +19 -1
- package/dist/core/cron.js +17 -0
- package/dist/core/datastores.js +1 -1
- package/dist/core/deferred-coding.d.ts +14 -0
- package/dist/core/deferred-coding.js +62 -0
- package/dist/core/delegation-child.d.ts +25 -0
- package/dist/core/delegation-child.js +37 -0
- package/dist/core/dispatch.d.ts +42 -0
- package/dist/core/dispatch.js +131 -56
- package/dist/core/engine-native.js +1 -1
- package/dist/core/error-code.d.ts +38 -0
- package/dist/core/error-code.js +168 -0
- package/dist/core/grants.d.ts +19 -6
- package/dist/core/grants.js +55 -6
- package/dist/core/host-events.d.ts +18 -2
- package/dist/core/host-events.js +284 -24
- package/dist/core/host-status.d.ts +85 -3
- package/dist/core/host-status.js +298 -20
- package/dist/core/issue-bridge.d.ts +50 -6
- package/dist/core/issue-bridge.js +124 -33
- package/dist/core/issue-events.d.ts +1 -1
- package/dist/core/issue-events.js +4 -10
- package/dist/core/issue-status.d.ts +1 -1
- package/dist/core/issue-status.js +54 -26
- package/dist/core/logger.d.ts +5 -0
- package/dist/core/logger.js +14 -1
- package/dist/core/lost-run-children.d.ts +6 -0
- package/dist/core/lost-run-children.js +32 -0
- package/dist/core/mark-ready.d.ts +34 -0
- package/dist/core/mark-ready.js +152 -0
- package/dist/core/markdown-code.d.ts +8 -0
- package/dist/core/markdown-code.js +107 -0
- package/dist/core/memory-tools.js +7 -0
- package/dist/core/merge-order-check.d.ts +2 -1
- package/dist/core/merge-order-check.js +8 -1
- package/dist/core/notification-templates.d.ts +1 -7
- package/dist/core/notification-templates.js +36 -2
- package/dist/core/pull-request-state-sync.js +2 -2
- package/dist/core/reconciler.d.ts +4 -3
- package/dist/core/reconciler.js +8 -1
- package/dist/core/related-pull-requests.d.ts +11 -3
- package/dist/core/related-pull-requests.js +27 -14
- package/dist/core/reply-parts.d.ts +21 -0
- package/dist/core/reply-parts.js +73 -0
- package/dist/core/review-fix.d.ts +1 -1
- package/dist/core/review-fix.js +2 -1
- package/dist/core/review-host-checks.d.ts +1 -1
- package/dist/core/review-host-tools.d.ts +3 -0
- package/dist/core/review-host-tools.js +99 -13
- package/dist/core/review-reads.d.ts +55 -0
- package/dist/core/review-reads.js +233 -0
- package/dist/core/run-status.d.ts +12 -0
- package/dist/core/run-status.js +12 -0
- package/dist/core/runner.d.ts +80 -34
- package/dist/core/runner.js +439 -144
- package/dist/core/schedule-start.d.ts +12 -0
- package/dist/core/schedule-start.js +14 -0
- package/dist/core/secret-broker-config.d.ts +10 -74
- package/dist/core/secret-guard.d.ts +46 -0
- package/dist/core/secret-guard.js +104 -0
- package/dist/core/secret-guarded-stores.d.ts +12 -0
- package/dist/core/secret-guarded-stores.js +41 -0
- package/dist/core/secret-spans.d.ts +50 -0
- package/dist/core/secret-spans.js +94 -0
- package/dist/core/stop-run.d.ts +46 -0
- package/dist/core/stop-run.js +61 -0
- package/dist/core/workflow-events.d.ts +14 -2
- package/dist/core/workflow-events.js +4 -0
- package/dist/core/workflow-run-events.d.ts +5 -3
- package/dist/core/workflow-run-events.js +43 -8
- package/dist/core/zod-json-schema.d.ts +4 -0
- package/dist/core/zod-json-schema.js +39 -0
- package/dist/core/zod3-window.d.ts +35 -0
- package/dist/core/zod3-window.js +124 -0
- package/dist/generated/prisma/browser.d.ts +34 -3
- package/dist/generated/prisma/client.d.ts +34 -3
- package/dist/generated/prisma/commonInputTypes.d.ts +220 -60
- package/dist/generated/prisma/enums.d.ts +10 -0
- package/dist/generated/prisma/enums.js +8 -0
- package/dist/generated/prisma/internal/class.d.ts +44 -0
- package/dist/generated/prisma/internal/class.js +4 -4
- package/dist/generated/prisma/internal/prismaNamespace.d.ts +428 -9
- package/dist/generated/prisma/internal/prismaNamespace.js +92 -1
- package/dist/generated/prisma/internal/prismaNamespaceBrowser.d.ts +95 -0
- package/dist/generated/prisma/internal/prismaNamespaceBrowser.js +92 -1
- package/dist/generated/prisma/models/Agent.d.ts +106 -8
- package/dist/generated/prisma/models/AgentRepository.d.ts +42 -1
- package/dist/generated/prisma/models/CodingAgentProfile.d.ts +229 -1
- package/dist/generated/prisma/models/CodingProxyRequest.d.ts +207 -3
- package/dist/generated/prisma/models/CodingProxySession.d.ts +114 -1
- package/dist/generated/prisma/models/CodingRun.d.ts +1637 -1
- package/dist/generated/prisma/models/DependencySnapshot.d.ts +1668 -0
- package/dist/generated/prisma/models/DependencySnapshot.js +1 -0
- package/dist/generated/prisma/models/IssuePullRequest.d.ts +6 -2
- package/dist/generated/prisma/models/LocalReview.d.ts +40 -1
- package/dist/generated/prisma/models/NativeGatewayCall.d.ts +39 -1
- package/dist/generated/prisma/models/NativeGatewaySession.d.ts +35 -1
- package/dist/generated/prisma/models/RegistryAllowance.d.ts +41 -1
- package/dist/generated/prisma/models/RegistryApprovedVersion.d.ts +75 -1
- package/dist/generated/prisma/models/RegistryAuditRecord.d.ts +1364 -0
- package/dist/generated/prisma/models/RegistryAuditRecord.js +1 -0
- package/dist/generated/prisma/models/Run.d.ts +738 -2
- package/dist/generated/prisma/models/RunBudgetEvent.d.ts +1211 -0
- package/dist/generated/prisma/models/RunBudgetEvent.js +1 -0
- package/dist/generated/prisma/models/RunTranscript.d.ts +1339 -0
- package/dist/generated/prisma/models/RunTranscript.js +1 -0
- package/dist/generated/prisma/models.d.ts +4 -0
- package/dist/help-index.json +1320 -69
- package/dist/import/create.js +32 -3
- package/dist/import/neutral-schema.d.ts +50 -281
- package/dist/import/neutral-schema.js +5 -4
- package/dist/knowledge/check.d.ts +1 -1
- package/dist/knowledge/cli.d.ts +11 -1
- package/dist/knowledge/cli.js +37 -4
- package/dist/knowledge/concept.d.ts +3 -19
- package/dist/knowledge/location.d.ts +31 -0
- package/dist/knowledge/location.js +78 -0
- package/dist/mcp/auth/access.d.ts +10 -2
- package/dist/mcp/auth/access.js +15 -1
- package/dist/mcp/auth/ownership.d.ts +7 -7
- package/dist/mcp/auth/self-hosted/credentials.d.ts +3 -3
- package/dist/mcp/auth/self-hosted/session.d.ts +2 -2
- package/dist/mcp/host-events/github-ingress.d.ts +2 -0
- package/dist/mcp/host-events/github-ingress.js +2 -1
- package/dist/mcp/index.d.ts +6 -0
- package/dist/mcp/index.js +58 -10
- package/dist/mcp/server.d.ts +13 -0
- package/dist/mcp/server.js +33 -14
- package/dist/mcp/tools/agents.d.ts +4 -0
- package/dist/mcp/tools/agents.js +198 -16
- package/dist/mcp/tools/datastore.js +17 -8
- package/dist/mcp/tools/grants.js +9 -3
- package/dist/mcp/tools/memory.js +11 -1
- package/dist/mcp/tools/notification-channels.js +2 -2
- package/dist/mcp/tools/packages.d.ts +2 -0
- package/dist/mcp/tools/packages.js +195 -0
- package/dist/mcp/tools/repositories.js +18 -5
- package/dist/mcp/tools/run-access.d.ts +16 -0
- package/dist/mcp/tools/run-access.js +25 -0
- package/dist/mcp/tools/run-transcript-download.d.ts +30 -0
- package/dist/mcp/tools/run-transcript-download.js +129 -0
- package/dist/mcp/tools/run-transcript.d.ts +19 -0
- package/dist/mcp/tools/run-transcript.js +70 -0
- package/dist/mcp/tools/runs.js +34 -12
- package/dist/mcp/tools/scheduling.js +3 -1
- package/dist/mcp/tools/secret-elicitation-form.js +5 -0
- package/dist/mcp/tools/secret-elicitation-server.d.ts +6 -0
- package/dist/mcp/tools/secret-elicitation-server.js +10 -61
- package/dist/mcp/tools/secrets.js +2 -2
- package/dist/mcp/tools/stop-run.d.ts +2 -0
- package/dist/mcp/tools/stop-run.js +82 -0
- package/dist/mcp/tools/subagents.js +2 -2
- package/dist/mcp/tools/tools.d.ts +1 -1
- package/dist/mcp/tools/tools.js +25 -9
- package/dist/mcp/transport/stdio-link-host.d.ts +20 -0
- package/dist/mcp/transport/stdio-link-host.js +67 -0
- package/dist/mcp/transport/streamable-http.d.ts +13 -0
- package/dist/mcp/transport/streamable-http.js +41 -1
- package/dist/native-worker/docker-launcher.d.ts +38 -2
- package/dist/native-worker/docker-launcher.js +65 -5
- package/dist/native-worker/gateway-server.d.ts +41 -3
- package/dist/native-worker/gateway-server.js +124 -27
- package/dist/native-worker/gateway.d.ts +27 -1
- package/dist/native-worker/gateway.js +70 -4
- package/dist/native-worker/http-server.d.ts +3 -1
- package/dist/native-worker/http-server.js +11 -7
- package/dist/native-worker/http-transport.js +1 -1
- package/dist/native-worker/kubernetes-launcher.d.ts +7 -3
- package/dist/native-worker/kubernetes-launcher.js +43 -3
- package/dist/native-worker/launch.d.ts +18 -1
- package/dist/native-worker/launch.js +6 -1
- package/dist/native-worker/ledger.d.ts +18 -2
- package/dist/native-worker/ledger.js +57 -4
- package/dist/native-worker/loopback.js +3 -8
- package/dist/native-worker/main.d.ts +2 -1
- package/dist/native-worker/main.js +14 -3
- package/dist/native-worker/protocol.d.ts +129 -577
- package/dist/native-worker/protocol.js +47 -1
- package/dist/native-worker/sandbox-executor.d.ts +17 -2
- package/dist/native-worker/sandbox-executor.js +78 -4
- package/dist/native-worker/stderr-redaction.d.ts +77 -0
- package/dist/native-worker/stderr-redaction.js +237 -0
- package/dist/native-worker/stdio.d.ts +1 -1
- package/dist/native-worker/stdio.js +6 -5
- package/dist/native-worker/warm-pool.d.ts +2 -2
- package/dist/native-worker/warm-pool.js +4 -4
- package/dist/native-worker/worker.js +28 -2
- package/dist/observability/metrics.js +6 -1
- package/dist/providers/auth/self-hosted.d.ts +1 -1
- package/dist/providers/coding-proxy/dependency-cache/eviction.d.ts +29 -0
- package/dist/providers/coding-proxy/dependency-cache/eviction.js +87 -0
- package/dist/providers/coding-proxy/dependency-cache/key.d.ts +33 -0
- package/dist/providers/coding-proxy/dependency-cache/key.js +86 -0
- package/dist/providers/coding-proxy/dependency-cache/model-request-seal.d.ts +18 -0
- package/dist/providers/coding-proxy/dependency-cache/model-request-seal.js +40 -0
- package/dist/providers/coding-proxy/dependency-cache/prisma-store.d.ts +25 -0
- package/dist/providers/coding-proxy/dependency-cache/prisma-store.js +131 -0
- package/dist/providers/coding-proxy/dependency-cache/service.d.ts +70 -0
- package/dist/providers/coding-proxy/dependency-cache/service.js +409 -0
- package/dist/providers/coding-proxy/dependency-cache/store.d.ts +85 -0
- package/dist/providers/coding-proxy/dependency-cache/store.js +95 -0
- package/dist/providers/coding-proxy/hold.d.ts +32 -0
- package/dist/providers/coding-proxy/hold.js +86 -0
- package/dist/providers/coding-proxy/interrupted.d.ts +70 -0
- package/dist/providers/coding-proxy/interrupted.js +104 -0
- package/dist/providers/coding-proxy/memory-ledger.d.ts +15 -2
- package/dist/providers/coding-proxy/memory-ledger.js +84 -8
- package/dist/providers/coding-proxy/metering.d.ts +16 -0
- package/dist/providers/coding-proxy/metering.js +27 -4
- package/dist/providers/coding-proxy/prisma-ledger.d.ts +15 -2
- package/dist/providers/coding-proxy/prisma-ledger.js +179 -67
- package/dist/providers/coding-proxy/proxy.d.ts +14 -1
- package/dist/providers/coding-proxy/proxy.js +166 -14
- package/dist/providers/coding-proxy/registry/audit.d.ts +53 -10
- package/dist/providers/coding-proxy/registry/audit.js +94 -17
- package/dist/providers/coding-proxy/registry/lockfile-parse-worker.d.ts +36 -0
- package/dist/providers/coding-proxy/registry/lockfile-parse-worker.js +67 -0
- package/dist/providers/coding-proxy/registry/lockfile-parse.d.ts +16 -0
- package/dist/providers/coding-proxy/registry/lockfile-parse.js +110 -0
- package/dist/providers/coding-proxy/registry/plan.d.ts +9 -0
- package/dist/providers/coding-proxy/registry/plan.js +201 -48
- package/dist/providers/coding-proxy/registry/prisma-store.d.ts +11 -5
- package/dist/providers/coding-proxy/registry/prisma-store.js +120 -17
- package/dist/providers/coding-proxy/registry/service.d.ts +97 -16
- package/dist/providers/coding-proxy/registry/service.js +579 -112
- package/dist/providers/coding-proxy/registry/store.d.ts +84 -9
- package/dist/providers/coding-proxy/registry/store.js +54 -1
- package/dist/providers/coding-proxy/runtime.d.ts +4 -0
- package/dist/providers/coding-proxy/runtime.js +78 -16
- package/dist/providers/coding-proxy/server.d.ts +6 -1
- package/dist/providers/coding-proxy/server.js +45 -1
- package/dist/providers/coding-proxy/types.d.ts +65 -1
- package/dist/providers/datastore/types.d.ts +5 -0
- package/dist/providers/engine/types.d.ts +17 -2
- package/dist/providers/engine/types.js +17 -0
- package/dist/providers/executor/composition.d.ts +10 -3
- package/dist/providers/executor/composition.js +47 -7
- package/dist/providers/executor/container.d.ts +361 -8
- package/dist/providers/executor/container.js +1136 -91
- package/dist/providers/executor/dbos-status.d.ts +31 -0
- package/dist/providers/executor/dbos-status.js +64 -0
- package/dist/providers/executor/dbos.d.ts +50 -1
- package/dist/providers/executor/dbos.js +207 -20
- package/dist/providers/executor/deferred.d.ts +12 -24
- package/dist/providers/executor/deferred.js +10 -29
- package/dist/providers/executor/in-process.d.ts +9 -1
- package/dist/providers/executor/in-process.js +14 -4
- package/dist/providers/executor/linked-issue-reader.d.ts +16 -0
- package/dist/providers/executor/linked-issue-reader.js +30 -0
- package/dist/providers/executor/routing.d.ts +2 -0
- package/dist/providers/executor/routing.js +4 -0
- package/dist/providers/executor/types.d.ts +13 -0
- package/dist/providers/index.d.ts +2 -1
- package/dist/providers/issue-tracker/types.d.ts +17 -0
- package/dist/providers/issue-tracker/types.js +39 -1
- package/dist/providers/jobs/claude-tool-setup.d.ts +4 -0
- package/dist/providers/jobs/claude-tool-setup.js +5 -2
- package/dist/providers/jobs/docker-isolation.d.ts +8 -0
- package/dist/providers/jobs/docker-isolation.js +17 -0
- package/dist/providers/jobs/docker.d.ts +1 -0
- package/dist/providers/jobs/docker.js +27 -2
- package/dist/providers/jobs/fake-kubernetes-api.d.ts +2 -0
- package/dist/providers/jobs/fake-kubernetes-api.js +3 -0
- package/dist/providers/jobs/kubernetes-api.d.ts +8 -0
- package/dist/providers/jobs/kubernetes-api.js +10 -0
- package/dist/providers/jobs/kubernetes-client.js +10 -1
- package/dist/providers/jobs/kubernetes-isolation.d.ts +2 -0
- package/dist/providers/jobs/kubernetes-isolation.js +17 -1
- package/dist/providers/jobs/kubernetes.d.ts +6 -1
- package/dist/providers/jobs/kubernetes.js +105 -16
- package/dist/providers/jobs/types.d.ts +33 -0
- package/dist/providers/jobs/types.js +13 -1
- package/dist/providers/llm/anthropic.js +5 -1
- package/dist/providers/llm/bedrock.d.ts +0 -12
- package/dist/providers/llm/bedrock.js +4 -0
- package/dist/providers/llm/claude-messages.d.ts +3 -1
- package/dist/providers/llm/claude-messages.js +4 -1
- package/dist/providers/llm/claude-provider.d.ts +2 -2
- package/dist/providers/llm/claude-provider.js +2 -2
- package/dist/providers/llm/openai.d.ts +2 -2
- package/dist/providers/llm/openai.js +6 -2
- package/dist/providers/llm/routing.d.ts +2 -2
- package/dist/providers/llm/routing.js +2 -2
- package/dist/providers/llm/types.d.ts +12 -2
- package/dist/providers/review-host/github-events.d.ts +9 -1
- package/dist/providers/review-host/github-events.js +34 -12
- package/dist/providers/review-host/github.d.ts +29 -2
- package/dist/providers/review-host/github.js +224 -29
- package/dist/providers/review-host/local.d.ts +8 -4
- package/dist/providers/review-host/local.js +49 -31
- package/dist/providers/review-host/patch-pages.d.ts +41 -0
- package/dist/providers/review-host/patch-pages.js +176 -0
- package/dist/providers/review-host/review-format.d.ts +19 -1
- package/dist/providers/review-host/review-format.js +38 -7
- package/dist/providers/review-host/types.d.ts +134 -2
- package/dist/providers/review-host/types.js +8 -0
- package/dist/providers/storage/gcs.d.ts +63 -0
- package/dist/providers/storage/gcs.js +69 -0
- package/dist/providers/storage/index.d.ts +15 -0
- package/dist/providers/storage/index.js +14 -0
- package/dist/providers/storage/local.d.ts +24 -0
- package/dist/providers/storage/local.js +92 -0
- package/dist/providers/storage/meter.d.ts +14 -0
- package/dist/providers/storage/meter.js +34 -0
- package/dist/providers/storage/s3.d.ts +41 -0
- package/dist/providers/storage/s3.js +107 -0
- package/dist/providers/storage/types.d.ts +37 -3
- package/dist/providers/storage/types.js +21 -4
- package/dist/providers/vcs/git.d.ts +33 -2
- package/dist/providers/vcs/git.js +324 -22
- package/dist/providers/vcs/github-remote.d.ts +26 -3
- package/dist/providers/vcs/github-remote.js +124 -14
- package/dist/providers/vcs/github.d.ts +214 -6
- package/dist/providers/vcs/github.js +447 -45
- package/dist/providers/vcs/index.js +8 -1
- package/dist/providers/vcs/pr-description.d.ts +127 -0
- package/dist/providers/vcs/pr-description.js +270 -0
- package/dist/providers/vcs/remote.d.ts +33 -1
- package/dist/providers/vcs/routing.d.ts +4 -2
- package/dist/providers/vcs/routing.js +6 -0
- package/dist/providers/vcs/types.d.ts +80 -3
- package/dist/quickstart/coding-seed.d.ts +14 -3
- package/dist/quickstart/coding-seed.js +213 -4
- package/dist/quickstart/reviewer-prompt.d.ts +1 -1
- package/dist/quickstart/reviewer-prompt.js +6 -6
- package/dist/quickstart-images.json +1 -1
- package/dist/sandbox/generated/zod.bundle.js +62 -1
- package/dist/sandbox/generated/zod3.bundle.js +5 -0
- package/dist/sandbox/host-functions.d.ts +1 -0
- package/dist/sandbox/host-functions.js +6 -11
- package/dist/sandbox/secret-broker.js +3 -4
- package/dist/sandbox/tool-capabilities.d.ts +10 -30
- package/dist/sandbox/zod-params.d.ts +37 -5
- package/dist/sandbox/zod-params.js +181 -26
- package/dist/serve.js +8 -1
- package/dist/viewer/api-schema.d.ts +349 -2627
- package/dist/viewer/api-schema.js +7 -0
- package/dist/viewer/build-schemas.js +6 -2
- package/dist/viewer/openapi.js +2 -4
- package/dist/wardby-bin.js +10 -0
- package/docs/agent-recipes.md +58 -20
- package/docs/code-review-agents.md +551 -41
- package/docs/coding-agent-setup.md +489 -7
- package/docs/coding-packages.md +1033 -90
- package/docs/coding-services.md +27 -12
- package/docs/coding-worker-byo-images.md +20 -0
- package/docs/coding-worker-isolation.md +105 -30
- package/docs/getting-started-gke.md +124 -137
- package/docs/getting-started-identity-provider.md +10 -1
- package/docs/getting-started.md +9 -0
- package/docs/jira-agents.md +17 -8
- package/docs/knowledge/blob-not-found-semantics.md +41 -0
- package/docs/knowledge/bounded-file-descriptor.md +52 -0
- package/docs/knowledge/delegation-admission-slots.md +52 -0
- package/docs/knowledge/http-dispatcher-bootstrap.md +61 -0
- package/docs/knowledge/import-rename-target.md +52 -0
- package/docs/knowledge/index.md +57 -0
- package/docs/knowledge/log.md +9 -0
- package/docs/knowledge/mcp-cancelled-task-precedence.md +41 -0
- package/docs/knowledge/mcp-http-prompt-discovery.md +41 -0
- package/docs/knowledge/mcp-schema-module-boundary.md +41 -0
- package/docs/knowledge/nullable-tool-uniqueness.md +52 -0
- package/docs/knowledge/overview-cli.md +74 -0
- package/docs/knowledge/overview-coding-proxy.md +72 -0
- package/docs/knowledge/overview-coding.md +72 -0
- package/docs/knowledge/overview-config.md +60 -0
- package/docs/knowledge/overview-core.md +72 -0
- package/docs/knowledge/overview-deploy.md +83 -0
- package/docs/knowledge/overview-mcp.md +61 -0
- package/docs/knowledge/overview-prisma.md +72 -0
- package/docs/knowledge/overview-providers.md +89 -0
- package/docs/knowledge/overview-sandbox.md +83 -0
- package/docs/knowledge/overview-surfaces.md +83 -0
- package/docs/knowledge/private-datastore-shared-boundary.md +54 -0
- package/docs/knowledge/proxy-settlement-projections.md +41 -0
- package/docs/knowledge/run-pricing-first-writer.md +43 -0
- package/docs/knowledge/schedule-claim-transaction.md +41 -0
- package/docs/knowledge/secret-entry-replica-contract.md +52 -0
- package/docs/knowledge/terminal-run-first-writer.md +41 -0
- package/docs/knowledge/transactional-agent-authorization.md +52 -0
- package/docs/knowledge/viewer-notify-write-isolation.md +30 -0
- package/docs/knowledge/warm-input-publication.md +30 -0
- package/docs/knowledge/workspace-staging-failure.md +54 -0
- package/docs/knowledge.md +54 -14
- package/docs/native-sandbox.md +119 -23
- package/docs/observability.md +4 -0
- package/docs/security-deployment.md +33 -2
- package/docs/slack-notifications.md +35 -19
- package/docs/tool-secrets.md +42 -10
- package/docs/viewer-api.openapi.yml +1 -1
- package/help/admin-viewer.md +24 -2
- package/help/architecture-agent.md +11 -6
- package/help/build-worker-image.md +3 -1
- package/help/builder-agent.md +19 -6
- package/help/code-review-agents.md +177 -6
- package/help/coding-packages.md +156 -6
- package/help/cost-attribution.md +9 -5
- package/help/creating-agents.md +14 -1
- package/help/dependency-cache.md +143 -0
- package/help/deploy-gke.md +15 -8
- package/help/errors/budget-exhausted.md +187 -0
- package/help/errors/budget-group-exhausted.md +5 -1
- package/help/errors/coding-stream-quota-exceeded.md +33 -0
- package/help/errors/coding-workspace-disk-exceeds-limit.md +39 -0
- package/help/errors/continuation-closed.md +9 -12
- package/help/errors/delegation-conflict.md +24 -0
- package/help/errors/file-list-truncated.md +45 -0
- package/help/errors/full-read-required.md +49 -0
- package/help/errors/github-account-not-allowed.md +4 -0
- package/help/errors/graph-incomplete.md +43 -0
- package/help/errors/graph-limit.md +43 -0
- package/help/errors/merge-conflict.md +49 -0
- package/help/errors/native-sandbox-gateway-error.md +20 -0
- package/help/errors/native-sandbox-launch-abandoned.md +21 -0
- package/help/errors/native-sandbox-worker-exited.md +3 -2
- package/help/errors/package-not-allowed-pending.md +52 -0
- package/help/errors/package-not-allowed.md +51 -0
- package/help/errors/package-refusals.md +43 -0
- package/help/errors/patch-cursor-malformed.md +31 -0
- package/help/errors/run-lost.md +39 -0
- package/help/errors/run-transcript-download.md +44 -0
- package/help/errors/secret-in-value.md +69 -0
- package/help/errors/target-merge.md +40 -0
- package/help/fix-merge-conflicts.md +73 -0
- package/help/github-app-setup.md +132 -0
- package/help/github.md +22 -0
- package/help/identity-and-access.md +2 -0
- package/help/jira.md +23 -6
- package/help/knowledge.md +16 -7
- package/help/local-repositories.md +1 -1
- package/help/low-power-mode.md +7 -1
- package/help/merge-order-check.md +5 -0
- package/help/native-capabilities.md +29 -1
- package/help/native-sandbox.md +15 -1
- package/help/operating-agents.md +5 -0
- package/help/package-audit-mode.md +91 -0
- package/help/related-pull-requests.md +10 -7
- package/help/resume-runs-locally.md +131 -0
- package/help/review-fix-rounds.md +33 -1
- package/help/sharing-agents.md +59 -0
- package/help/slack-notifications.md +19 -5
- package/help/status-comments.md +31 -0
- package/help/stop-run.md +58 -0
- package/help/troubleshooting/budgets.md +28 -0
- package/help/troubleshooting/coding-workers.md +78 -7
- package/package.json +17 -10
- package/prisma/migrations/20261016000000_registry_approved_accepted_advisories/migration.sql +3 -0
- package/prisma/migrations/20261016000001_mark_ready_when_passing/migration.sql +3 -0
- package/prisma/migrations/20261017000000_debug_trace_max_mb/migration.sql +4 -0
- package/prisma/migrations/20261018000000_dependency_cache/migration.sql +53 -0
- package/prisma/migrations/20261019000000_claude_max_output_tokens/migration.sql +5 -0
- package/prisma/migrations/20261021000000_pr_description_hash/migration.sql +4 -0
- package/prisma/migrations/20261022000000_dependency_snapshot_validation_metadata/migration.sql +4 -0
- package/prisma/migrations/20261023000000_package_audit_mode/migration.sql +27 -0
- package/prisma/migrations/20261024000000_budget_events/migration.sql +26 -0
- package/prisma/migrations/20261025000000_cache_aware_holds/migration.sql +9 -0
- package/prisma/migrations/20261026000000_budget_overrun/migration.sql +4 -0
- package/prisma/migrations/20261027000000_coding_run_request/migration.sql +1 -0
- package/prisma/migrations/20261028000000_native_gateway_review_reads/migration.sql +6 -0
- package/prisma/migrations/20261029000000_lead_survives_deploy/migration.sql +6 -0
- package/prisma/migrations/20261030000000_local_review_covered_sha/migration.sql +2 -0
- package/prisma/migrations/20261031000000_coding_run_budget_limit/migration.sql +9 -0
- package/prisma/migrations/20261101000000_run_transcripts/migration.sql +22 -0
- package/prisma/schema.prisma +311 -97
- package/dist/sandbox/generated/zod-to-json-schema.bundle.js +0 -5
package/dist/core/grants.js
CHANGED
|
@@ -4,13 +4,13 @@ export const LEVELS = {
|
|
|
4
4
|
budget_group: ["read", "use", "write"],
|
|
5
5
|
};
|
|
6
6
|
/**
|
|
7
|
-
* Highest level an "everyone" grant may carry
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
7
|
+
* Highest level an "everyone" grant may carry. Agents may be public-write
|
|
8
|
+
* (anyone signed in can edit them) but only while they hold none of their
|
|
9
|
+
* owner's bindings: see agentOwnerBindings and the guard in grant_access and
|
|
10
|
+
* the binding tools. Otherwise anyone could rewrite the prompt of an agent
|
|
11
|
+
* that holds its owner's secrets.
|
|
12
12
|
*/
|
|
13
|
-
export const EVERYONE_MAX = { agent: "
|
|
13
|
+
export const EVERYONE_MAX = { agent: "write", tool: "use", budget_group: "use" };
|
|
14
14
|
/** Types grant_access/revoke_access accept today (Phase 1: agents only). */
|
|
15
15
|
export const SHAREABLE_TYPES = ["agent"];
|
|
16
16
|
export const EVERYONE_KEY = "everyone";
|
|
@@ -124,3 +124,52 @@ export async function deleteGrantsFor(db, type, resourceId) {
|
|
|
124
124
|
const { count } = await db.resourceGrant.deleteMany({ where: { resourceType: type, resourceId } });
|
|
125
125
|
return count;
|
|
126
126
|
}
|
|
127
|
+
/**
|
|
128
|
+
* The owner-only bindings an agent holds, by kind (empty = none). An agent
|
|
129
|
+
* may be public-write only while this is empty; binding a secret, datastore,
|
|
130
|
+
* repository, sub-agent or owner-granted tool capability to a public-write
|
|
131
|
+
* agent is refused for the same reason. A coding profile counts: its
|
|
132
|
+
* repository is the owner's GitHub access, and the prompt is part of every
|
|
133
|
+
* coding task.
|
|
134
|
+
*/
|
|
135
|
+
export async function agentOwnerBindings(db, agentId) {
|
|
136
|
+
const [secrets, datastores, repositories, subagents, capabilityTools, codingProfiles] = await Promise.all([
|
|
137
|
+
db.agentSecret.count({ where: { agentId } }),
|
|
138
|
+
db.agentDatastore.count({ where: { agentId } }),
|
|
139
|
+
db.agentRepository.count({ where: { agentId } }),
|
|
140
|
+
db.agentSubAgent.count({ where: { parentAgentId: agentId } }),
|
|
141
|
+
// The owner's attach_tool stamps its consent even when it grants nothing:
|
|
142
|
+
// only an attachment that carries a capability counts.
|
|
143
|
+
db.agentTool.count({
|
|
144
|
+
where: {
|
|
145
|
+
agentId,
|
|
146
|
+
capabilitiesGrantedById: { not: null },
|
|
147
|
+
NOT: {
|
|
148
|
+
allowedSecrets: { equals: [] },
|
|
149
|
+
allowedDatastorePrefixes: { equals: [] },
|
|
150
|
+
allowedHosts: { equals: [] },
|
|
151
|
+
allowedSharedDatastorePrefixes: { equals: {} },
|
|
152
|
+
},
|
|
153
|
+
},
|
|
154
|
+
}),
|
|
155
|
+
db.codingAgentProfile.count({ where: { agentId } }),
|
|
156
|
+
]);
|
|
157
|
+
return Object.entries({
|
|
158
|
+
secrets,
|
|
159
|
+
datastores,
|
|
160
|
+
repositories,
|
|
161
|
+
"sub-agents": subagents,
|
|
162
|
+
"tool capabilities": capabilityTools,
|
|
163
|
+
"a coding repository": codingProfiles,
|
|
164
|
+
})
|
|
165
|
+
.filter(([, count]) => count > 0)
|
|
166
|
+
.map(([kind]) => kind);
|
|
167
|
+
}
|
|
168
|
+
/** True when everyone holds write on the agent. */
|
|
169
|
+
export async function isPublicWritable(db, agentId) {
|
|
170
|
+
const row = await db.resourceGrant.findFirst({
|
|
171
|
+
where: { resourceType: "agent", resourceId: agentId, granteeKey: EVERYONE_KEY },
|
|
172
|
+
select: { level: true },
|
|
173
|
+
});
|
|
174
|
+
return row !== null && atLeast("agent", row.level, "write");
|
|
175
|
+
}
|
|
@@ -13,7 +13,7 @@ import { type OpenSibling } from "./related-pull-requests.js";
|
|
|
13
13
|
import { type RepoAccessGate } from "./repo-access.js";
|
|
14
14
|
import { type DriftScope } from "../knowledge/relevance.js";
|
|
15
15
|
export type { HostEvent };
|
|
16
|
-
export type HostEventDb = Pick<PrismaClient, "agentRepository" | "deferredReview" | "agent" | "run" | "runHostCheck" | "runHostStatus" | "codingRun" | "task" | "webhook" | "budgetGroup" | "issuePullRequest" | "workItem" | "runAttribution" | "agentIssueProject" | "$transaction" | "$queryRaw">;
|
|
16
|
+
export type HostEventDb = Pick<PrismaClient, "runBudgetEvent" | "agentRepository" | "deferredReview" | "agent" | "run" | "runHostCheck" | "runHostStatus" | "codingRun" | "registryFetch" | "registryAuditRecord" | "task" | "webhook" | "budgetGroup" | "issuePullRequest" | "workItem" | "runAttribution" | "agentIssueProject" | "$transaction" | "$queryRaw">;
|
|
17
17
|
export interface RouteHostEventDeps {
|
|
18
18
|
db: HostEventDb;
|
|
19
19
|
executor: Executor;
|
|
@@ -24,6 +24,12 @@ export interface RouteHostEventDeps {
|
|
|
24
24
|
repoAccess: RepoAccessGate;
|
|
25
25
|
/** Issue trackers for moving/commenting on the issue a closed PR was opened for; absent → pr_closed is ignored. */
|
|
26
26
|
issueTrackers?: IssueTrackerRegistry;
|
|
27
|
+
/**
|
|
28
|
+
* False (WARDBY_MENTION_EXPLANATIONS=false): a mention that starts nothing gets no
|
|
29
|
+
* reply saying why. The reply is a comment anyone who can see the repository reads,
|
|
30
|
+
* so a deployment may keep what is linked there to itself. Absent = true.
|
|
31
|
+
*/
|
|
32
|
+
mentionExplanations?: boolean;
|
|
27
33
|
}
|
|
28
34
|
/** What starting a review needs: everything routing needs except the mention handle. */
|
|
29
35
|
export type ReviewStartDeps = Omit<RouteHostEventDeps, "mentionHandle">;
|
|
@@ -38,6 +44,7 @@ export interface RouteResult {
|
|
|
38
44
|
/** Cosmetic work to do after the webhook response is sent (reactions, status comments). */
|
|
39
45
|
followUps: Array<() => Promise<void>>;
|
|
40
46
|
}
|
|
47
|
+
/** A `@app review` outside code: quoting the command in code documents it. */
|
|
41
48
|
export declare function isReviewCommand(body: string, mentionHandle: string): boolean;
|
|
42
49
|
type PushEvent = Extract<HostEvent, {
|
|
43
50
|
kind: "push";
|
|
@@ -56,7 +63,7 @@ type ConceptCitationStatus = {
|
|
|
56
63
|
* commits, so they and the affected concept paths travel as untrusted context.
|
|
57
64
|
* Commit messages and author names are never included.
|
|
58
65
|
*/
|
|
59
|
-
export declare function mergeTaskText(event: PushEvent, scope: DriftScope | null, bundleComplete?: boolean, citations?: ReadonlyMap<string, ConceptCitationStatus
|
|
66
|
+
export declare function mergeTaskText(event: PushEvent, scope: DriftScope | null, bundleComplete?: boolean, citations?: ReadonlyMap<string, ConceptCitationStatus>, bundlePath?: string): string;
|
|
60
67
|
type MentionEvent = Extract<HostEvent, {
|
|
61
68
|
kind: "mention";
|
|
62
69
|
}>;
|
|
@@ -83,7 +90,16 @@ export declare function siblingContinuationHints(siblings: readonly OpenSibling[
|
|
|
83
90
|
* taskOverride column.
|
|
84
91
|
*/
|
|
85
92
|
export declare function mentionTaskText(event: MentionEvent, siblings?: readonly OpenSibling[]): string;
|
|
93
|
+
/** The reply to a mention whose author can see the repository but cannot push to it. */
|
|
94
|
+
export declare const NO_WRITE_ACCESS_BODY: string;
|
|
86
95
|
export declare function unknownPriorRunBody(priorRunId: string): string;
|
|
96
|
+
/** Why a mention from someone allowed to use wardby started nothing; posted so they are not left guessing. */
|
|
97
|
+
export type MentionDeclineReason = "no_responder" | "no_reviewer" | "agent_not_authorized" | "pull_request_not_reviewable";
|
|
98
|
+
/**
|
|
99
|
+
* Never names the App's handle: this comment is posted by the App, and a
|
|
100
|
+
* body that mentioned it could be matched as a mention of itself.
|
|
101
|
+
*/
|
|
102
|
+
export declare function mentionDeclinedBody(reason: MentionDeclineReason): string;
|
|
87
103
|
export interface ReviewTarget {
|
|
88
104
|
agentId: string;
|
|
89
105
|
checkName: string;
|
package/dist/core/host-events.js
CHANGED
|
@@ -6,16 +6,20 @@
|
|
|
6
6
|
* docs/private/2026-09-25-code-review-host-design.md §6.3.
|
|
7
7
|
*/
|
|
8
8
|
import { Prisma } from "#prisma";
|
|
9
|
-
import { linkedPullRequestAttribution, openingRunAttribution, RESPONSE_PATH_SNAPSHOT_BUDGET } from "./attribution.js";
|
|
9
|
+
import { linkedPullRequestAttribution, openingRunAttribution, resolveWorkItem, RESPONSE_PATH_SNAPSHOT_BUDGET, } from "./attribution.js";
|
|
10
|
+
import { headStoppedAtBudget, pushingRunInFlight } from "./budget-stopped-head.js";
|
|
10
11
|
import { checkContinuation, dispatchRun } from "./dispatch.js";
|
|
11
12
|
import { mentionStatusRow, postMentionStatus } from "./host-status.js";
|
|
12
|
-
import { handlePullRequestClosed } from "./issue-bridge.js";
|
|
13
|
+
import { githubIssueKey, handlePullRequestClosed } from "./issue-bridge.js";
|
|
13
14
|
import { logger } from "./logger.js";
|
|
15
|
+
import { withoutMarkdownCode } from "./markdown-code.js";
|
|
16
|
+
import { maybeMarkReady } from "./mark-ready.js";
|
|
14
17
|
import { syncMergeOrderChecksForPullRequest } from "./merge-order-check.js";
|
|
15
|
-
import { MAX_SIBLING_HINTS, openSiblings, runTreeRoot, SIBLING_GUIDANCE, } from "./related-pull-requests.js";
|
|
18
|
+
import { MAX_SIBLING_HINTS, openPullRequestHints, openSiblings, openSiblingsForIssue, runTreeRoot, SIBLING_GUIDANCE, } from "./related-pull-requests.js";
|
|
16
19
|
import { requiredLevel } from "./repo-access.js";
|
|
17
20
|
import { composeTaskOverride } from "./untrusted-content.js";
|
|
18
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";
|
|
19
23
|
import { driftScope } from "../knowledge/relevance.js";
|
|
20
24
|
import { spanHash } from "../knowledge/span-hash.js";
|
|
21
25
|
const log = logger.child({ module: "host-events" });
|
|
@@ -31,11 +35,14 @@ export const DEFERRED_REVIEW_BATCH = 50;
|
|
|
31
35
|
function escapeRegExp(value) {
|
|
32
36
|
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
33
37
|
}
|
|
38
|
+
/** A `@app review` outside code: quoting the command in code documents it. */
|
|
34
39
|
export function isReviewCommand(body, mentionHandle) {
|
|
35
|
-
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));
|
|
36
41
|
}
|
|
37
42
|
const MAX_BUNDLE_FILES = 200;
|
|
38
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;
|
|
39
46
|
const MAX_LISTED_CHANGES = 200;
|
|
40
47
|
/** The whole bundle load (listing plus reads) must finish inside GitHub's ~10 s webhook window. */
|
|
41
48
|
const BUNDLE_LOAD_DEADLINE_MS = 4000;
|
|
@@ -49,7 +56,7 @@ const BUNDLE_INCOMPLETE_TEXT = "The knowledge bundle could not be fully read; tr
|
|
|
49
56
|
* commits, so they and the affected concept paths travel as untrusted context.
|
|
50
57
|
* Commit messages and author names are never included.
|
|
51
58
|
*/
|
|
52
|
-
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) {
|
|
53
60
|
const statusOf = (concept) => citations.get(concept) ?? { state: "unverified" };
|
|
54
61
|
const changed = event.changedPathsComplete
|
|
55
62
|
? `Changed files:\n${[
|
|
@@ -61,7 +68,7 @@ export function mergeTaskText(event, scope, bundleComplete = true, citations = n
|
|
|
61
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.";
|
|
62
69
|
const listed = scope
|
|
63
70
|
? `Knowledge concepts whose citations, affects globs, or files changed (${scope.reason}):\n` +
|
|
64
|
-
scope.concepts.map((p) => `- ${
|
|
71
|
+
scope.concepts.map((p) => `- ${bundlePath}/${p} — ${describeStatus(statusOf(p))}`).join("\n")
|
|
65
72
|
: null;
|
|
66
73
|
let context;
|
|
67
74
|
if (!bundleComplete)
|
|
@@ -76,7 +83,7 @@ export function mergeTaskText(event, scope, bundleComplete = true, citations = n
|
|
|
76
83
|
if (scope && scope.concepts.length > 0) {
|
|
77
84
|
const states = scope.concepts.map((p) => statusOf(p).state);
|
|
78
85
|
const count = (state) => states.filter((x) => x === state).length;
|
|
79
|
-
const onlyKnowledge = event.changedPathsComplete && event.changedPaths.every((p) => p.startsWith(`${
|
|
86
|
+
const onlyKnowledge = event.changedPathsComplete && event.changedPaths.every((p) => p.startsWith(`${bundlePath}/`));
|
|
80
87
|
summary =
|
|
81
88
|
`Citation check at ${event.after.slice(0, 12)}: ${count("verified")} of ${states.length} affected concepts verified, ` +
|
|
82
89
|
`${count("stale")} stale, ${count("unverified")} not verified. Only knowledge files changed: ${onlyKnowledge ? "yes" : "no"}.`;
|
|
@@ -106,13 +113,36 @@ function startBudget(ms) {
|
|
|
106
113
|
});
|
|
107
114
|
return { promise, expired: () => expired, clear: () => clearTimeout(timer) };
|
|
108
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
|
+
}
|
|
109
139
|
/**
|
|
110
140
|
* The knowledge bundle at the pushed commit, read with bounded concurrency
|
|
111
141
|
* under one overall deadline. A failure never fails the route; it yields an
|
|
112
142
|
* incomplete result so the watcher treats every concept as possibly affected.
|
|
113
143
|
*/
|
|
114
|
-
async function loadBundle(host, event, budget) {
|
|
115
|
-
const prefix = `${
|
|
144
|
+
async function loadBundle(host, event, budget, bundlePath) {
|
|
145
|
+
const prefix = `${bundlePath}/`;
|
|
116
146
|
const concepts = [];
|
|
117
147
|
const where = { repository: event.repository, after: event.after };
|
|
118
148
|
let filesRead = 0;
|
|
@@ -306,6 +336,12 @@ export function mentionTaskText(event, siblings = []) {
|
|
|
306
336
|
if (hints)
|
|
307
337
|
sections.push(hints);
|
|
308
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
|
+
}
|
|
309
345
|
const title = event.subject?.title.replace(/\s+/g, " ").trim().slice(0, MAX_TITLE);
|
|
310
346
|
const description = event.subject?.body.trim() ? event.subject.body.slice(0, MAX_TASK_BODY) : "";
|
|
311
347
|
sections.push([
|
|
@@ -342,11 +378,49 @@ async function priorRunContinues(deps, event) {
|
|
|
342
378
|
const check = await checkContinuation(deps.db, event.priorRunId, event.repository);
|
|
343
379
|
return check.ok && check.root.pullRequestNumber === event.number;
|
|
344
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
|
+
}
|
|
345
400
|
export function unknownPriorRunBody(priorRunId) {
|
|
346
401
|
return (`❌ I can't continue this pull request. Its description names wardby run \`${priorRunId}\`, but this wardby ` +
|
|
347
402
|
"deployment has no record of that run opening this pull request (another wardby deployment may have opened it). " +
|
|
348
403
|
"Ask the deployment that opened it, or make the change on this branch by hand.");
|
|
349
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
|
+
}
|
|
350
424
|
/** A review run's task: the head to review, plus any extra context its trigger adds. */
|
|
351
425
|
export function reviewTaskText(repository, prNumber, headSha, context) {
|
|
352
426
|
const task = `Review pull request #${prNumber} in ${repository} (head ${headSha}).`;
|
|
@@ -457,6 +531,59 @@ context) {
|
|
|
457
531
|
}
|
|
458
532
|
return runIds;
|
|
459
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
|
+
}
|
|
460
587
|
/**
|
|
461
588
|
* CI finished on a pull request's head: re-runs, once per head, each review
|
|
462
589
|
* that published only a COMMENT while CI there was still running or had not
|
|
@@ -492,6 +619,16 @@ async function rereviewAfterCi(deps, host, event, prNumber, reviewerLinks) {
|
|
|
492
619
|
// Only reviewers still authorized are claimed, so a denied one keeps its chance for later.
|
|
493
620
|
const allowed = await authorizedLinks(deps, event, reviewerLinks.filter((l) => waiting.some((r) => r.run.agentId === l.agentId)));
|
|
494
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
|
+
}
|
|
495
632
|
// Claimed per row, so concurrent completions re-run each review only once.
|
|
496
633
|
const agentIds = new Set();
|
|
497
634
|
for (const row of waiting.filter((r) => allowedIds.has(r.run.agentId))) {
|
|
@@ -624,6 +761,22 @@ async function deferUntilCi(deps, host, key, links) {
|
|
|
624
761
|
}
|
|
625
762
|
return { startNow: [], runIds };
|
|
626
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
|
+
}
|
|
627
780
|
/**
|
|
628
781
|
* Starts deferred reviews for one PR head. The head must still be that sha,
|
|
629
782
|
* open, and not a fork; otherwise every row is dropped (a newer push records
|
|
@@ -641,6 +794,9 @@ async function startDeferred(deps, host, key, rows, reviewerLinks, mode, reason)
|
|
|
641
794
|
log.info({ ...where, state: head.state }, "deferred review dropped: the pull request moved on or closed");
|
|
642
795
|
return [];
|
|
643
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);
|
|
644
800
|
const allowed = await authorizedLinks(deps, key, reviewerLinks.filter((l) => rows.some((r) => r.agentId === l.agentId)));
|
|
645
801
|
const allowedIds = new Set(allowed.map((l) => l.agentId));
|
|
646
802
|
const targets = [];
|
|
@@ -659,6 +815,10 @@ async function startDeferred(deps, host, key, rows, reviewerLinks, mode, reason)
|
|
|
659
815
|
return [];
|
|
660
816
|
log.info({ ...where, agentIds: targets.map((t) => t.agentId), reason }, "starting the deferred review");
|
|
661
817
|
try {
|
|
818
|
+
if (decision.kind === "skip") {
|
|
819
|
+
await postSkippedReviewChecks(deps, host, key.repository, key.prNumber, key.headSha, targets);
|
|
820
|
+
return [];
|
|
821
|
+
}
|
|
662
822
|
return await startReviews(deps, host, key.repository, key.prNumber, key.headSha, targets, true);
|
|
663
823
|
}
|
|
664
824
|
catch (err) {
|
|
@@ -809,13 +969,36 @@ export async function requestLeadRunId(db, prMarkerRunId, repository) {
|
|
|
809
969
|
}
|
|
810
970
|
/** Run statuses that mean the delegating run has not finished. */
|
|
811
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
|
+
}
|
|
812
992
|
/**
|
|
813
993
|
* pr_updated for a pull request a delegated coding run opened, while the
|
|
814
994
|
* request's lead run is still running: records one DeferredReview row per
|
|
815
995
|
* reviewer (reason "request", with or without waitForCi) and starts nothing;
|
|
816
996
|
* the lead's finalizer (startDeferredForRequest) or the sweep starts them.
|
|
817
997
|
* Returns null, to review as without it, for any other pull request, or when
|
|
818
|
-
* the lead cannot be resolved or the deferral cannot be recorded.
|
|
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.
|
|
819
1002
|
*/
|
|
820
1003
|
async function deferUntilRequest(deps, host, key, links,
|
|
821
1004
|
/** The pull request's origin, read at most once per event (shared with the merge order check). */
|
|
@@ -828,6 +1011,15 @@ readOrigin) {
|
|
|
828
1011
|
if (!origin.markerRunId)
|
|
829
1012
|
return null;
|
|
830
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
|
+
}
|
|
831
1023
|
}
|
|
832
1024
|
catch (err) {
|
|
833
1025
|
log.warn({ err, repository: key.repository, prNumber: key.prNumber }, "could not read the pull request's delegating run; reviewing as usual");
|
|
@@ -1022,11 +1214,14 @@ export async function routeHostEvent(event, deps) {
|
|
|
1022
1214
|
const budget = startBudget(BUNDLE_LOAD_DEADLINE_MS);
|
|
1023
1215
|
let taskOverride;
|
|
1024
1216
|
try {
|
|
1025
|
-
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);
|
|
1026
1221
|
const scope = driftScope({
|
|
1027
1222
|
changedPaths: event.changedPaths,
|
|
1028
1223
|
changedPathsComplete: event.changedPathsComplete,
|
|
1029
|
-
bundlePath
|
|
1224
|
+
bundlePath,
|
|
1030
1225
|
concepts,
|
|
1031
1226
|
});
|
|
1032
1227
|
// Only the concepts the merge affects are checked, and only against a bundle that was fully read.
|
|
@@ -1034,7 +1229,7 @@ export async function routeHostEvent(event, deps) {
|
|
|
1034
1229
|
const citations = complete
|
|
1035
1230
|
? await verifyCitations(host, event, concepts.filter((c) => affected.has(c.path)), budget)
|
|
1036
1231
|
: new Map();
|
|
1037
|
-
taskOverride = mergeTaskText(event, scope, complete, citations);
|
|
1232
|
+
taskOverride = mergeTaskText(event, scope, complete, citations, bundlePath);
|
|
1038
1233
|
}
|
|
1039
1234
|
finally {
|
|
1040
1235
|
budget.clear();
|
|
@@ -1101,7 +1296,15 @@ export async function routeHostEvent(event, deps) {
|
|
|
1101
1296
|
const reviewers = reviewTargets(allowed.filter((l) => !l.waitForCi || startNow.has(l)));
|
|
1102
1297
|
const runIds = [...deferral.runIds];
|
|
1103
1298
|
if (reviewers.length > 0) {
|
|
1104
|
-
|
|
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
|
+
}
|
|
1105
1308
|
}
|
|
1106
1309
|
return { runIds, followUps };
|
|
1107
1310
|
}
|
|
@@ -1109,11 +1312,16 @@ export async function routeHostEvent(event, deps) {
|
|
|
1109
1312
|
if (reviewerLinks.length === 0)
|
|
1110
1313
|
return none;
|
|
1111
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
|
+
});
|
|
1112
1320
|
for (const prNumber of event.prNumbers) {
|
|
1113
1321
|
runIds.push(...(await rereviewAfterCi(deps, host, event, prNumber, reviewerLinks)));
|
|
1114
1322
|
runIds.push(...(await startDeferredAfterCi(deps, host, event, prNumber, reviewerLinks)));
|
|
1115
1323
|
}
|
|
1116
|
-
return { runIds, followUps
|
|
1324
|
+
return { runIds, followUps };
|
|
1117
1325
|
}
|
|
1118
1326
|
case "check_rerun": {
|
|
1119
1327
|
const owners = reviewerLinks.filter((l) => l.checkName === event.checkName);
|
|
@@ -1136,12 +1344,15 @@ export async function routeHostEvent(event, deps) {
|
|
|
1136
1344
|
const reviewCommand = event.isPullRequest && isReviewCommand(event.body, deps.mentionHandle);
|
|
1137
1345
|
const responderLink = links.find((l) => l.access === "write" && l.triggers.includes("mention"));
|
|
1138
1346
|
const candidates = reviewCommand ? reviewerLinks : responderLink ? [responderLink] : [];
|
|
1139
|
-
|
|
1347
|
+
// Explanations off and nothing to start: say nothing, and skip the author lookup.
|
|
1348
|
+
if (candidates.length === 0 && deps.mentionExplanations === false)
|
|
1140
1349
|
return none;
|
|
1141
1350
|
// H5-3: a mention drives a write-capable agent holding its owner's
|
|
1142
1351
|
// tools and secrets, so its author needs real push access to the
|
|
1143
1352
|
// repository (author_association, checked upstream, is only a
|
|
1144
|
-
// 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.
|
|
1145
1356
|
const author = await deps.repoAccess.authorizeHostUser({
|
|
1146
1357
|
provider: event.provider,
|
|
1147
1358
|
repository: event.repository,
|
|
@@ -1150,15 +1361,54 @@ export async function routeHostEvent(event, deps) {
|
|
|
1150
1361
|
});
|
|
1151
1362
|
if (!author.ok) {
|
|
1152
1363
|
log.info({ repository: event.repository, number: event.number, reason: author.reason, level: author.level }, "mention ignored: its author lacks write access");
|
|
1153
|
-
|
|
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] };
|
|
1154
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");
|
|
1155
1405
|
const allowed = await authorizedLinks(deps, event, candidates);
|
|
1156
1406
|
if (allowed.length === 0)
|
|
1157
|
-
return
|
|
1407
|
+
return decline("agent_not_authorized");
|
|
1158
1408
|
if (reviewCommand) {
|
|
1159
1409
|
const head = await host.pullRequestHead(event.repository, event.number);
|
|
1160
1410
|
if (head.isFork || head.state !== "open")
|
|
1161
|
-
return
|
|
1411
|
+
return decline("pull_request_not_reviewable");
|
|
1162
1412
|
return {
|
|
1163
1413
|
runIds: await startReviews(deps, host, event.repository, event.number, head.headSha, reviewTargets(allowed)),
|
|
1164
1414
|
followUps: [react],
|
|
@@ -1183,9 +1433,15 @@ export async function routeHostEvent(event, deps) {
|
|
|
1183
1433
|
return { runIds: [], followUps: [react, reply] };
|
|
1184
1434
|
}
|
|
1185
1435
|
// Seeded from the PR's validated marker run: the request it belongs to. Stored state only.
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
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
|
+
});
|
|
1189
1445
|
const dispatched = await dispatchRun({
|
|
1190
1446
|
db: deps.db,
|
|
1191
1447
|
executor: deps.executor,
|
|
@@ -1195,7 +1451,11 @@ export async function routeHostEvent(event, deps) {
|
|
|
1195
1451
|
taskOverride: mentionTaskText(event, siblings),
|
|
1196
1452
|
attribution: event.isPullRequest
|
|
1197
1453
|
? await linkedPullRequestAttribution(deps.db, deps.issueTrackers, { codeProvider: host.provider, repository: event.repository, number: event.number }, RESPONSE_PATH_SNAPSHOT_BUDGET)
|
|
1198
|
-
:
|
|
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
|
+
},
|
|
1199
1459
|
// Where to report, written with the run: even if this instance dies
|
|
1200
1460
|
// before the follow-up below, the run's outcome still gets a comment.
|
|
1201
1461
|
afterPersist: async (tx, run) => {
|