@wardby/cli 0.5.2 → 0.6.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/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 +7 -1
- package/dist/claude-coding-worker/driver.js +38 -5
- package/dist/claude-coding-worker/sdk.js +13 -4
- package/dist/cli-help.d.ts +1 -1
- package/dist/cli-help.js +5 -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 +141 -5
- package/dist/coding/claude-context.d.ts +45 -0
- package/dist/coding/claude-context.js +434 -0
- package/dist/coding/local-git.js +9 -1
- package/dist/coding/profile.d.ts +12 -0
- package/dist/coding/profile.js +6 -0
- package/dist/coding/protocol.d.ts +104 -0
- package/dist/coding/protocol.js +87 -3
- package/dist/coding/registry/pypi.d.ts +25 -1
- package/dist/coding/registry/pypi.js +140 -16
- package/dist/coding/registry/types.d.ts +17 -3
- package/dist/coding-worker/artifact.d.ts +8 -0
- package/dist/coding-worker/artifact.js +2 -2
- package/dist/coding-worker/driver.js +2 -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/types.d.ts +2 -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 +56 -2
- package/dist/config/providers.js +92 -6
- package/dist/core/db.d.ts +16 -6
- package/dist/core/db.js +12 -10
- package/dist/core/dispatch.d.ts +11 -0
- package/dist/core/dispatch.js +36 -1
- package/dist/core/engine-native.js +9 -5
- package/dist/core/host-events.d.ts +49 -3
- package/dist/core/host-events.js +289 -15
- package/dist/core/http-runtime.js +13 -6
- package/dist/core/issue-bridge.js +42 -2
- package/dist/core/issue-events.js +12 -1
- package/dist/core/issue-status.js +14 -3
- package/dist/core/merge-order-check.d.ts +129 -0
- package/dist/core/merge-order-check.js +336 -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 +25 -0
- package/dist/core/notification-templates.js +105 -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/reconciler.d.ts +4 -2
- package/dist/core/reconciler.js +29 -5
- package/dist/core/related-pull-requests.d.ts +48 -4
- package/dist/core/related-pull-requests.js +113 -11
- package/dist/core/repo-access.d.ts +15 -0
- package/dist/core/repo-access.js +66 -0
- package/dist/core/review-fix.d.ts +37 -0
- package/dist/core/review-fix.js +207 -6
- package/dist/core/review-host-tools.d.ts +20 -0
- package/dist/core/review-host-tools.js +58 -2
- package/dist/core/runner.d.ts +336 -3
- package/dist/core/runner.js +894 -317
- package/dist/core/secret-broker-config.d.ts +103 -0
- package/dist/core/secret-broker-config.js +112 -0
- package/dist/core/secrets.d.ts +39 -4
- package/dist/core/secrets.js +148 -40
- 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 +82 -0
- package/dist/core/workflow-events.js +44 -0
- package/dist/core/workflow-run-events.d.ts +16 -0
- package/dist/core/workflow-run-events.js +40 -0
- package/dist/generated/prisma/browser.d.ts +58 -0
- package/dist/generated/prisma/client.d.ts +58 -0
- package/dist/generated/prisma/commonInputTypes.d.ts +150 -0
- package/dist/generated/prisma/enums.d.ts +27 -0
- package/dist/generated/prisma/enums.js +23 -0
- package/dist/generated/prisma/internal/class.d.ts +88 -0
- package/dist/generated/prisma/internal/class.js +4 -4
- package/dist/generated/prisma/internal/prismaNamespace.d.ts +772 -1
- package/dist/generated/prisma/internal/prismaNamespace.js +135 -4
- package/dist/generated/prisma/internal/prismaNamespaceBrowser.d.ts +139 -0
- package/dist/generated/prisma/internal/prismaNamespaceBrowser.js +135 -4
- package/dist/generated/prisma/models/Agent.d.ts +360 -1
- package/dist/generated/prisma/models/CodingAgentProfile.d.ts +73 -1
- package/dist/generated/prisma/models/CodingRun.d.ts +205 -1
- package/dist/generated/prisma/models/DeferredReview.d.ts +78 -1
- package/dist/generated/prisma/models/NativeGatewayCall.d.ts +1675 -0
- package/dist/generated/prisma/models/NativeGatewayCall.js +1 -0
- package/dist/generated/prisma/models/NativeGatewaySession.d.ts +1517 -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/Run.d.ts +826 -1
- package/dist/generated/prisma/models/RunHostCheck.d.ts +36 -1
- 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 +8 -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 +1492 -113
- package/dist/import/create.js +17 -0
- package/dist/import/neutral-schema.d.ts +6 -2
- package/dist/import/neutral-schema.js +2 -0
- package/dist/mcp/auth/access.d.ts +1 -0
- package/dist/mcp/auth/ownership.d.ts +1 -1
- 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/context.d.ts +6 -0
- package/dist/mcp/host-events/github-ingress.d.ts +23 -1
- package/dist/mcp/host-events/github-ingress.js +68 -0
- package/dist/mcp/index.d.ts +13 -0
- package/dist/mcp/index.js +59 -10
- package/dist/mcp/tools/agents.js +83 -12
- package/dist/mcp/tools/notification-channels.d.ts +2 -0
- package/dist/mcp/tools/notification-channels.js +293 -0
- package/dist/mcp/tools/power.d.ts +2 -0
- package/dist/mcp/tools/power.js +77 -0
- package/dist/mcp/tools/runs.d.ts +0 -6
- package/dist/mcp/tools/runs.js +7 -2
- 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 +200 -10
- package/dist/mcp/tools/secret-elicitation-server.js +8 -1
- 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 +184 -7
- package/dist/mcp/tools/tools.d.ts +1 -1
- package/dist/mcp/tools/tools.js +22 -2
- 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 +74 -0
- package/dist/native-worker/docker-launcher.js +237 -0
- package/dist/native-worker/gateway-server.d.ts +39 -0
- package/dist/native-worker/gateway-server.js +315 -0
- package/dist/native-worker/gateway.d.ts +44 -0
- package/dist/native-worker/gateway.js +197 -0
- package/dist/native-worker/http-server.d.ts +22 -0
- package/dist/native-worker/http-server.js +109 -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 +102 -0
- package/dist/native-worker/kubernetes-launcher.js +335 -0
- package/dist/native-worker/launch.d.ts +45 -0
- package/dist/native-worker/launch.js +44 -0
- package/dist/native-worker/ledger.d.ts +109 -0
- package/dist/native-worker/ledger.js +192 -0
- package/dist/native-worker/loopback.d.ts +12 -0
- package/dist/native-worker/loopback.js +49 -0
- package/dist/native-worker/main.d.ts +7 -0
- package/dist/native-worker/main.js +74 -0
- package/dist/native-worker/protocol.d.ts +744 -0
- package/dist/native-worker/protocol.js +200 -0
- package/dist/native-worker/sandbox-executor.d.ts +68 -0
- package/dist/native-worker/sandbox-executor.js +210 -0
- package/dist/native-worker/stdio.d.ts +29 -0
- package/dist/native-worker/stdio.js +188 -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 +115 -0
- 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 +13 -1
- 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/mock-upstream.d.ts +33 -0
- package/dist/providers/coding-proxy/mock-upstream.js +116 -0
- package/dist/providers/coding-proxy/registry/prisma-store.d.ts +3 -0
- package/dist/providers/coding-proxy/registry/prisma-store.js +17 -0
- package/dist/providers/coding-proxy/registry/service.d.ts +4 -0
- package/dist/providers/coding-proxy/registry/service.js +20 -3
- package/dist/providers/coding-proxy/registry/store.d.ts +18 -1
- package/dist/providers/coding-proxy/registry/store.js +33 -0
- package/dist/providers/coding-proxy/runtime.js +12 -1
- package/dist/providers/coding-proxy/types.d.ts +2 -0
- package/dist/providers/engine/types.d.ts +19 -0
- package/dist/providers/engine/types.js +22 -0
- package/dist/providers/executor/composition.d.ts +24 -1
- package/dist/providers/executor/composition.js +39 -6
- package/dist/providers/executor/container.d.ts +24 -1
- package/dist/providers/executor/container.js +101 -11
- package/dist/providers/executor/dbos.d.ts +42 -0
- package/dist/providers/executor/dbos.js +118 -12
- package/dist/providers/executor/deferred.d.ts +56 -0
- package/dist/providers/executor/deferred.js +119 -0
- package/dist/providers/executor/routing.d.ts +37 -10
- package/dist/providers/executor/routing.js +73 -32
- package/dist/providers/executor/types.d.ts +12 -0
- package/dist/providers/jobs/docker.js +3 -3
- package/dist/providers/jobs/fake-kubernetes-api.d.ts +21 -2
- package/dist/providers/jobs/fake-kubernetes-api.js +42 -1
- package/dist/providers/jobs/kubernetes-api.d.ts +17 -1
- package/dist/providers/jobs/kubernetes-api.js +7 -0
- package/dist/providers/jobs/kubernetes-client.d.ts +10 -1
- package/dist/providers/jobs/kubernetes-client.js +52 -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 +16 -1
- package/dist/providers/jobs/kubernetes-isolation.js +58 -5
- 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 +56 -7
- package/dist/providers/jobs/kubernetes.js +117 -36
- 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 +9 -0
- package/dist/providers/llm/openai.js +0 -1
- package/dist/providers/review-host/github.d.ts +11 -1
- package/dist/providers/review-host/github.js +50 -0
- package/dist/providers/review-host/types.d.ts +23 -0
- package/dist/providers/vcs/github.d.ts +32 -6
- package/dist/providers/vcs/github.js +74 -16
- package/dist/quickstart/coding-db.js +14 -2
- package/dist/quickstart/coding-doctor.d.ts +3 -0
- package/dist/quickstart/coding-doctor.js +3 -0
- package/dist/quickstart/coding-images.d.ts +9 -0
- package/dist/quickstart/coding-images.js +39 -0
- package/dist/quickstart/coding-seed.d.ts +20 -1
- package/dist/quickstart/coding-seed.js +96 -7
- package/dist/quickstart/coding.d.ts +2 -0
- package/dist/quickstart/coding.js +75 -3
- package/dist/quickstart/config.d.ts +2 -0
- package/dist/quickstart/config.js +4 -0
- package/dist/quickstart/images.d.ts +13 -0
- package/dist/quickstart/images.js +80 -12
- package/dist/quickstart/index.d.ts +0 -2
- package/dist/quickstart/index.js +10 -14
- package/dist/quickstart/manifests.d.ts +56 -0
- package/dist/quickstart/manifests.js +523 -0
- package/dist/quickstart/python-detect.d.ts +10 -0
- package/dist/quickstart/python-detect.js +32 -0
- package/dist/quickstart/repo-packages.d.ts +10 -0
- package/dist/quickstart/repo-packages.js +132 -0
- package/dist/quickstart/reviewer-prompt.d.ts +12 -0
- package/dist/quickstart/reviewer-prompt.js +84 -0
- package/dist/quickstart/scenarios.d.ts +19 -0
- package/dist/quickstart/scenarios.js +28 -0
- package/dist/quickstart/sync-reviewer-prompt.d.ts +1 -0
- package/dist/quickstart/sync-reviewer-prompt.js +10 -0
- 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/host-functions.d.ts +34 -0
- package/dist/sandbox/host-functions.js +144 -80
- 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 +162 -0
- package/dist/sandbox/user-tool.d.ts +14 -0
- package/dist/sandbox/user-tool.js +32 -0
- package/dist/serve.js +18 -0
- package/dist/viewer/api-schema.d.ts +214 -18
- package/dist/viewer/api-schema.js +22 -0
- package/dist/viewer/build-schemas.js +4 -1
- 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 +172 -0
- package/docs/README.md +7 -0
- package/docs/agent-recipes.md +57 -8
- package/docs/architecture-runtime.md +16 -1
- package/docs/code-review-agents.md +139 -5
- package/docs/coding-agent-setup.md +127 -2
- package/docs/coding-packages.md +105 -5
- package/docs/coding-services.md +3 -1
- package/docs/coding-worker-byo-images.md +239 -2
- package/docs/coding-worker-isolation.md +88 -23
- package/docs/getting-started-gke.md +225 -9
- package/docs/getting-started-identity-provider.md +21 -10
- package/docs/getting-started.md +140 -11
- package/docs/jira-agents.md +5 -1
- package/docs/knowledge.md +11 -0
- package/docs/native-sandbox.md +635 -0
- package/docs/release-verification.md +9 -0
- package/docs/security-deployment.md +113 -12
- package/docs/slack-notifications.md +252 -0
- package/docs/tool-secrets.md +252 -0
- package/docs/viewer-api.md +46 -5
- package/docs/viewer-api.openapi.yml +1404 -0
- package/help/admin-viewer.md +6 -2
- package/help/agent-recipes.md +27 -0
- package/help/architecture-agent.md +136 -0
- package/help/brokered-secrets.md +79 -0
- package/help/build-claude-code-image.md +131 -0
- package/help/build-worker-image.md +195 -0
- package/help/code-review-agents.md +68 -6
- package/help/coding-packages.md +31 -2
- package/help/creating-agents.md +14 -0
- package/help/deploy-gke.md +37 -6
- package/help/deployment-targets.md +24 -13
- package/help/errors/coding-provider-not-configured.md +2 -2
- package/help/errors/database-auth-invalid.md +28 -0
- package/help/errors/github-account-not-allowed.md +25 -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/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-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-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 +20 -0
- package/help/errors/native-sandbox-worker-lost.md +20 -0
- package/help/errors/native-sandbox-worker-unready.md +22 -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-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/getting-started.md +18 -0
- package/help/github.md +18 -1
- package/help/identity-and-access.md +5 -1
- package/help/local-repositories.md +35 -5
- package/help/low-power-mode.md +125 -0
- package/help/merge-order-check.md +117 -0
- package/help/mobile-app.md +54 -0
- package/help/native-sandbox.md +136 -0
- package/help/observability.md +8 -0
- package/help/related-pull-requests.md +70 -9
- package/help/repo-instructions-and-skills.md +91 -0
- package/help/review-fix-rounds.md +10 -0
- package/help/slack-notifications.md +62 -0
- package/help/troubleshooting/coding-workers.md +21 -0
- package/package.json +13 -5
- 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/schema.prisma +362 -91
|
@@ -3,9 +3,18 @@
|
|
|
3
3
|
* request opened by a coding run in the same top-level run tree (a lead that
|
|
4
4
|
* fanned out to several repositories), plus any recorded for the same tracker
|
|
5
5
|
* issue (IssuePullRequest). Control-plane rows only.
|
|
6
|
-
* Ordered by
|
|
7
|
-
*
|
|
8
|
-
*
|
|
6
|
+
* Ordered by the delegating agent's declared merge order when any entry has
|
|
7
|
+
* one (CodingRun.mergeOrder; nulls last), then by when each pull request's
|
|
8
|
+
* coding run was dispatched: the lead's delegation order (for a lead with
|
|
9
|
+
* parallelDelegations, the order its concurrent delegations were admitted,
|
|
10
|
+
* ties broken by repository#number). A pull request's mergeOrder is the
|
|
11
|
+
* newest non-null value set by its opener run or any later continuation
|
|
12
|
+
* that acted on it, so a lead that re-delegates with a different order
|
|
13
|
+
* after the fact is reflected here. When mergeOrder is set on any entry,
|
|
14
|
+
* every truncation cap below this collector (MAX_RELATED_GROUP here,
|
|
15
|
+
* MAX_SIBLING_HINTS on openSiblingsOf, MAX_RELATED_PULL_REQUESTS on
|
|
16
|
+
* renderRelatedSection) drops entries with no order first, since they sort
|
|
17
|
+
* last.
|
|
9
18
|
* A PR opened while siblings were still running lists only the PRs opened
|
|
10
19
|
* before it; updateRelatedPullRequests rewrites every open PR of the set
|
|
11
20
|
* with the full list when the lead run ends. Continuations join
|
|
@@ -13,7 +22,7 @@
|
|
|
13
22
|
* pull requests brings the original request's tree, and the original request
|
|
14
23
|
* brings every later follow-up tree that continued one of its pull requests.
|
|
15
24
|
*/
|
|
16
|
-
import { type PrismaClient, type Run } from "#prisma";
|
|
25
|
+
import { type PrismaClient, type Run, type RunStatus } from "#prisma";
|
|
17
26
|
import { type IssueTrackerRegistry } from "../providers/issue-tracker/types.js";
|
|
18
27
|
import type { ReviewHostRegistry } from "../providers/review-host/types.js";
|
|
19
28
|
/** Most pull requests a group holds; the description shows fewer (MAX_RELATED_PULL_REQUESTS). */
|
|
@@ -28,6 +37,13 @@ export interface RelatedPullRequest {
|
|
|
28
37
|
openedByRunId: string;
|
|
29
38
|
/** Only when stored (IssuePullRequest.state, kept current by pr_closed); otherwise unknown. */
|
|
30
39
|
state?: StoredPullRequestState;
|
|
40
|
+
/**
|
|
41
|
+
* Step in the delegating agent's merge order (CodingRun.mergeOrder, 1-99):
|
|
42
|
+
* the newest non-null value among the coding run that opened it and its
|
|
43
|
+
* continuations. Absent when none of them set one, or when the entry only
|
|
44
|
+
* came from IssuePullRequest (no CodingRun to read it from).
|
|
45
|
+
*/
|
|
46
|
+
mergeOrder?: number;
|
|
31
47
|
}
|
|
32
48
|
export interface RelatedPullRequestGroup {
|
|
33
49
|
pullRequests: RelatedPullRequest[];
|
|
@@ -36,7 +52,35 @@ export interface RelatedPullRequestGroup {
|
|
|
36
52
|
provider: string;
|
|
37
53
|
key: string;
|
|
38
54
|
};
|
|
55
|
+
/**
|
|
56
|
+
* The top-level run of every run tree the walk visited (the original
|
|
57
|
+
* request and each follow-up tree that continued one of its pull
|
|
58
|
+
* requests), with its status; absent when none was read. `codingRun` is
|
|
59
|
+
* true when the root is itself a coding run (a top-level follow-up such
|
|
60
|
+
* as a review fix round or an @mention picked up by a coding link), not a
|
|
61
|
+
* native lead agent's run.
|
|
62
|
+
*/
|
|
63
|
+
roots?: Array<{
|
|
64
|
+
id: string;
|
|
65
|
+
status: RunStatus;
|
|
66
|
+
codingRun: boolean;
|
|
67
|
+
}>;
|
|
39
68
|
}
|
|
69
|
+
/**
|
|
70
|
+
* The top-level run of the tree a pull request's marker run belongs to,
|
|
71
|
+
* whatever its status: the marker's coding run (a continuation resolves to
|
|
72
|
+
* its root coding run first), then up its parents. Null when the marker
|
|
73
|
+
* names no coding run, or one recorded for a different repository than
|
|
74
|
+
* `repository` (compared case-insensitively; omit it to skip the check).
|
|
75
|
+
* `isOpener` is true when that root is the opening coding run itself (the
|
|
76
|
+
* pull request was not delegated). The single place this walk and its
|
|
77
|
+
* repository guard live (requestLeadRunId, the merge order check).
|
|
78
|
+
*/
|
|
79
|
+
export declare function runTreeRoot(db: Pick<PrismaClient, "$queryRaw">, markerRunId: string, repository?: string): Promise<{
|
|
80
|
+
id: string;
|
|
81
|
+
status: RunStatus;
|
|
82
|
+
isOpener: boolean;
|
|
83
|
+
} | null>;
|
|
40
84
|
export declare function collectRelatedPullRequests(db: RelatedPullRequestsDb, runId: string,
|
|
41
85
|
/**
|
|
42
86
|
* Scopes the issue join (and any tree row explicitly tagged for a
|
|
@@ -3,9 +3,18 @@
|
|
|
3
3
|
* request opened by a coding run in the same top-level run tree (a lead that
|
|
4
4
|
* fanned out to several repositories), plus any recorded for the same tracker
|
|
5
5
|
* issue (IssuePullRequest). Control-plane rows only.
|
|
6
|
-
* Ordered by
|
|
7
|
-
*
|
|
8
|
-
*
|
|
6
|
+
* Ordered by the delegating agent's declared merge order when any entry has
|
|
7
|
+
* one (CodingRun.mergeOrder; nulls last), then by when each pull request's
|
|
8
|
+
* coding run was dispatched: the lead's delegation order (for a lead with
|
|
9
|
+
* parallelDelegations, the order its concurrent delegations were admitted,
|
|
10
|
+
* ties broken by repository#number). A pull request's mergeOrder is the
|
|
11
|
+
* newest non-null value set by its opener run or any later continuation
|
|
12
|
+
* that acted on it, so a lead that re-delegates with a different order
|
|
13
|
+
* after the fact is reflected here. When mergeOrder is set on any entry,
|
|
14
|
+
* every truncation cap below this collector (MAX_RELATED_GROUP here,
|
|
15
|
+
* MAX_SIBLING_HINTS on openSiblingsOf, MAX_RELATED_PULL_REQUESTS on
|
|
16
|
+
* renderRelatedSection) drops entries with no order first, since they sort
|
|
17
|
+
* last.
|
|
9
18
|
* A PR opened while siblings were still running lists only the PRs opened
|
|
10
19
|
* before it; updateRelatedPullRequests rewrites every open PR of the set
|
|
11
20
|
* with the full list when the lead run ends. Continuations join
|
|
@@ -36,17 +45,50 @@ async function treeCodingRuns(db, seeds) {
|
|
|
36
45
|
UNION
|
|
37
46
|
SELECT p."id", p."parentRunId" FROM "Run" p JOIN up u ON p."id" = u."parentRunId"
|
|
38
47
|
), down AS (
|
|
39
|
-
SELECT u."id" FROM up u WHERE u."parentRunId" IS NULL
|
|
48
|
+
SELECT u."id", u."id" AS "rootId" FROM up u WHERE u."parentRunId" IS NULL
|
|
40
49
|
UNION
|
|
41
|
-
SELECT c."id" FROM "Run" c JOIN down d ON c."parentRunId" = d."id"
|
|
50
|
+
SELECT c."id", d."rootId" FROM "Run" c JOIN down d ON c."parentRunId" = d."id"
|
|
42
51
|
)
|
|
43
|
-
SELECT cr."runId", cr."result", cr."rootCodingRunId", cr."issueProvider", cr."issueKey", r."startedAt"
|
|
52
|
+
SELECT cr."runId", cr."result", cr."rootCodingRunId", cr."issueProvider", cr."issueKey", cr."mergeOrder", r."startedAt",
|
|
53
|
+
d."rootId" AS "treeRootId", root."status"::text AS "treeRootStatus",
|
|
54
|
+
EXISTS (SELECT 1 FROM "CodingRun" rc WHERE rc."runId" = d."rootId") AS "treeRootIsCodingRun"
|
|
44
55
|
FROM "CodingRun" cr
|
|
45
56
|
JOIN down d ON cr."runId" = d."id"
|
|
46
57
|
JOIN "Run" r ON r."id" = cr."runId"
|
|
58
|
+
JOIN "Run" root ON root."id" = d."rootId"
|
|
47
59
|
ORDER BY r."startedAt" ASC, cr."runId" ASC
|
|
48
60
|
LIMIT ${MAX_TREE_CODING_RUNS}`;
|
|
49
61
|
}
|
|
62
|
+
/**
|
|
63
|
+
* The top-level run of the tree a pull request's marker run belongs to,
|
|
64
|
+
* whatever its status: the marker's coding run (a continuation resolves to
|
|
65
|
+
* its root coding run first), then up its parents. Null when the marker
|
|
66
|
+
* names no coding run, or one recorded for a different repository than
|
|
67
|
+
* `repository` (compared case-insensitively; omit it to skip the check).
|
|
68
|
+
* `isOpener` is true when that root is the opening coding run itself (the
|
|
69
|
+
* pull request was not delegated). The single place this walk and its
|
|
70
|
+
* repository guard live (requestLeadRunId, the merge order check).
|
|
71
|
+
*/
|
|
72
|
+
export async function runTreeRoot(db, markerRunId, repository) {
|
|
73
|
+
const repo = repository ?? null;
|
|
74
|
+
// UNION, not UNION ALL: a parentRunId cycle (never written, but not a constraint) can't recurse forever.
|
|
75
|
+
const rows = await db.$queryRaw `
|
|
76
|
+
WITH RECURSIVE opener AS (
|
|
77
|
+
SELECT COALESCE(cr."rootCodingRunId", cr."runId") AS "id"
|
|
78
|
+
FROM "CodingRun" cr
|
|
79
|
+
WHERE cr."runId" = ${markerRunId}
|
|
80
|
+
AND (${repo}::text IS NULL OR lower(cr."repository") = lower(${repo}::text))
|
|
81
|
+
), up AS (
|
|
82
|
+
SELECT r."id", r."parentRunId", r."status"::text AS "status" FROM "Run" r JOIN opener o ON r."id" = o."id"
|
|
83
|
+
UNION
|
|
84
|
+
SELECT p."id", p."parentRunId", p."status"::text FROM "Run" p JOIN up u ON p."id" = u."parentRunId"
|
|
85
|
+
)
|
|
86
|
+
SELECT u."id", u."status", u."id" IN (SELECT "id" FROM opener) AS "isOpener"
|
|
87
|
+
FROM up u
|
|
88
|
+
WHERE u."parentRunId" IS NULL
|
|
89
|
+
LIMIT 1`;
|
|
90
|
+
return rows[0] ?? null;
|
|
91
|
+
}
|
|
50
92
|
function safeRepository(value) {
|
|
51
93
|
try {
|
|
52
94
|
return normalizeGitHubRepository(value);
|
|
@@ -139,9 +181,40 @@ onlyIssueKey) {
|
|
|
139
181
|
number: pr.pullRequestNumber,
|
|
140
182
|
openedAt: new Date(r.startedAt),
|
|
141
183
|
openedByRunId: r.runId,
|
|
184
|
+
...(r.mergeOrder != null ? { mergeOrder: r.mergeOrder } : {}),
|
|
142
185
|
});
|
|
143
186
|
}
|
|
144
187
|
}
|
|
188
|
+
// A continuation dispatched with its own mergeOrder overrides the opener's
|
|
189
|
+
// (one that omits it stores the value it inherited), and it starts after
|
|
190
|
+
// the opener; the entry's mergeOrder is the newest non-null
|
|
191
|
+
// value among the opener row and every continuation row (same PR key,
|
|
192
|
+
// "pull_request_opened" or "pull_request_updated") found in the tree walk.
|
|
193
|
+
const newestMergeOrder = new Map();
|
|
194
|
+
for (const r of rows) {
|
|
195
|
+
if (r.mergeOrder == null)
|
|
196
|
+
continue;
|
|
197
|
+
const pr = pullRequestOutcome(r.result);
|
|
198
|
+
if (pr?.outcome !== "pull_request_opened" && pr?.outcome !== "pull_request_updated")
|
|
199
|
+
continue;
|
|
200
|
+
if (onlyIssueKey && r.issueKey && (r.issueProvider !== onlyIssueKey.provider || r.issueKey !== onlyIssueKey.key)) {
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
203
|
+
const repository = safeRepository(pr.repository);
|
|
204
|
+
if (!repository)
|
|
205
|
+
continue;
|
|
206
|
+
const key = keyOf(repository, pr.pullRequestNumber);
|
|
207
|
+
const startedAt = new Date(r.startedAt);
|
|
208
|
+
const current = newestMergeOrder.get(key);
|
|
209
|
+
if (!current || startedAt.getTime() >= current.startedAt.getTime()) {
|
|
210
|
+
newestMergeOrder.set(key, { startedAt, mergeOrder: r.mergeOrder });
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
for (const [key, entry] of byKey) {
|
|
214
|
+
const newest = newestMergeOrder.get(key);
|
|
215
|
+
if (newest)
|
|
216
|
+
entry.mergeOrder = newest.mergeOrder;
|
|
217
|
+
}
|
|
145
218
|
const issueFilter = onlyIssueKey
|
|
146
219
|
? new Map([[`${onlyIssueKey.provider}\0${onlyIssueKey.key}`, onlyIssueKey]])
|
|
147
220
|
: issues;
|
|
@@ -177,11 +250,23 @@ onlyIssueKey) {
|
|
|
177
250
|
});
|
|
178
251
|
}
|
|
179
252
|
}
|
|
253
|
+
const order = (e) => e.mergeOrder ?? Number.POSITIVE_INFINITY;
|
|
180
254
|
const pullRequests = [...byKey.values()]
|
|
181
|
-
.sort((a, b) => a
|
|
255
|
+
.sort((a, b) => order(a) - order(b) ||
|
|
256
|
+
a.openedAt.getTime() - b.openedAt.getTime() ||
|
|
182
257
|
`${a.repository}#${a.number}`.localeCompare(`${b.repository}#${b.number}`))
|
|
183
258
|
.slice(0, MAX_RELATED_GROUP);
|
|
184
|
-
|
|
259
|
+
const roots = new Map();
|
|
260
|
+
for (const r of rows) {
|
|
261
|
+
if (r.treeRootId && r.treeRootStatus) {
|
|
262
|
+
roots.set(r.treeRootId, { status: r.treeRootStatus, codingRun: r.treeRootIsCodingRun === true });
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
return {
|
|
266
|
+
pullRequests,
|
|
267
|
+
...(issue ? { issue } : {}),
|
|
268
|
+
...(roots.size > 0 ? { roots: [...roots].map(([id, root]) => ({ id, ...root })) } : {}),
|
|
269
|
+
};
|
|
185
270
|
}
|
|
186
271
|
function issueFor(group, trackers) {
|
|
187
272
|
if (!group.issue)
|
|
@@ -219,7 +304,12 @@ export async function updateRelatedPullRequests(db, run, hosts, trackers) {
|
|
|
219
304
|
for (const pr of group.pullRequests) {
|
|
220
305
|
// A stored merged/closed state is final and such a PR is never edited: no host read.
|
|
221
306
|
if (pr.state === "merged" || pr.state === "closed") {
|
|
222
|
-
live.push({
|
|
307
|
+
live.push({
|
|
308
|
+
repository: pr.repository,
|
|
309
|
+
number: pr.number,
|
|
310
|
+
state: pr.state,
|
|
311
|
+
...(pr.mergeOrder !== undefined ? { mergeOrder: pr.mergeOrder } : {}),
|
|
312
|
+
});
|
|
223
313
|
continue;
|
|
224
314
|
}
|
|
225
315
|
try {
|
|
@@ -240,12 +330,23 @@ export async function updateRelatedPullRequests(db, run, hosts, trackers) {
|
|
|
240
330
|
if (opener && opener.repository.toLowerCase() === pr.repository)
|
|
241
331
|
markerRunId = origin.markerRunId;
|
|
242
332
|
}
|
|
243
|
-
live.push({
|
|
333
|
+
live.push({
|
|
334
|
+
repository: pr.repository,
|
|
335
|
+
number: pr.number,
|
|
336
|
+
state,
|
|
337
|
+
...(markerRunId ? { markerRunId } : {}),
|
|
338
|
+
...(pr.mergeOrder !== undefined ? { mergeOrder: pr.mergeOrder } : {}),
|
|
339
|
+
});
|
|
244
340
|
}
|
|
245
341
|
catch (err) {
|
|
246
342
|
log.warn({ err, runId: run.id, repository: pr.repository, number: pr.number }, "could not read a related pull request");
|
|
247
343
|
// The stored state (issue-recorded PRs) is better than none; never edited without a live read.
|
|
248
|
-
live.push({
|
|
344
|
+
live.push({
|
|
345
|
+
repository: pr.repository,
|
|
346
|
+
number: pr.number,
|
|
347
|
+
...(pr.state ? { state: pr.state } : {}),
|
|
348
|
+
...(pr.mergeOrder !== undefined ? { mergeOrder: pr.mergeOrder } : {}),
|
|
349
|
+
});
|
|
249
350
|
}
|
|
250
351
|
}
|
|
251
352
|
const issue = issueFor(group, trackers);
|
|
@@ -258,6 +359,7 @@ export async function updateRelatedPullRequests(db, run, hosts, trackers) {
|
|
|
258
359
|
number: pr.number,
|
|
259
360
|
...(pr.state ? { state: pr.state } : {}),
|
|
260
361
|
...(pr === target ? { self: true } : {}),
|
|
362
|
+
...(pr.mergeOrder !== undefined ? { mergeOrder: pr.mergeOrder } : {}),
|
|
261
363
|
})),
|
|
262
364
|
...(issue ? { issue } : {}),
|
|
263
365
|
});
|
|
@@ -23,6 +23,8 @@ export declare const AUTHORIZED_VIA: readonly AuthorizedVia[];
|
|
|
23
23
|
export type RepoAccessDenial = "owner_required" | "not_authorized" | "identity_not_linked" | "insufficient_permission" | "check_failed"
|
|
24
24
|
/** A `local:` repository that is not inside the current trusted roots (LOCAL_REPO_ROOTS). */
|
|
25
25
|
| "local_repo_not_allowed"
|
|
26
|
+
/** A GitHub repository whose owner is neither the App's owner nor in WARDBY_GITHUB_ALLOWED_ACCOUNTS. */
|
|
27
|
+
| "github_account_not_allowed"
|
|
26
28
|
/** In-flight use only: the host was unreachable or rate-limited, even after one retry. */
|
|
27
29
|
| "check_unavailable";
|
|
28
30
|
export type RepoAccessDecision = {
|
|
@@ -78,6 +80,8 @@ export interface RepoAccessGate {
|
|
|
78
80
|
required: HostPermission;
|
|
79
81
|
fresh?: boolean;
|
|
80
82
|
}): Promise<RepoAccessDecision>;
|
|
83
|
+
/** Is this repository's account served by the GitHub App? undefined = yes (or not a GitHub repository). */
|
|
84
|
+
checkAccount?(provider: string, repository: string): Promise<RepoAccessDecision | undefined>;
|
|
81
85
|
}
|
|
82
86
|
export interface RepoAccessGateOptions {
|
|
83
87
|
db: Pick<PrismaClient, "hostIdentity">;
|
|
@@ -92,9 +96,20 @@ export interface RepoAccessGateOptions {
|
|
|
92
96
|
/** Delay before the one retry of a transient error (default 1s). */
|
|
93
97
|
retryDelayMs?: number;
|
|
94
98
|
sleep?: (ms: number) => Promise<void>;
|
|
99
|
+
/**
|
|
100
|
+
* The served GitHub accounts (default: the App owner from hosts.github plus
|
|
101
|
+
* WARDBY_GITHUB_ALLOWED_ACCOUNTS). undefined = no GitHub App, nothing to guard.
|
|
102
|
+
* Throwing = fail closed.
|
|
103
|
+
*/
|
|
104
|
+
trustedAccounts?: () => Promise<ReadonlySet<string> | undefined>;
|
|
95
105
|
}
|
|
96
106
|
/** 5xx, rate limits (403/429), and transport failures: worth one retry for a run already under way. */
|
|
97
107
|
export declare function isTransientHostError(err: unknown): boolean;
|
|
108
|
+
/**
|
|
109
|
+
* Why the trusted GitHub accounts could not be read, as a fixed string: every
|
|
110
|
+
* branch returns a literal, so nothing from the error itself reaches a log.
|
|
111
|
+
*/
|
|
112
|
+
export declare function trustedAccountsFailure(err: unknown): string;
|
|
98
113
|
export declare const DEFAULT_REPO_ACCESS_TTL_MS: number;
|
|
99
114
|
export declare function createRepoAccessGate(options: RepoAccessGateOptions): RepoAccessGate;
|
|
100
115
|
/** Actionable, non-leaking text for a denial, for tool results and run errors. */
|
package/dist/core/repo-access.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { HOST_PERMISSION_RANK, ReviewHostError, } from "../providers/review-host/types.js";
|
|
2
2
|
import { LocalRepoError, isLocalRepository, loadLocalRepoRoots, resolveLocalRepository } from "../coding/local-repo.js";
|
|
3
|
+
import { githubAccountOf, isGitHubAccountAllowed, loadGitHubAllowedAccounts, trustedAccounts as trustedAccountSet, } from "../config/github-allowed-accounts.js";
|
|
3
4
|
import { logger } from "./logger.js";
|
|
4
5
|
const log = logger.child({ module: "repo-access" });
|
|
5
6
|
export const AUTHORIZED_VIA = ["host_permission", "admin", "grandfathered", "local_root"];
|
|
@@ -20,6 +21,24 @@ export function isTransientHostError(err) {
|
|
|
20
21
|
return false;
|
|
21
22
|
return err.message === "github_api_unavailable" || /^github_api_error:(5\d\d|429|403)(:|$)/.test(err.message);
|
|
22
23
|
}
|
|
24
|
+
/**
|
|
25
|
+
* Why the trusted GitHub accounts could not be read, as a fixed string: every
|
|
26
|
+
* branch returns a literal, so nothing from the error itself reaches a log.
|
|
27
|
+
*/
|
|
28
|
+
export function trustedAccountsFailure(err) {
|
|
29
|
+
const message = err instanceof Error ? err.message : "";
|
|
30
|
+
if (message === "github_allowed_accounts_invalid")
|
|
31
|
+
return "github_allowed_accounts_invalid";
|
|
32
|
+
if (message === "github_api_unavailable")
|
|
33
|
+
return "github_api_unavailable";
|
|
34
|
+
if (message === "github_api_invalid_response")
|
|
35
|
+
return "github_api_invalid_response";
|
|
36
|
+
if (message === "github_app_private_key_invalid")
|
|
37
|
+
return "github_app_private_key_invalid";
|
|
38
|
+
if (message.startsWith("github_api_error:"))
|
|
39
|
+
return "github_api_error";
|
|
40
|
+
return "unexpected_error";
|
|
41
|
+
}
|
|
23
42
|
const defaultLocalRoots = () => loadLocalRepoRoots(process.env).roots;
|
|
24
43
|
export const DEFAULT_REPO_ACCESS_TTL_MS = 5 * 60_000;
|
|
25
44
|
export function createRepoAccessGate(options) {
|
|
@@ -29,6 +48,41 @@ export function createRepoAccessGate(options) {
|
|
|
29
48
|
const retryDelayMs = options.retryDelayMs ?? 1000;
|
|
30
49
|
const sleep = options.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
31
50
|
const cache = new Map();
|
|
51
|
+
const readTrusted = options.trustedAccounts ??
|
|
52
|
+
(async () => {
|
|
53
|
+
const host = options.hosts.github;
|
|
54
|
+
if (!host?.appOwnerLogin)
|
|
55
|
+
return undefined;
|
|
56
|
+
let extra;
|
|
57
|
+
try {
|
|
58
|
+
extra = loadGitHubAllowedAccounts();
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
// A fixed code: the parse error quotes the raw value, which must not reach the log.
|
|
62
|
+
throw new Error("github_allowed_accounts_invalid");
|
|
63
|
+
}
|
|
64
|
+
return trustedAccountSet(await host.appOwnerLogin(), extra);
|
|
65
|
+
});
|
|
66
|
+
/** A server-wide boundary, checked before any stamp, owner, or host call. Local repositories are exempt. */
|
|
67
|
+
async function checkAccount(provider, repository) {
|
|
68
|
+
if (provider !== "github" || isLocalRepository(repository))
|
|
69
|
+
return undefined;
|
|
70
|
+
let trusted;
|
|
71
|
+
try {
|
|
72
|
+
trusted = await readTrusted();
|
|
73
|
+
}
|
|
74
|
+
catch (err) {
|
|
75
|
+
// Only a fixed code is logged, never the error: its message may carry
|
|
76
|
+
// WARDBY_GITHUB_ALLOWED_ACCOUNTS's value (CodeQL js/clear-text-logging).
|
|
77
|
+
log.warn({ code: trustedAccountsFailure(err) }, "could not read the GitHub App's trusted accounts; denying");
|
|
78
|
+
return { ok: false, reason: "check_failed" };
|
|
79
|
+
}
|
|
80
|
+
if (trusted === undefined)
|
|
81
|
+
return undefined;
|
|
82
|
+
return isGitHubAccountAllowed(trusted, githubAccountOf(repository))
|
|
83
|
+
? undefined
|
|
84
|
+
: { ok: false, reason: "github_account_not_allowed" };
|
|
85
|
+
}
|
|
32
86
|
/** The user's level, or why it could not be determined (host missing or failing). */
|
|
33
87
|
async function levelOf(provider, repository, user, fresh, retryTransient) {
|
|
34
88
|
const key = `${provider}\u0000${repository}\u0000${user.id}`;
|
|
@@ -69,6 +123,9 @@ export function createRepoAccessGate(options) {
|
|
|
69
123
|
return atLeast(level, required) ? { ok: true, level } : { ok: false, reason: "insufficient_permission", level };
|
|
70
124
|
}
|
|
71
125
|
async function authorizePrincipal(input) {
|
|
126
|
+
const denied = await checkAccount(input.provider, input.repository);
|
|
127
|
+
if (denied)
|
|
128
|
+
return denied;
|
|
72
129
|
let identity;
|
|
73
130
|
try {
|
|
74
131
|
identity = await options.db.hostIdentity.findUnique({
|
|
@@ -108,6 +165,9 @@ export function createRepoAccessGate(options) {
|
|
|
108
165
|
return { ok: false, reason: "local_repo_not_allowed" };
|
|
109
166
|
}
|
|
110
167
|
}
|
|
168
|
+
const denied = await checkAccount(input.provider, input.repository);
|
|
169
|
+
if (denied)
|
|
170
|
+
return denied;
|
|
111
171
|
if (!input.ownerId)
|
|
112
172
|
return { ok: false, reason: "owner_required" };
|
|
113
173
|
if (input.authorizedVia === "admin" || input.authorizedVia === "grandfathered")
|
|
@@ -124,11 +184,15 @@ export function createRepoAccessGate(options) {
|
|
|
124
184
|
},
|
|
125
185
|
authorizePrincipal,
|
|
126
186
|
async authorizeHostUser(input) {
|
|
187
|
+
const denied = await checkAccount(input.provider, input.repository);
|
|
188
|
+
if (denied)
|
|
189
|
+
return denied;
|
|
127
190
|
const answer = await levelOf(input.provider, input.repository, input.user, input.fresh === true, false);
|
|
128
191
|
if (!answer.ok)
|
|
129
192
|
return unavailable(answer);
|
|
130
193
|
return decide(answer.level, input.required);
|
|
131
194
|
},
|
|
195
|
+
checkAccount,
|
|
132
196
|
};
|
|
133
197
|
}
|
|
134
198
|
/** Actionable, non-leaking text for a denial, for tool results and run errors. */
|
|
@@ -144,6 +208,8 @@ export function describeDenial(decision, repository) {
|
|
|
144
208
|
return `The agent owner's GitHub account has ${decision.level ?? "no"} access to ${repository}, which is not enough.`;
|
|
145
209
|
case "local_repo_not_allowed":
|
|
146
210
|
return `local_repo_not_allowed: ${repository} is not a git repository inside the configured local roots (LOCAL_REPO_ROOTS).`;
|
|
211
|
+
case "github_account_not_allowed":
|
|
212
|
+
return `github_account_not_allowed: ${repository} belongs to a GitHub account this server does not serve (the App's owner and WARDBY_GITHUB_ALLOWED_ACCOUNTS).`;
|
|
147
213
|
case "check_failed":
|
|
148
214
|
return `Could not verify access to ${repository} right now; try again later.`;
|
|
149
215
|
case "check_unavailable":
|
|
@@ -6,12 +6,18 @@
|
|
|
6
6
|
* (startReviewFixAfterReview), once per run. What the agent does with the
|
|
7
7
|
* review is up to its own instructions; this module only decides whether a
|
|
8
8
|
* round may start. See docs/private/2026-10-03-review-fix-rounds-design.md.
|
|
9
|
+
*
|
|
10
|
+
* A round whose coding run ends with no change (the agent concluded the
|
|
11
|
+
* finding is wrong) gets one fresh review instead, which counts as a round
|
|
12
|
+
* (reReviewAfterNoChangeFix, from the coding run's terminal path): no push
|
|
13
|
+
* means no new review would otherwise replace the failed check.
|
|
9
14
|
*/
|
|
10
15
|
import type { PrismaClient } from "#prisma";
|
|
11
16
|
import type { Executor } from "../providers/executor/types.js";
|
|
12
17
|
import type { IssueTrackerRegistry } from "../providers/issue-tracker/types.js";
|
|
13
18
|
import type { ReviewHostProvider, ReviewHostRegistry } from "../providers/review-host/types.js";
|
|
14
19
|
import { type DispatchDb } from "./dispatch.js";
|
|
20
|
+
import { type ReviewStartDeps } from "./host-events.js";
|
|
15
21
|
import { type RepoAccessGate } from "./repo-access.js";
|
|
16
22
|
export declare const DEFAULT_MAX_FIX_ROUNDS = 2;
|
|
17
23
|
export type ReviewFixDb = DispatchDb & Pick<PrismaClient, "agentRepository" | "codingRun" | "runHostStatus" | "runHostCheck" | "agentIssueProject" | "issuePullRequest" | "workItem">;
|
|
@@ -40,6 +46,14 @@ export type ReviewFixResult = {
|
|
|
40
46
|
reason: ReviewFixSkip;
|
|
41
47
|
};
|
|
42
48
|
export declare function capBody(maxRounds: number): string;
|
|
49
|
+
/**
|
|
50
|
+
* Whether a run's stored task is an automatic fix round's for this pull
|
|
51
|
+
* request: the continuation hint, then exactly the fix header, then the fix
|
|
52
|
+
* request. Every one of those sections is written by wardby, and a mention's
|
|
53
|
+
* header (the first section after the hint a person could influence) names
|
|
54
|
+
* its author instead, so a comment cannot pass for a fix round.
|
|
55
|
+
*/
|
|
56
|
+
export declare function isReviewFixTask(taskOverride: string | null, repository: string, prNumber: number): boolean;
|
|
43
57
|
/**
|
|
44
58
|
* The fix round's task. The trusted part (what the runner puts in the system
|
|
45
59
|
* prompt) is only what wardby itself wrote: the continuation hint, the header
|
|
@@ -58,3 +72,26 @@ export declare function reviewFixTaskText(input: {
|
|
|
58
72
|
export declare function startReviewFixRound(req: ReviewFixRequest, deps: ReviewFixDeps): Promise<ReviewFixResult>;
|
|
59
73
|
/** The finalizer's entry point: a fix round for the review this run published, if it requested changes. Never throws. */
|
|
60
74
|
export declare function startReviewFixAfterReview(runId: string, deps: ReviewFixDeps): Promise<void>;
|
|
75
|
+
/** Most of a no-change fix run's summary (model output, already redacted by the protocol) put in the re-review's context. */
|
|
76
|
+
export declare const MAX_NO_CHANGE_SUMMARY_CHARS = 4000;
|
|
77
|
+
export type ReReviewDeps = ReviewStartDeps;
|
|
78
|
+
export type ReReviewSkip = "not_no_change" | "not_fix_round" | "no_host" | "no_request" | "not_current" | "opted_out" | "no_reviewer" | "not_authorized" | "claimed" | "capped" | "record_failed";
|
|
79
|
+
export type ReReviewResult = {
|
|
80
|
+
kind: "started";
|
|
81
|
+
runIds: string[];
|
|
82
|
+
round: number;
|
|
83
|
+
} | {
|
|
84
|
+
kind: "skipped";
|
|
85
|
+
reason: ReReviewSkip;
|
|
86
|
+
};
|
|
87
|
+
/** The re-review's extra context: a fixed, trusted pointer, and the fix run's summary as untrusted data. */
|
|
88
|
+
export declare function noChangeReviewContext(summary: string): {
|
|
89
|
+
task: string;
|
|
90
|
+
untrustedContext: string;
|
|
91
|
+
};
|
|
92
|
+
/**
|
|
93
|
+
* The coding-run terminal hook: a fix round's coding run that ended with no change starts one
|
|
94
|
+
* fresh review of the PR's unchanged head by the reviewer that requested changes, counted as a
|
|
95
|
+
* round. Does nothing for any other run. Never throws.
|
|
96
|
+
*/
|
|
97
|
+
export declare function reReviewAfterNoChangeFix(runId: string, deps: ReReviewDeps): Promise<void>;
|