@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
|
@@ -30,10 +30,10 @@ import { createHash } from "node:crypto";
|
|
|
30
30
|
import { isIP } from "node:net";
|
|
31
31
|
import { logger } from "../../core/logger.js";
|
|
32
32
|
import { DISABLED_POWER, wakeTimeoutMs } from "../../core/power.js";
|
|
33
|
-
import { MAX_CODING_ARTIFACT_BYTES, MAX_CODING_INPUT_BYTES, MAX_DEBUG_TRACE_LINE_BYTES,
|
|
33
|
+
import { MAX_CODING_ARTIFACT_BYTES, MAX_CODING_INPUT_BYTES, MAX_DEBUG_TRACE_LINE_BYTES, parseCodingWorkerOutputJson, } from "../../coding/protocol.js";
|
|
34
34
|
import { CODING_PROXY_DENY_PORT, CODING_PROXY_PORT } from "./docker-isolation.js";
|
|
35
35
|
import { parseWorkerDiagnosticLine } from "./docker.js";
|
|
36
|
-
import { KubernetesAlreadyExistsError, KubernetesConflictError } from "./kubernetes-api.js";
|
|
36
|
+
import { KubernetesAlreadyExistsError, KubernetesConflictError, KubernetesQuotaExceededError, } from "./kubernetes-api.js";
|
|
37
37
|
import { ENFORCEMENT_PROBE_DENY_REACHABLE, ENFORCEMENT_PROBE_PROVEN, ENFORCEMENT_PROBE_DENY_REFUSED, ENFORCEMENT_PROBE_PROXY_UNREACHABLE, KEEPER_CONTAINER, KEEPER_SEEDED_MARKER, KUBERNETES_ISOLATION_ERROR, STORAGE_ROOT, TOOL_RUNNER_CONTAINER, WORKER_CONTAINER, assertRunNetworkPolicyMatches, assertRunPodMatches, buildCapabilitySecret, buildRunNetworkPolicy, buildRunPod, enforcementStreakScript, kubernetesRunNames, kubernetesRunNamesForToken, runLabels, serviceContainerName, validateKubernetesSpec, } from "./kubernetes-isolation.js";
|
|
38
38
|
import { claudeToolSetup } from "./claude-tool-setup.js";
|
|
39
39
|
import { podQuotaUsage, quotaShortfall } from "./kubernetes-quota.js";
|
|
@@ -41,6 +41,7 @@ import { readProxyWitness } from "./kubernetes-witness.js";
|
|
|
41
41
|
import { safeExtract } from "./safe-extract.js";
|
|
42
42
|
import { reportServiceState, } from "./service-state.js";
|
|
43
43
|
import { serviceUnreadyError } from "../../coding/services/wording.js";
|
|
44
|
+
import { JobCapacityError, } from "./types.js";
|
|
44
45
|
import { replaceDirectoryFromStaging } from "./workspace-swap.js";
|
|
45
46
|
import { collectExclusions, normalizeCollectExclusions, tarExcludeArgs, } from "../../coding/collect-exclude.js";
|
|
46
47
|
const BACKEND = "kubernetes";
|
|
@@ -184,10 +185,22 @@ const TOKEN = /^[a-f0-9]{20}$/;
|
|
|
184
185
|
const WORKSPACE_STORAGE = `${STORAGE_ROOT}/workspace`;
|
|
185
186
|
const INPUT_STORAGE = `${STORAGE_ROOT}/input`;
|
|
186
187
|
const RESULT_PATH = `${STORAGE_ROOT}/output/result.json`;
|
|
188
|
+
const TRANSCRIPT_PATH = `${STORAGE_ROOT}/output/session.jsonl`;
|
|
187
189
|
/** Stands in for the proxy ClusterIP when building a pod only to count its resources. */
|
|
188
190
|
const QUOTA_PROBE_PROXY_IP = "10.0.0.1";
|
|
191
|
+
/** A Kubernetes object name (RFC 1123 subdomain). */
|
|
192
|
+
const DNS_1123_NAME = /^[a-z0-9]([-a-z0-9.]{0,251}[a-z0-9])?$/;
|
|
189
193
|
const kubernetesLog = logger.child({ module: "kubernetes-jobs" });
|
|
190
|
-
const PHASES = new Set([
|
|
194
|
+
const PHASES = new Set([
|
|
195
|
+
"provisioning",
|
|
196
|
+
"requeued",
|
|
197
|
+
"active",
|
|
198
|
+
"succeeded",
|
|
199
|
+
"failed",
|
|
200
|
+
"stopped",
|
|
201
|
+
"lost",
|
|
202
|
+
"removed",
|
|
203
|
+
]);
|
|
191
204
|
const TERMINAL_PHASES = new Set(["succeeded", "failed", "stopped", "lost", "removed"]);
|
|
192
205
|
// ---------------------------------------------------------------------------
|
|
193
206
|
// Pure helpers: the lifecycle rules.
|
|
@@ -206,6 +219,8 @@ function statusFor(record) {
|
|
|
206
219
|
switch (record.phase) {
|
|
207
220
|
case "provisioning":
|
|
208
221
|
return { state: "pending" };
|
|
222
|
+
case "requeued":
|
|
223
|
+
return { state: "pending", capacityRefused: true };
|
|
209
224
|
case "active":
|
|
210
225
|
return { state: "running" };
|
|
211
226
|
case "succeeded":
|
|
@@ -508,8 +523,9 @@ export class KubernetesJobLauncher {
|
|
|
508
523
|
const handle = { backend: BACKEND, id: `${this.namespace}/${names.token}` };
|
|
509
524
|
const specHash = stableSpecHash(spec);
|
|
510
525
|
const existing = await this.readRecord(names);
|
|
511
|
-
if (existing)
|
|
526
|
+
if (existing && !this.isRequeued(existing, specHash)) {
|
|
512
527
|
return this.existingLaunch(existing.record, specHash, handle);
|
|
528
|
+
}
|
|
513
529
|
const createdAt = this.now();
|
|
514
530
|
const record = {
|
|
515
531
|
schemaVersion: RECORD_SCHEMA_VERSION,
|
|
@@ -521,22 +537,54 @@ export class KubernetesJobLauncher {
|
|
|
521
537
|
phase: "provisioning",
|
|
522
538
|
collectExclude: spec.collectExclude ?? collectExclusions([]),
|
|
523
539
|
};
|
|
524
|
-
|
|
525
|
-
//
|
|
526
|
-
|
|
540
|
+
if (existing) {
|
|
541
|
+
// Replaces the requeued record; a conflict means another replica got there first.
|
|
542
|
+
try {
|
|
543
|
+
await this.api.replaceConfigMap(this.namespace, names.record, this.recordConfigMap(names, record, existing.resourceVersion));
|
|
544
|
+
}
|
|
545
|
+
catch (error) {
|
|
546
|
+
if (!(error instanceof KubernetesConflictError))
|
|
547
|
+
throw error;
|
|
548
|
+
const raced = await this.readRecord(names);
|
|
549
|
+
if (!raced)
|
|
550
|
+
throw error;
|
|
551
|
+
if (!this.isRequeued(raced, specHash))
|
|
552
|
+
return this.existingLaunch(raced.record, specHash, handle);
|
|
553
|
+
// Requeued again by the other replica (its attempt was refused too): start over from that record.
|
|
554
|
+
return this.launch(spec);
|
|
555
|
+
}
|
|
527
556
|
}
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
557
|
+
else {
|
|
558
|
+
try {
|
|
559
|
+
// The record is the idempotency fence before any other cluster side effect. A quota refusing
|
|
560
|
+
// this create itself (a count/configmaps quota) is not a capacity refusal: it fails the run.
|
|
561
|
+
await this.api.createConfigMap(this.namespace, this.recordConfigMap(names, record));
|
|
562
|
+
}
|
|
563
|
+
catch (error) {
|
|
564
|
+
if (!(error instanceof KubernetesAlreadyExistsError))
|
|
565
|
+
throw error;
|
|
566
|
+
const raced = await this.readRecord(names);
|
|
567
|
+
if (!raced)
|
|
568
|
+
throw error;
|
|
569
|
+
if (!this.isRequeued(raced, specHash))
|
|
570
|
+
return this.existingLaunch(raced.record, specHash, handle);
|
|
571
|
+
// Another replica requeued it between our read and create: start over from the requeued record.
|
|
572
|
+
return this.launch(spec);
|
|
573
|
+
}
|
|
535
574
|
}
|
|
536
575
|
try {
|
|
537
576
|
await this.provision(spec, names, record);
|
|
538
577
|
}
|
|
539
578
|
catch (error) {
|
|
579
|
+
if (error instanceof KubernetesQuotaExceededError) {
|
|
580
|
+
// Capacity, not failure: leave a record a later launch() provisions afresh.
|
|
581
|
+
await this.updateRecord(names, () => ({ phase: "requeued" })).catch(() => undefined);
|
|
582
|
+
await this.cleanupRun(names, 0);
|
|
583
|
+
this.hintQuotaUnconfigured(error.quotaName);
|
|
584
|
+
throw new JobCapacityError("kubernetes_quota_exceeded: the namespace ResourceQuota has no room for this run", {
|
|
585
|
+
cause: error,
|
|
586
|
+
});
|
|
587
|
+
}
|
|
540
588
|
// Record the failure before deleting the pod, so a replica that observes "no pod" in between
|
|
541
589
|
// can't record `lost` first and have this more specific outcome dropped.
|
|
542
590
|
await this.updateRecord(names, () => ({
|
|
@@ -758,6 +806,22 @@ export class KubernetesJobLauncher {
|
|
|
758
806
|
await this.sleep(ENFORCEMENT_POLL_MS);
|
|
759
807
|
}
|
|
760
808
|
}
|
|
809
|
+
/** True when `found` is a requeued record for this spec, which launch() replaces with a fresh one. */
|
|
810
|
+
isRequeued(found, specHash) {
|
|
811
|
+
if (found.record.phase !== "requeued")
|
|
812
|
+
return false;
|
|
813
|
+
if (found.record.specHash !== specHash)
|
|
814
|
+
throw new Error("job_spec_conflict");
|
|
815
|
+
return true;
|
|
816
|
+
}
|
|
817
|
+
/** Tells the operator once that a quota refused a pod while no quota is configured for the pre-launch check. */
|
|
818
|
+
hintQuotaUnconfigured(refusedQuota) {
|
|
819
|
+
if (this.options.config.resourceQuota)
|
|
820
|
+
return;
|
|
821
|
+
const name = refusedQuota !== undefined && DNS_1123_NAME.test(refusedQuota) ? refusedQuota : undefined;
|
|
822
|
+
this.warnOnce(`kubernetes_resource_quota_unconfigured: a pod was refused by ${name ? `ResourceQuota ${name}` : "a ResourceQuota"}; ` +
|
|
823
|
+
`set KUBERNETES_RESOURCE_QUOTA=${name ?? "<name>"} and grant the launcher get on that ResourceQuota so runs wait before they start`);
|
|
824
|
+
}
|
|
761
825
|
existingLaunch(record, specHash, handle) {
|
|
762
826
|
if (record.specHash !== specHash)
|
|
763
827
|
throw new Error("job_spec_conflict");
|
|
@@ -957,7 +1021,8 @@ export class KubernetesJobLauncher {
|
|
|
957
1021
|
// Observation and collection
|
|
958
1022
|
/** Observes a non-terminal run against its pod and persists any transition; returns the stored record. */
|
|
959
1023
|
async refresh(names, record) {
|
|
960
|
-
|
|
1024
|
+
// A requeued run has no pod by design; observing it would record it lost.
|
|
1025
|
+
if (isTerminal(record.phase) || record.phase === "requeued")
|
|
961
1026
|
return record;
|
|
962
1027
|
const pod = await this.api.readPod(this.namespace, names.pod);
|
|
963
1028
|
const observed = observePod(pod, record, this.now(), this.readyTimeoutMs);
|
|
@@ -985,6 +1050,30 @@ export class KubernetesJobLauncher {
|
|
|
985
1050
|
}
|
|
986
1051
|
return undefined;
|
|
987
1052
|
}
|
|
1053
|
+
async readTranscript(handle, maxBytes) {
|
|
1054
|
+
const names = this.namesFor(handle);
|
|
1055
|
+
const found = names && (await this.readRecord(names));
|
|
1056
|
+
if (!names || !found)
|
|
1057
|
+
return undefined;
|
|
1058
|
+
const collector = new BoundedCollector(maxBytes);
|
|
1059
|
+
let exitCode;
|
|
1060
|
+
try {
|
|
1061
|
+
exitCode = await this.api.exec(this.namespace, names.pod, KEEPER_CONTAINER, ["head", "-c", String(maxBytes + 1), TRANSCRIPT_PATH], { stdout: collector, timeoutMs: SHORT_EXEC_TIMEOUT_MS });
|
|
1062
|
+
if (!collector.writableEnded)
|
|
1063
|
+
collector.end();
|
|
1064
|
+
await finished(collector);
|
|
1065
|
+
}
|
|
1066
|
+
catch {
|
|
1067
|
+
// The pod is gone (a stopped run) or unreachable: there is nothing to collect.
|
|
1068
|
+
collector.destroy();
|
|
1069
|
+
return undefined;
|
|
1070
|
+
}
|
|
1071
|
+
if (exitCode !== 0)
|
|
1072
|
+
return undefined;
|
|
1073
|
+
if (collector.exceeded)
|
|
1074
|
+
throw new Error("transcript_too_large");
|
|
1075
|
+
return collector.text();
|
|
1076
|
+
}
|
|
988
1077
|
async readResultArtifact(names, runId) {
|
|
989
1078
|
const collector = new BoundedCollector(MAX_CODING_ARTIFACT_BYTES);
|
|
990
1079
|
let raw;
|
|
@@ -1005,7 +1094,7 @@ export class KubernetesJobLauncher {
|
|
|
1005
1094
|
}
|
|
1006
1095
|
let output;
|
|
1007
1096
|
try {
|
|
1008
|
-
output =
|
|
1097
|
+
output = parseCodingWorkerOutputJson(raw);
|
|
1009
1098
|
}
|
|
1010
1099
|
catch (error) {
|
|
1011
1100
|
throw errorWithCode("kubernetes_result_artifact_invalid", error);
|
|
@@ -21,6 +21,14 @@ export interface JobSpec {
|
|
|
21
21
|
/** Required only for Claude's credential-free repository tool container. */
|
|
22
22
|
toolImage?: string;
|
|
23
23
|
inputArtifact: string;
|
|
24
|
+
/** Set only when the run's dependency cache is on (CodingRun.dependencyCache); absent otherwise, never false. */
|
|
25
|
+
dependencyCache?: true;
|
|
26
|
+
/**
|
|
27
|
+
* Claude Code only, set only when the run keeps its session (CodingRun.sessionResume); absent otherwise,
|
|
28
|
+
* never false. The worker's SDK config dir, where the session file grows, gets its own size-limited
|
|
29
|
+
* scratch so a long session never fills the worker's shared /tmp.
|
|
30
|
+
*/
|
|
31
|
+
sessionResume?: true;
|
|
24
32
|
timeoutSec: number;
|
|
25
33
|
limits: JobResourceLimits;
|
|
26
34
|
labels: Record<string, string>;
|
|
@@ -42,6 +50,12 @@ export interface JobHandle {
|
|
|
42
50
|
}
|
|
43
51
|
export type JobStatus = {
|
|
44
52
|
state: "pending" | "running";
|
|
53
|
+
/**
|
|
54
|
+
* Only ever on `pending`: the backend refused this job's launch for capacity (JobCapacityError)
|
|
55
|
+
* and nothing of it is running, nor will it start on its own. A caller that finds its job so
|
|
56
|
+
* may return the run to its queue and later `launch` the same spec again.
|
|
57
|
+
*/
|
|
58
|
+
capacityRefused?: true;
|
|
45
59
|
} | {
|
|
46
60
|
state: "succeeded";
|
|
47
61
|
} | {
|
|
@@ -61,11 +75,24 @@ export interface JobResult {
|
|
|
61
75
|
/** Output-schema failure locations (`path:code`) that passed SAFE_CODING_OUTPUT_ISSUE; never values. */
|
|
62
76
|
diagnosticIssues?: string[];
|
|
63
77
|
}
|
|
78
|
+
/**
|
|
79
|
+
* The backend has no room for this job right now (Kubernetes: the namespace ResourceQuota). No
|
|
80
|
+
* workload of the job is left running; the backend may keep a record of the refusal (Kubernetes
|
|
81
|
+
* does: its run record, which `status` then reports as `pending` with `capacityRefused`). The
|
|
82
|
+
* caller may launch the same spec again later.
|
|
83
|
+
*/
|
|
84
|
+
export declare class JobCapacityError extends Error {
|
|
85
|
+
readonly code = "coding_capacity_exceeded";
|
|
86
|
+
constructor(message: string, options?: ErrorOptions);
|
|
87
|
+
}
|
|
64
88
|
/**
|
|
65
89
|
* Lifecycle invariants implemented by every launcher:
|
|
66
90
|
*
|
|
67
91
|
* - `launch` is idempotent by runId for an identical spec and rejects a
|
|
68
92
|
* different spec for the same runId.
|
|
93
|
+
* - `launch` may reject with JobCapacityError when the backend has no room for
|
|
94
|
+
* the job; nothing of it is then left running, and a later `launch` of the
|
|
95
|
+
* same spec provisions it afresh.
|
|
69
96
|
* - pending may become running or terminal; running may become terminal;
|
|
70
97
|
* terminal states never change.
|
|
71
98
|
* - `collect` is repeatable for a terminal job until `remove` succeeds.
|
|
@@ -100,6 +127,12 @@ export interface JobLauncher {
|
|
|
100
127
|
launch: (spec: JobSpec) => Promise<JobHandle>;
|
|
101
128
|
status: (handle: JobHandle) => Promise<JobStatus>;
|
|
102
129
|
collect: (handle: JobHandle) => Promise<JobResult>;
|
|
130
|
+
/**
|
|
131
|
+
* The run's session transcript file (the worker's session.jsonl), at most `maxBytes`; undefined
|
|
132
|
+
* when there is none or the job's storage is gone (a stopped job). Throws `transcript_too_large`
|
|
133
|
+
* when larger. Never cached in the job record: it is not redacted yet. Call before `remove`.
|
|
134
|
+
*/
|
|
135
|
+
readTranscript?: (handle: JobHandle, maxBytes: number) => Promise<string | undefined>;
|
|
103
136
|
stop: (handle: JobHandle, reason?: string) => Promise<void>;
|
|
104
137
|
remove: (handle: JobHandle) => Promise<void>;
|
|
105
138
|
}
|
|
@@ -3,4 +3,16 @@
|
|
|
3
3
|
* values are carried in a versioned input artifact, never expanded into an
|
|
4
4
|
* arbitrary environment map or shell command.
|
|
5
5
|
*/
|
|
6
|
-
|
|
6
|
+
/**
|
|
7
|
+
* The backend has no room for this job right now (Kubernetes: the namespace ResourceQuota). No
|
|
8
|
+
* workload of the job is left running; the backend may keep a record of the refusal (Kubernetes
|
|
9
|
+
* does: its run record, which `status` then reports as `pending` with `capacityRefused`). The
|
|
10
|
+
* caller may launch the same spec again later.
|
|
11
|
+
*/
|
|
12
|
+
export class JobCapacityError extends Error {
|
|
13
|
+
code = "coding_capacity_exceeded";
|
|
14
|
+
constructor(message, options) {
|
|
15
|
+
super(message, options);
|
|
16
|
+
this.name = "JobCapacityError";
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import Anthropic from "@anthropic-ai/sdk";
|
|
8
8
|
import { ClaudeLlmProvider } from "./claude-provider.js";
|
|
9
|
+
import { simulatorModelClientOptions } from "../../config/test-simulators.js";
|
|
9
10
|
export function anthropicCredentialsPresent(env = process.env) {
|
|
10
11
|
return Boolean(env.ANTHROPIC_API_KEY);
|
|
11
12
|
}
|
|
@@ -14,6 +15,9 @@ export class AnthropicLlmProvider extends ClaudeLlmProvider {
|
|
|
14
15
|
if (!client && !apiKey) {
|
|
15
16
|
throw new Error("ANTHROPIC_API_KEY is not set — required by the Anthropic LlmProvider adapter.");
|
|
16
17
|
}
|
|
17
|
-
|
|
18
|
+
// WARDBY_TEST_SIMULATORS (test only): the scripted model, with a placeholder key the
|
|
19
|
+
// credential-free fetch drops; the real key never goes there.
|
|
20
|
+
const simulator = client ? null : simulatorModelClientOptions("anthropic");
|
|
21
|
+
super(client ?? new Anthropic(simulator ?? { apiKey }), lookup);
|
|
18
22
|
}
|
|
19
23
|
}
|
|
@@ -1,15 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Bedrock-Claude adapter for the LlmProvider seam. `@anthropic-ai/bedrock-sdk`
|
|
3
|
-
* is imported only here; all protocol/pricing-dispatch logic lives in the
|
|
4
|
-
* shared ClaudeLlmProvider base class (claude-provider.ts) — AnthropicBedrock
|
|
5
|
-
* exposes the identical .messages.stream() surface as @anthropic-ai/sdk, so
|
|
6
|
-
* this file's only job is constructing the right client.
|
|
7
|
-
*
|
|
8
|
-
* Named BedrockClaudeLlmProvider, not BedrockLlmProvider: Bedrock hosts
|
|
9
|
-
* multiple model vendors, and this adapter only ever speaks the
|
|
10
|
-
* Anthropic-native protocol for Claude models. A future non-Claude Bedrock
|
|
11
|
-
* adapter gets its own name, not a rename of this one.
|
|
12
|
-
*/
|
|
13
1
|
import { AnthropicBedrock } from "@anthropic-ai/bedrock-sdk";
|
|
14
2
|
import { ClaudeLlmProvider } from "./claude-provider.js";
|
|
15
3
|
import type { CatalogLookup } from "./catalog-lookup.js";
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
* Anthropic-native protocol for Claude models. A future non-Claude Bedrock
|
|
11
11
|
* adapter gets its own name, not a rename of this one.
|
|
12
12
|
*/
|
|
13
|
+
import { refuseBedrockUnderTestSimulators } from "../../config/test-simulators.js";
|
|
13
14
|
import { AnthropicBedrock } from "@anthropic-ai/bedrock-sdk";
|
|
14
15
|
import { ClaudeLlmProvider } from "./claude-provider.js";
|
|
15
16
|
export function bedrockCredentialsPresent(env = process.env) {
|
|
@@ -23,6 +24,9 @@ export class BedrockClaudeLlmProvider extends ClaudeLlmProvider {
|
|
|
23
24
|
if (!client && !region) {
|
|
24
25
|
throw new Error("BEDROCK_REGION (or AWS_REGION) is required by the Bedrock Claude LlmProvider adapter.");
|
|
25
26
|
}
|
|
27
|
+
// WARDBY_TEST_SIMULATORS (test only) refuses Bedrock: AWS-signed calls cannot go to the simulator.
|
|
28
|
+
if (!client)
|
|
29
|
+
refuseBedrockUnderTestSimulators();
|
|
26
30
|
super(client ?? new AnthropicBedrock({ awsRegion: region }), lookup);
|
|
27
31
|
}
|
|
28
32
|
}
|
|
@@ -106,5 +106,7 @@ export type ClaudeStreamEvent = {
|
|
|
106
106
|
} | {
|
|
107
107
|
type: "message_stop";
|
|
108
108
|
};
|
|
109
|
-
export declare function mapClaudeStream(events: AsyncIterable<ClaudeStreamEvent>, priceUsd: (usage: LlmUsage) => number
|
|
109
|
+
export declare function mapClaudeStream(events: AsyncIterable<ClaudeStreamEvent>, priceUsd: (usage: LlmUsage) => number,
|
|
110
|
+
/** Called at message_start: the response has started, and output may be billed from here on. */
|
|
111
|
+
onResponseStarted?: () => void): AsyncIterable<LlmStreamEvent>;
|
|
110
112
|
export {};
|
|
@@ -98,7 +98,9 @@ export function withCacheBreakpoints(req) {
|
|
|
98
98
|
});
|
|
99
99
|
return { ...req, ...(system ? { system } : {}), messages };
|
|
100
100
|
}
|
|
101
|
-
export async function* mapClaudeStream(events, priceUsd
|
|
101
|
+
export async function* mapClaudeStream(events, priceUsd,
|
|
102
|
+
/** Called at message_start: the response has started, and output may be billed from here on. */
|
|
103
|
+
onResponseStarted) {
|
|
102
104
|
const toolBuffers = new Map();
|
|
103
105
|
let start = {};
|
|
104
106
|
let outputTokens = 0;
|
|
@@ -106,6 +108,7 @@ export async function* mapClaudeStream(events, priceUsd) {
|
|
|
106
108
|
for await (const ev of events) {
|
|
107
109
|
switch (ev.type) {
|
|
108
110
|
case "message_start":
|
|
111
|
+
onResponseStarted?.();
|
|
109
112
|
start = ev.message.usage ?? {};
|
|
110
113
|
outputTokens = ev.message.usage?.output_tokens ?? 0;
|
|
111
114
|
break;
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* stays exactly what each concrete adapter (anthropic.ts, bedrock.ts) has in
|
|
6
6
|
* common, and nothing more.
|
|
7
7
|
*/
|
|
8
|
-
import type { LlmMessage, LlmRequest, LlmStreamEvent, LlmToolDef } from "./types.js";
|
|
8
|
+
import type { LlmMessage, LlmRequest, LlmStreamEvent, LlmStreamHooks, LlmToolDef } from "./types.js";
|
|
9
9
|
import { type CatalogLookup } from "./catalog-lookup.js";
|
|
10
10
|
import type { CatalogEntry } from "./catalog-types.js";
|
|
11
11
|
import type { CatalogLlmAdapter } from "./routing.js";
|
|
@@ -27,7 +27,7 @@ export declare class ClaudeLlmProvider implements CatalogLlmAdapter {
|
|
|
27
27
|
private readonly lookup;
|
|
28
28
|
constructor(client: ClaudeMessagesClient, lookup?: CatalogLookup);
|
|
29
29
|
withEntry(entry: CatalogEntry): ClaudeLlmProvider;
|
|
30
|
-
stream(req: LlmRequest, signal?: AbortSignal): AsyncIterable<LlmStreamEvent>;
|
|
30
|
+
stream(req: LlmRequest, signal?: AbortSignal, hooks?: LlmStreamHooks): AsyncIterable<LlmStreamEvent>;
|
|
31
31
|
countTokens(model: string, messages: LlmMessage[], tools?: LlmToolDef[]): Promise<number>;
|
|
32
32
|
priceUsd(model: string, usage: {
|
|
33
33
|
inputTokens: number;
|
|
@@ -21,14 +21,14 @@ export class ClaudeLlmProvider {
|
|
|
21
21
|
withEntry(entry) {
|
|
22
22
|
return new ClaudeLlmProvider(this.client, pinnedLookup(entry));
|
|
23
23
|
}
|
|
24
|
-
async *stream(req, signal) {
|
|
24
|
+
async *stream(req, signal, hooks) {
|
|
25
25
|
// Defence in depth behind config-time validation: a level the model
|
|
26
26
|
// doesn't accept would fail the whole call, so it is dropped here.
|
|
27
27
|
const { effort, ...rest } = req;
|
|
28
28
|
const accepted = effort && this.lookup(req.model).efforts.includes(effort);
|
|
29
29
|
const claudeReq = withCacheBreakpoints(toClaudeRequest(accepted ? { ...rest, effort } : rest, DEFAULT_MAX_TOKENS));
|
|
30
30
|
const raw = this.client.messages.stream({ model: req.model, ...claudeReq }, { signal });
|
|
31
|
-
yield* mapClaudeStream(raw, (usage) => this.priceUsd(req.model, usage));
|
|
31
|
+
yield* mapClaudeStream(raw, (usage) => this.priceUsd(req.model, usage), hooks?.onResponseStarted);
|
|
32
32
|
}
|
|
33
33
|
async countTokens(model, messages, tools) {
|
|
34
34
|
this.lookup(model); // fail-closed on a model the catalog does not serve
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
* apply.
|
|
28
28
|
*/
|
|
29
29
|
import OpenAI from "openai";
|
|
30
|
-
import type { LlmMessage, LlmRequest, LlmStreamEvent, LlmToolDef } from "./types.js";
|
|
30
|
+
import type { LlmMessage, LlmRequest, LlmStreamEvent, LlmStreamHooks, LlmToolDef } from "./types.js";
|
|
31
31
|
import { type CatalogLookup } from "./catalog-lookup.js";
|
|
32
32
|
import type { CatalogEntry } from "./catalog-types.js";
|
|
33
33
|
import type { CatalogLlmAdapter } from "./routing.js";
|
|
@@ -44,7 +44,7 @@ export declare class OpenAiLlmProvider implements CatalogLlmAdapter {
|
|
|
44
44
|
*/
|
|
45
45
|
constructor(apiKey?: string, client?: OpenAI, lookup?: CatalogLookup);
|
|
46
46
|
withEntry(entry: CatalogEntry): OpenAiLlmProvider;
|
|
47
|
-
stream(req: LlmRequest, signal?: AbortSignal): AsyncIterable<LlmStreamEvent>;
|
|
47
|
+
stream(req: LlmRequest, signal?: AbortSignal, hooks?: LlmStreamHooks): AsyncIterable<LlmStreamEvent>;
|
|
48
48
|
/**
|
|
49
49
|
* OpenAI reports both `cached_tokens` and `cache_write_tokens` as subsets
|
|
50
50
|
* of `input_tokens`. wardby's LlmUsage convention (claude-messages.ts,
|
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
* apply.
|
|
28
28
|
*/
|
|
29
29
|
import OpenAI from "openai";
|
|
30
|
+
import { simulatorModelClientOptions } from "../../config/test-simulators.js";
|
|
30
31
|
import { encode as encodeCl100kBase } from "gpt-tokenizer/encoding/cl100k_base";
|
|
31
32
|
import { encode as encodeO200kBase } from "gpt-tokenizer/encoding/o200k_base";
|
|
32
33
|
import { computeCost } from "./pricing-core.js";
|
|
@@ -133,12 +134,14 @@ export class OpenAiLlmProvider {
|
|
|
133
134
|
if (!apiKey) {
|
|
134
135
|
throw new Error("OPENAI_API_KEY is not set — required by the OpenAI LlmProvider adapter.");
|
|
135
136
|
}
|
|
136
|
-
|
|
137
|
+
// WARDBY_TEST_SIMULATORS (test only): the scripted model, with a placeholder key the
|
|
138
|
+
// credential-free fetch drops; the real key never goes there.
|
|
139
|
+
this.client = new OpenAI(simulatorModelClientOptions("openai") ?? { apiKey });
|
|
137
140
|
}
|
|
138
141
|
withEntry(entry) {
|
|
139
142
|
return new OpenAiLlmProvider("", this.client, pinnedLookup(entry));
|
|
140
143
|
}
|
|
141
|
-
async *stream(req, signal) {
|
|
144
|
+
async *stream(req, signal, hooks) {
|
|
142
145
|
const entry = this.lookup(req.model);
|
|
143
146
|
// Effort is sent only at a level the catalog says the model accepts and
|
|
144
147
|
// dropped otherwise (the LlmRequest.effort contract). Temperature only
|
|
@@ -157,6 +160,7 @@ export class OpenAiLlmProvider {
|
|
|
157
160
|
...(sendTemperature ? { temperature: req.temperature } : {}),
|
|
158
161
|
};
|
|
159
162
|
const stream = await this.client.responses.create(params, { signal });
|
|
163
|
+
hooks?.onResponseStarted?.();
|
|
160
164
|
let sawToolCall = false;
|
|
161
165
|
for await (const event of stream) {
|
|
162
166
|
switch (event.type) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ModelCatalog } from "./catalog.js";
|
|
2
2
|
import { type CatalogEntry, type ModelProvider, type ResolvedCatalogEntry } from "./catalog-types.js";
|
|
3
|
-
import type { LlmEffort, LlmMessage, LlmProvider, LlmRequest, LlmStreamEvent, LlmToolDef } from "./types.js";
|
|
3
|
+
import type { LlmEffort, LlmMessage, LlmProvider, LlmRequest, LlmStreamEvent, LlmStreamHooks, LlmToolDef } from "./types.js";
|
|
4
4
|
/** Effort levels `model` accepts, from the current catalog; no credentials needed (config-time validation). */
|
|
5
5
|
export declare function modelSupportedEfforts(model: string): readonly LlmEffort[];
|
|
6
6
|
export declare function modelAcceptsEffort(model: string, effort: LlmEffort): boolean;
|
|
@@ -24,7 +24,7 @@ export declare class RoutingLlmProvider implements LlmProvider {
|
|
|
24
24
|
/** A provider bound to one run's stored entry: prices and shapes requests from it, never the live catalog. */
|
|
25
25
|
forRun(entry: CatalogEntry): LlmProvider;
|
|
26
26
|
private resolve;
|
|
27
|
-
stream(req: LlmRequest, signal?: AbortSignal): AsyncIterable<LlmStreamEvent>;
|
|
27
|
+
stream(req: LlmRequest, signal?: AbortSignal, hooks?: LlmStreamHooks): AsyncIterable<LlmStreamEvent>;
|
|
28
28
|
countTokens(model: string, messages: LlmMessage[], tools?: LlmToolDef[]): Promise<number>;
|
|
29
29
|
priceUsd(model: string, usage: {
|
|
30
30
|
inputTokens: number;
|
|
@@ -54,8 +54,8 @@ export class RoutingLlmProvider {
|
|
|
54
54
|
resolve(model) {
|
|
55
55
|
return this.byProvider.get(this.entryFor(model).provider);
|
|
56
56
|
}
|
|
57
|
-
stream(req, signal) {
|
|
58
|
-
return this.resolve(req.model).stream(req, signal);
|
|
57
|
+
stream(req, signal, hooks) {
|
|
58
|
+
return this.resolve(req.model).stream(req, signal, hooks);
|
|
59
59
|
}
|
|
60
60
|
countTokens(model, messages, tools) {
|
|
61
61
|
return this.resolve(model).countTokens(model, messages, tools);
|
|
@@ -68,7 +68,9 @@ export interface LlmUsage {
|
|
|
68
68
|
export type LlmStreamEvent = {
|
|
69
69
|
type: "text";
|
|
70
70
|
delta: string;
|
|
71
|
-
}
|
|
71
|
+
}
|
|
72
|
+
/** `id` must be unique within a run, not only within a turn: a delegation keys its child run on it. */
|
|
73
|
+
| {
|
|
72
74
|
type: "tool_call";
|
|
73
75
|
id: string;
|
|
74
76
|
name: string;
|
|
@@ -78,8 +80,16 @@ export type LlmStreamEvent = {
|
|
|
78
80
|
stopReason: string;
|
|
79
81
|
usage: LlmUsage;
|
|
80
82
|
};
|
|
83
|
+
/** Observers of a stream's progress that its events do not show. */
|
|
84
|
+
export interface LlmStreamHooks {
|
|
85
|
+
/**
|
|
86
|
+
* Called once the provider's response has started. From then on the provider
|
|
87
|
+
* may bill output (thinking or reasoning included) before any event is yielded.
|
|
88
|
+
*/
|
|
89
|
+
onResponseStarted?: () => void;
|
|
90
|
+
}
|
|
81
91
|
export interface LlmProvider {
|
|
82
|
-
stream(req: LlmRequest, signal?: AbortSignal): AsyncIterable<LlmStreamEvent>;
|
|
92
|
+
stream(req: LlmRequest, signal?: AbortSignal, hooks?: LlmStreamHooks): AsyncIterable<LlmStreamEvent>;
|
|
83
93
|
/**
|
|
84
94
|
* Pre-flight token count for budget enforcement. `tools`, when passed,
|
|
85
95
|
* must be counted too — a provider bills the serialized tool schemas as
|
|
@@ -6,8 +6,16 @@ type Json = Record<string, unknown>;
|
|
|
6
6
|
* counts: anyone can write the marker into their own PR's description.
|
|
7
7
|
*/
|
|
8
8
|
export declare function appMarkerRunId(pr: Json | null, slug: string): string | undefined;
|
|
9
|
+
/** Why a mention was dropped before routing; never carries the body. */
|
|
10
|
+
export interface DroppedMention {
|
|
11
|
+
eventName: string;
|
|
12
|
+
repository: string;
|
|
13
|
+
association: string;
|
|
14
|
+
reason: "author_association" | "not_a_user";
|
|
15
|
+
}
|
|
16
|
+
/** `onDropped` hears about a mention the author pre-filter dropped, so the caller can log it. */
|
|
9
17
|
export declare function normalizeGitHubEvent(eventName: string, payload: unknown, app: {
|
|
10
18
|
id: number;
|
|
11
19
|
slug: string;
|
|
12
|
-
}): HostEvent | null;
|
|
20
|
+
}, onDropped?: (dropped: DroppedMention) => void): HostEvent | null;
|
|
13
21
|
export {};
|
|
@@ -4,8 +4,14 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { createHmac, timingSafeEqual } from "node:crypto";
|
|
6
6
|
import { normalizeGitHubRepository } from "../../coding/protocol.js";
|
|
7
|
+
import { withoutMarkdownCode } from "../../core/markdown-code.js";
|
|
7
8
|
const PR_ACTIONS = new Set(["opened", "synchronize", "reopened", "ready_for_review"]);
|
|
8
|
-
|
|
9
|
+
/**
|
|
10
|
+
* GitHub labels an organization member whose membership is private CONTRIBUTOR
|
|
11
|
+
* or NONE, so both pass; first-time authors and mannequins are drive-by
|
|
12
|
+
* accounts that cannot have access to a repository they never committed to.
|
|
13
|
+
*/
|
|
14
|
+
const TRUSTED_ASSOCIATIONS = new Set(["OWNER", "MEMBER", "COLLABORATOR", "CONTRIBUTOR", "NONE"]);
|
|
9
15
|
const SAFE_SHA = /^[0-9a-f]{40}$/;
|
|
10
16
|
// GitHub's push payload includes at most 2048 commits.
|
|
11
17
|
const PUSH_MAX_COMMITS = 2048;
|
|
@@ -37,9 +43,10 @@ function repositoryOf(payload) {
|
|
|
37
43
|
return null;
|
|
38
44
|
}
|
|
39
45
|
}
|
|
46
|
+
/** A mention of the App outside code: a mention quoted in code documents it, not requests a run. */
|
|
40
47
|
function mentions(body, slug) {
|
|
41
48
|
const escaped = slug.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
42
|
-
return new RegExp(`(^|[^\\w@.-])@${escaped}(?![\\w-])`, "i").test(body);
|
|
49
|
+
return new RegExp(`(^|[^\\w@.-])@${escaped}(?![\\w-])`, "i").test(withoutMarkdownCode(body));
|
|
43
50
|
}
|
|
44
51
|
const text = (value) => (typeof value === "string" ? value : "");
|
|
45
52
|
function subjectOf(item) {
|
|
@@ -73,20 +80,27 @@ function priorRunIdOf(pr, slug) {
|
|
|
73
80
|
* any org member, COLLABORATOR includes read and triage). The real gate is the
|
|
74
81
|
* author's live repository permission, checked by id in core/host-events.ts.
|
|
75
82
|
*/
|
|
76
|
-
function trustedHuman(association, user) {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
83
|
+
function trustedHuman(association, user, drop) {
|
|
84
|
+
const label = typeof association === "string" ? association : "missing";
|
|
85
|
+
if (typeof association !== "string" || !TRUSTED_ASSOCIATIONS.has(association)) {
|
|
86
|
+
drop("author_association", label);
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
if (user?.type !== "User" || typeof user.login !== "string" || int(user.id) === null) {
|
|
90
|
+
drop("not_a_user", label);
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
return true;
|
|
82
94
|
}
|
|
83
|
-
|
|
95
|
+
/** `onDropped` hears about a mention the author pre-filter dropped, so the caller can log it. */
|
|
96
|
+
export function normalizeGitHubEvent(eventName, payload, app, onDropped) {
|
|
84
97
|
const p = obj(payload);
|
|
85
98
|
if (!p)
|
|
86
99
|
return null;
|
|
87
100
|
const repository = repositoryOf(p);
|
|
88
101
|
if (!repository)
|
|
89
102
|
return null;
|
|
103
|
+
const drop = (reason, association) => onDropped?.({ eventName, repository, association, reason });
|
|
90
104
|
if (eventName === "push") {
|
|
91
105
|
const defaultBranch = obj(p.repository)?.default_branch;
|
|
92
106
|
const { before, after } = p;
|
|
@@ -181,10 +195,10 @@ export function normalizeGitHubEvent(eventName, payload, app) {
|
|
|
181
195
|
return null;
|
|
182
196
|
const body = text(issue?.body);
|
|
183
197
|
const user = obj(issue?.user);
|
|
184
|
-
if (!trustedHuman(issue?.author_association, user))
|
|
185
|
-
return null;
|
|
186
198
|
if (!mentions(title, app.slug) && !mentions(body, app.slug))
|
|
187
199
|
return null;
|
|
200
|
+
if (!trustedHuman(issue?.author_association, user, drop))
|
|
201
|
+
return null;
|
|
188
202
|
if (p.action === "edited") {
|
|
189
203
|
// Only the author's own edit, and only one that newly adds the mention — not every later edit.
|
|
190
204
|
const sender = obj(p.sender);
|
|
@@ -209,6 +223,10 @@ export function normalizeGitHubEvent(eventName, payload, app) {
|
|
|
209
223
|
body,
|
|
210
224
|
author: user.login,
|
|
211
225
|
authorId: String(user.id),
|
|
226
|
+
authorAssociation: String(issue?.author_association),
|
|
227
|
+
...(typeof obj(p.repository)?.private === "boolean"
|
|
228
|
+
? { repositoryPrivate: obj(p.repository)?.private }
|
|
229
|
+
: {}),
|
|
212
230
|
subject: { title, body },
|
|
213
231
|
};
|
|
214
232
|
}
|
|
@@ -221,7 +239,7 @@ export function normalizeGitHubEvent(eventName, payload, app) {
|
|
|
221
239
|
const commentId = int(comment?.id);
|
|
222
240
|
if (typeof body !== "string" || !commentId || !mentions(body, app.slug))
|
|
223
241
|
return null;
|
|
224
|
-
if (!trustedHuman(comment?.author_association, user))
|
|
242
|
+
if (!trustedHuman(comment?.author_association, user, drop))
|
|
225
243
|
return null;
|
|
226
244
|
const isInline = eventName === "pull_request_review_comment";
|
|
227
245
|
const parent = obj(isInline ? p.pull_request : p.issue);
|
|
@@ -242,6 +260,10 @@ export function normalizeGitHubEvent(eventName, payload, app) {
|
|
|
242
260
|
body,
|
|
243
261
|
author: user.login,
|
|
244
262
|
authorId: String(user.id),
|
|
263
|
+
authorAssociation: String(comment?.author_association),
|
|
264
|
+
...(typeof obj(p.repository)?.private === "boolean"
|
|
265
|
+
? { repositoryPrivate: obj(p.repository)?.private }
|
|
266
|
+
: {}),
|
|
245
267
|
...(subject ? { subject } : {}),
|
|
246
268
|
...(priorRunId ? { priorRunId } : {}),
|
|
247
269
|
};
|