@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
|
@@ -10,6 +10,7 @@ import { NativeEngine } from "../core/engine-native.js";
|
|
|
10
10
|
import { computeCost } from "../providers/llm/pricing-core.js";
|
|
11
11
|
import { PRIVILEGED_BRIDGE_NAMES } from "../sandbox/host-functions.js";
|
|
12
12
|
import { runUserToolCall } from "../sandbox/user-tool.js";
|
|
13
|
+
import { rememberSecretValue } from "./stderr-redaction.js";
|
|
13
14
|
import { GatewayError, NATIVE_WORKER_PROTOCOL_VERSION, } from "./protocol.js";
|
|
14
15
|
const isPending = (value) => typeof value === "object" && value !== null && value.pending === true;
|
|
15
16
|
/** Runs one native run to its result and reports it with `finish`. Resolves when done. */
|
|
@@ -54,7 +55,11 @@ export async function runNativeWorker(input, transport) {
|
|
|
54
55
|
bridge,
|
|
55
56
|
async (argsJson) => {
|
|
56
57
|
try {
|
|
57
|
-
|
|
58
|
+
const result = await call("host.call", { tool, invocation, bridge, argsJson });
|
|
59
|
+
// A secret value now lives in this process: keep it out of everything it writes to stderr.
|
|
60
|
+
if (bridge === "__bridge_secretsGet" && typeof result === "string")
|
|
61
|
+
rememberSecretValue(result);
|
|
62
|
+
return result;
|
|
58
63
|
}
|
|
59
64
|
catch (err) {
|
|
60
65
|
// A bridge failure reaches the tool as the same error message it would see in-process.
|
|
@@ -103,7 +108,28 @@ export async function runNativeWorker(input, transport) {
|
|
|
103
108
|
await call("progress", { turns: progress.turns, usage: progress.usage });
|
|
104
109
|
},
|
|
105
110
|
};
|
|
106
|
-
|
|
111
|
+
let result;
|
|
112
|
+
try {
|
|
113
|
+
result = await new NativeEngine().run(ctx);
|
|
114
|
+
}
|
|
115
|
+
catch (err) {
|
|
116
|
+
// A gateway failure (a delegation that could not be created, say) ends the run as failed with
|
|
117
|
+
// its code, not as an opaque worker exit. run_not_drivable means the run already ended.
|
|
118
|
+
if (!(err instanceof GatewayError) || err.code === "run_not_drivable")
|
|
119
|
+
throw err;
|
|
120
|
+
await textSent;
|
|
121
|
+
await call("finish", {
|
|
122
|
+
status: "failed",
|
|
123
|
+
finalText: "",
|
|
124
|
+
turns: 0,
|
|
125
|
+
// `internal` wraps raw exception text on some transports, so only its code (and the cause
|
|
126
|
+
// code the gateway carried with it, if any) is persisted, never its message.
|
|
127
|
+
error: err.code === "internal"
|
|
128
|
+
? `native_sandbox_gateway_error: internal${err.causeCode ? `: ${err.causeCode}` : ""}`
|
|
129
|
+
: `native_sandbox_gateway_error: ${err.code}: ${err.message.slice(0, 1024)}`,
|
|
130
|
+
});
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
107
133
|
// Live text reaches observers before the run is reported finished.
|
|
108
134
|
await textSent;
|
|
109
135
|
await call("finish", {
|
|
@@ -133,8 +133,13 @@ export class WardbyMetrics {
|
|
|
133
133
|
}
|
|
134
134
|
observeProxyAudit = (event) => {
|
|
135
135
|
this.proxyAuditEvents.inc({ event: event.type });
|
|
136
|
-
|
|
136
|
+
// A completed reply repeats its request's hold (for the overshoot audit): count it once, when held.
|
|
137
|
+
// A refused request carries the hold that did not fit, which was never reserved.
|
|
138
|
+
if (event.reservationUsd !== undefined &&
|
|
139
|
+
event.type !== "response.completed" &&
|
|
140
|
+
event.type !== "request.rejected") {
|
|
137
141
|
this.proxyBudgetReserved.inc(event.reservationUsd);
|
|
142
|
+
}
|
|
138
143
|
if (event.type !== "response.completed")
|
|
139
144
|
return;
|
|
140
145
|
if (event.costUsd !== undefined)
|
|
@@ -54,8 +54,8 @@ export declare class SelfHostedAuthProvider implements AuthProvider {
|
|
|
54
54
|
id: string;
|
|
55
55
|
state: string | null;
|
|
56
56
|
resource: string;
|
|
57
|
-
clientId: string;
|
|
58
57
|
expiresAt: Date;
|
|
58
|
+
clientId: string;
|
|
59
59
|
consumedAt: Date | null;
|
|
60
60
|
redirectUri: string;
|
|
61
61
|
codeChallenge: string;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { BlobStore } from "../../storage/types.js";
|
|
2
|
+
import type { DependencyCacheStore, EvictionRow } from "./store.js";
|
|
3
|
+
/** How often the proxy runs an eviction pass. */
|
|
4
|
+
export declare const DEFAULT_EVICTION_INTERVAL_MS: number;
|
|
5
|
+
export interface EvictionLimits {
|
|
6
|
+
maxAgeMs: number;
|
|
7
|
+
repoCapBytes: number;
|
|
8
|
+
totalCapBytes: number;
|
|
9
|
+
}
|
|
10
|
+
/** The snapshots to evict, in the order to evict them. */
|
|
11
|
+
export declare function selectEvictions(rows: readonly EvictionRow[], limits: EvictionLimits, now: Date): EvictionRow[];
|
|
12
|
+
export interface EvictionOptions {
|
|
13
|
+
store: DependencyCacheStore;
|
|
14
|
+
blobs: Pick<BlobStore, "delete">;
|
|
15
|
+
limits: EvictionLimits;
|
|
16
|
+
now?: () => Date;
|
|
17
|
+
}
|
|
18
|
+
/** One eviction pass; `ran` is false when another holder had the lock. */
|
|
19
|
+
export declare function runEviction(options: EvictionOptions): Promise<{
|
|
20
|
+
ran: boolean;
|
|
21
|
+
deleted: number;
|
|
22
|
+
failed: number;
|
|
23
|
+
}>;
|
|
24
|
+
/** Runs an eviction pass every `intervalMs` (the first one interval after start). */
|
|
25
|
+
export declare function startEviction(options: EvictionOptions & {
|
|
26
|
+
intervalMs?: number;
|
|
27
|
+
}): {
|
|
28
|
+
stop(): void;
|
|
29
|
+
};
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dependency cache eviction: a periodic pass inside the proxy, coordinated
|
|
3
|
+
* across replicas by a Postgres advisory lock (the store's
|
|
4
|
+
* withEvictionLock). It removes snapshots past the maximum age (by creation,
|
|
5
|
+
* so every snapshot is rebuilt at least that often), then the least recently
|
|
6
|
+
* used beyond each repository's cap, then beyond the total cap. A blob is
|
|
7
|
+
* deleted before its row, so a failed blob delete leaves the row for the next
|
|
8
|
+
* pass to retry rather than an orphaned blob.
|
|
9
|
+
*/
|
|
10
|
+
import { logger } from "../../../core/logger.js";
|
|
11
|
+
const evictionLog = logger.child({ module: "dependency-cache" });
|
|
12
|
+
/** How often the proxy runs an eviction pass. */
|
|
13
|
+
export const DEFAULT_EVICTION_INTERVAL_MS = 60 * 60_000;
|
|
14
|
+
const leastRecentlyUsed = (a, b) => a.lastUsedAt.getTime() - b.lastUsedAt.getTime() || (a.id < b.id ? -1 : a.id > b.id ? 1 : 0);
|
|
15
|
+
/** The snapshots to evict, in the order to evict them. */
|
|
16
|
+
export function selectEvictions(rows, limits, now) {
|
|
17
|
+
const cutoff = now.getTime() - limits.maxAgeMs;
|
|
18
|
+
const evicted = rows
|
|
19
|
+
.filter((row) => row.createdAt.getTime() <= cutoff)
|
|
20
|
+
.sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime() || leastRecentlyUsed(a, b));
|
|
21
|
+
let kept = rows.filter((row) => row.createdAt.getTime() > cutoff);
|
|
22
|
+
const byRepository = new Map();
|
|
23
|
+
for (const row of kept)
|
|
24
|
+
byRepository.set(row.repository, [...(byRepository.get(row.repository) ?? []), row]);
|
|
25
|
+
const overRepoCap = new Set();
|
|
26
|
+
for (const repoRows of byRepository.values()) {
|
|
27
|
+
let total = repoRows.reduce((sum, row) => sum + row.sizeBytes, 0);
|
|
28
|
+
for (const row of [...repoRows].sort(leastRecentlyUsed)) {
|
|
29
|
+
if (total <= limits.repoCapBytes)
|
|
30
|
+
break;
|
|
31
|
+
evicted.push(row);
|
|
32
|
+
overRepoCap.add(row.id);
|
|
33
|
+
total -= row.sizeBytes;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
kept = kept.filter((row) => !overRepoCap.has(row.id));
|
|
37
|
+
let total = kept.reduce((sum, row) => sum + row.sizeBytes, 0);
|
|
38
|
+
for (const row of [...kept].sort(leastRecentlyUsed)) {
|
|
39
|
+
if (total <= limits.totalCapBytes)
|
|
40
|
+
break;
|
|
41
|
+
evicted.push(row);
|
|
42
|
+
total -= row.sizeBytes;
|
|
43
|
+
}
|
|
44
|
+
return evicted;
|
|
45
|
+
}
|
|
46
|
+
/** One eviction pass; `ran` is false when another holder had the lock. */
|
|
47
|
+
export async function runEviction(options) {
|
|
48
|
+
const result = { ran: false, deleted: 0, failed: 0 };
|
|
49
|
+
result.ran = await options.store.withEvictionLock(async () => {
|
|
50
|
+
const rows = await options.store.listForEviction();
|
|
51
|
+
for (const row of selectEvictions(rows, options.limits, (options.now ?? (() => new Date()))())) {
|
|
52
|
+
try {
|
|
53
|
+
await options.blobs.delete(row.blobKey);
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
result.failed += 1;
|
|
57
|
+
evictionLog.warn({ event: "dependency_cache.evict_blob_failed", snapshotId: row.id, err: error }, "dependency cache: a snapshot's blob could not be deleted; the next pass retries");
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
await options.store.delete(row.id);
|
|
61
|
+
result.deleted += 1;
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
return result;
|
|
65
|
+
}
|
|
66
|
+
/** Runs an eviction pass every `intervalMs` (the first one interval after start). */
|
|
67
|
+
export function startEviction(options) {
|
|
68
|
+
let running = false;
|
|
69
|
+
const timer = setInterval(() => {
|
|
70
|
+
if (running)
|
|
71
|
+
return;
|
|
72
|
+
running = true;
|
|
73
|
+
runEviction(options)
|
|
74
|
+
.then((result) => {
|
|
75
|
+
if (result.deleted > 0 || result.failed > 0)
|
|
76
|
+
evictionLog.info({ event: "dependency_cache.evicted", deleted: result.deleted, failed: result.failed }, "dependency cache eviction pass");
|
|
77
|
+
})
|
|
78
|
+
.catch((error) => {
|
|
79
|
+
evictionLog.warn({ event: "dependency_cache.evict_failed", err: error }, "dependency cache eviction failed");
|
|
80
|
+
})
|
|
81
|
+
.finally(() => {
|
|
82
|
+
running = false;
|
|
83
|
+
});
|
|
84
|
+
}, options.intervalMs ?? DEFAULT_EVICTION_INTERVAL_MS);
|
|
85
|
+
timer.unref();
|
|
86
|
+
return { stop: () => clearInterval(timer) };
|
|
87
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/** The only ecosystem the cache serves today. */
|
|
2
|
+
export declare const DEPENDENCY_CACHE_ECOSYSTEM = "npm";
|
|
3
|
+
/** Lockfile hashes one session may hold (plans beyond this are not recorded). */
|
|
4
|
+
export declare const MAX_DEPENDENCY_CACHE_LOCK_HASHES = 8;
|
|
5
|
+
export declare function sha256Hex(data: string | Uint8Array): string;
|
|
6
|
+
/** JSON with object keys sorted, so equal values always serialize alike. */
|
|
7
|
+
export declare function canonicalJson(value: unknown): string;
|
|
8
|
+
export declare function isLockHash(value: string): boolean;
|
|
9
|
+
/**
|
|
10
|
+
* The project folder relative to the repository root, or null when it is not
|
|
11
|
+
* acceptable: "." (or absent) is the root; otherwise 1-3 plain segments, none
|
|
12
|
+
* empty, none starting with a dot, none named node_modules.
|
|
13
|
+
*/
|
|
14
|
+
export declare function parseProjectPath(raw: string | null | undefined): string | null;
|
|
15
|
+
export interface SnapshotKeyParts {
|
|
16
|
+
repository: string;
|
|
17
|
+
projectPath: string;
|
|
18
|
+
lockHash: string;
|
|
19
|
+
workerImage: string;
|
|
20
|
+
}
|
|
21
|
+
export interface SnapshotKey extends SnapshotKeyParts {
|
|
22
|
+
ecosystem: string;
|
|
23
|
+
/** The opaque id a worker downloads the derived key by: sha256 of the canonical identity tuple. */
|
|
24
|
+
keyId: string;
|
|
25
|
+
}
|
|
26
|
+
export declare function deriveSnapshotKey(parts: SnapshotKeyParts): SnapshotKey;
|
|
27
|
+
/**
|
|
28
|
+
* A fresh blob key for one upload of `key`. Each upload writes its own object
|
|
29
|
+
* (the random suffix), so a concurrent or refused upload can never replace
|
|
30
|
+
* the bytes of a snapshot already committed; a row always names the exact
|
|
31
|
+
* object its sha256 and size describe.
|
|
32
|
+
*/
|
|
33
|
+
export declare function newBlobKey(key: SnapshotKey): string;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dependency cache keys, derived only on the proxy. A worker names nothing
|
|
3
|
+
* in a key except its project folder (validated here); the lock hash comes
|
|
4
|
+
* from a lockfile plan the proxy approved, and the repository and image come
|
|
5
|
+
* from the run's own records.
|
|
6
|
+
*
|
|
7
|
+
* The agent's package policy is not part of a key. A snapshot's contents
|
|
8
|
+
* depend only on the lockfile and the image (`npm ci --ignore-scripts`
|
|
9
|
+
* installs exactly the lockfile's integrity-checked packages), and every
|
|
10
|
+
* restore already requires the proxy to have approved this run's lockfile
|
|
11
|
+
* under the restoring agent's current policy (the plan gate,
|
|
12
|
+
* `dependencyCacheLockHashes`). So a snapshot built under another policy, or
|
|
13
|
+
* by another agent on the same repository, restores only when today's policy
|
|
14
|
+
* approves the same lockfile.
|
|
15
|
+
*/
|
|
16
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
17
|
+
/** The only ecosystem the cache serves today. */
|
|
18
|
+
export const DEPENDENCY_CACHE_ECOSYSTEM = "npm";
|
|
19
|
+
/** Lockfile hashes one session may hold (plans beyond this are not recorded). */
|
|
20
|
+
export const MAX_DEPENDENCY_CACHE_LOCK_HASHES = 8;
|
|
21
|
+
const LOCK_HASH = /^[0-9a-f]{64}$/;
|
|
22
|
+
/** A project-path segment: no leading dot (no `.`, `..` or dot-folder), no
|
|
23
|
+
* separators, whitespace or control characters. */
|
|
24
|
+
const PROJECT_SEGMENT = /^[A-Za-z0-9_@+-][A-Za-z0-9._@+-]{0,99}$/;
|
|
25
|
+
const MAX_PROJECT_SEGMENTS = 3;
|
|
26
|
+
export function sha256Hex(data) {
|
|
27
|
+
return createHash("sha256").update(data).digest("hex");
|
|
28
|
+
}
|
|
29
|
+
function canonical(value) {
|
|
30
|
+
if (Array.isArray(value))
|
|
31
|
+
return value.map((item) => (item === undefined ? null : canonical(item)));
|
|
32
|
+
if (value !== null && typeof value === "object") {
|
|
33
|
+
const record = value;
|
|
34
|
+
// Code-unit order (the default sort), never locale order: the hash must
|
|
35
|
+
// be the same on every host.
|
|
36
|
+
return Object.fromEntries(Object.keys(record)
|
|
37
|
+
.filter((key) => record[key] !== undefined)
|
|
38
|
+
.sort()
|
|
39
|
+
.map((key) => [key, canonical(record[key])]));
|
|
40
|
+
}
|
|
41
|
+
return value;
|
|
42
|
+
}
|
|
43
|
+
/** JSON with object keys sorted, so equal values always serialize alike. */
|
|
44
|
+
export function canonicalJson(value) {
|
|
45
|
+
return JSON.stringify(canonical(value)) ?? "null";
|
|
46
|
+
}
|
|
47
|
+
export function isLockHash(value) {
|
|
48
|
+
return LOCK_HASH.test(value);
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* The project folder relative to the repository root, or null when it is not
|
|
52
|
+
* acceptable: "." (or absent) is the root; otherwise 1-3 plain segments, none
|
|
53
|
+
* empty, none starting with a dot, none named node_modules.
|
|
54
|
+
*/
|
|
55
|
+
export function parseProjectPath(raw) {
|
|
56
|
+
if (raw === undefined || raw === null || raw === ".")
|
|
57
|
+
return ".";
|
|
58
|
+
const segments = raw.split("/");
|
|
59
|
+
if (segments.length > MAX_PROJECT_SEGMENTS)
|
|
60
|
+
return null;
|
|
61
|
+
for (const segment of segments) {
|
|
62
|
+
if (!PROJECT_SEGMENT.test(segment) || segment === "node_modules")
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
return raw;
|
|
66
|
+
}
|
|
67
|
+
export function deriveSnapshotKey(parts) {
|
|
68
|
+
const identity = {
|
|
69
|
+
ecosystem: DEPENDENCY_CACHE_ECOSYSTEM,
|
|
70
|
+
repository: parts.repository,
|
|
71
|
+
projectPath: parts.projectPath,
|
|
72
|
+
lockHash: parts.lockHash,
|
|
73
|
+
workerImage: parts.workerImage,
|
|
74
|
+
};
|
|
75
|
+
return { ...identity, keyId: sha256Hex(canonicalJson(identity)) };
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* A fresh blob key for one upload of `key`. Each upload writes its own object
|
|
79
|
+
* (the random suffix), so a concurrent or refused upload can never replace
|
|
80
|
+
* the bytes of a snapshot already committed; a row always names the exact
|
|
81
|
+
* object its sha256 and size describe.
|
|
82
|
+
*/
|
|
83
|
+
export function newBlobKey(key) {
|
|
84
|
+
return (`dependency-cache/${key.ecosystem}/${sha256Hex(key.repository)}/${sha256Hex(key.projectPath).slice(0, 16)}/` +
|
|
85
|
+
`${key.lockHash}-${sha256Hex(key.workerImage).slice(0, 16)}-${randomUUID()}.tgz`);
|
|
86
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { DependencyCacheStore } from "./store.js";
|
|
2
|
+
/**
|
|
3
|
+
* Seals a run's dependency-cache session on its first model request: the agent exists from that
|
|
4
|
+
* request on, so the cache's pre-agent phase is over whatever the worker did (or failed to do).
|
|
5
|
+
* The workers run the cache before they create the agent (Codex: driver.ts runs it before
|
|
6
|
+
* startThread; Claude Code: driver.ts calls the prepare tool before createQuery), so no model
|
|
7
|
+
* request precedes it. The seal is idempotent and conditional (only an unsealed session of a run
|
|
8
|
+
* with the cache on), remembered per run in this process so it costs one write per run per proxy
|
|
9
|
+
* replica, and never fails the model request: a database error is logged and retried on the
|
|
10
|
+
* run's next request.
|
|
11
|
+
*/
|
|
12
|
+
export declare class ModelRequestSeal {
|
|
13
|
+
private readonly store;
|
|
14
|
+
private readonly now;
|
|
15
|
+
private readonly sealed;
|
|
16
|
+
constructor(store: Pick<DependencyCacheStore, "sealForModelRequest">, now?: () => Date);
|
|
17
|
+
onModelRequest(runId: string): Promise<void>;
|
|
18
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { logger } from "../../../core/logger.js";
|
|
2
|
+
const sealLog = logger.child({ module: "dependency-cache" });
|
|
3
|
+
/** Runs remembered as sealed per proxy process; the oldest are forgotten past this (a re-seal is a no-op write). */
|
|
4
|
+
const MAX_REMEMBERED_RUNS = 10_000;
|
|
5
|
+
/**
|
|
6
|
+
* Seals a run's dependency-cache session on its first model request: the agent exists from that
|
|
7
|
+
* request on, so the cache's pre-agent phase is over whatever the worker did (or failed to do).
|
|
8
|
+
* The workers run the cache before they create the agent (Codex: driver.ts runs it before
|
|
9
|
+
* startThread; Claude Code: driver.ts calls the prepare tool before createQuery), so no model
|
|
10
|
+
* request precedes it. The seal is idempotent and conditional (only an unsealed session of a run
|
|
11
|
+
* with the cache on), remembered per run in this process so it costs one write per run per proxy
|
|
12
|
+
* replica, and never fails the model request: a database error is logged and retried on the
|
|
13
|
+
* run's next request.
|
|
14
|
+
*/
|
|
15
|
+
export class ModelRequestSeal {
|
|
16
|
+
store;
|
|
17
|
+
now;
|
|
18
|
+
sealed = new Set();
|
|
19
|
+
constructor(store, now = () => new Date()) {
|
|
20
|
+
this.store = store;
|
|
21
|
+
this.now = now;
|
|
22
|
+
}
|
|
23
|
+
async onModelRequest(runId) {
|
|
24
|
+
if (this.sealed.has(runId))
|
|
25
|
+
return;
|
|
26
|
+
try {
|
|
27
|
+
await this.store.sealForModelRequest(runId, this.now());
|
|
28
|
+
}
|
|
29
|
+
catch (error) {
|
|
30
|
+
sealLog.warn({ event: "dependency_cache.model_seal_failed", runId, err: error }, "dependency cache: seal on the run's model request failed");
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
this.sealed.add(runId);
|
|
34
|
+
if (this.sealed.size > MAX_REMEMBERED_RUNS) {
|
|
35
|
+
const oldest = this.sealed.values().next().value;
|
|
36
|
+
if (oldest !== undefined)
|
|
37
|
+
this.sealed.delete(oldest);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { type PrismaClient } from "#prisma";
|
|
2
|
+
import type { DependencyCacheSessionState } from "../registry/store.js";
|
|
3
|
+
import { type DependencyCacheStore, type EvictionRow, type NewSnapshot, type SnapshotIdentity, type SnapshotRow, type SnapshotScope } from "./store.js";
|
|
4
|
+
export declare class PrismaDependencyCacheStore implements DependencyCacheStore {
|
|
5
|
+
private readonly db;
|
|
6
|
+
constructor(db: PrismaClient);
|
|
7
|
+
session(runId: string): Promise<DependencyCacheSessionState | null>;
|
|
8
|
+
seal(runId: string, now: Date): Promise<void>;
|
|
9
|
+
sealForModelRequest(runId: string, now: Date): Promise<void>;
|
|
10
|
+
findByIdentity(identity: SnapshotIdentity): Promise<SnapshotRow | null>;
|
|
11
|
+
findNearest(scope: SnapshotScope, excludeLockHash: string): Promise<SnapshotRow | null>;
|
|
12
|
+
hasUploaded(runId: string, ecosystem: string, projectPath: string): Promise<boolean>;
|
|
13
|
+
insert(snapshot: NewSnapshot): Promise<"inserted" | "exists">;
|
|
14
|
+
touch(id: string, now: Date): Promise<void>;
|
|
15
|
+
delete(id: string): Promise<void>;
|
|
16
|
+
deleteIfBlob(id: string, blobKey: string): Promise<boolean>;
|
|
17
|
+
listForEviction(): Promise<EvictionRow[]>;
|
|
18
|
+
/**
|
|
19
|
+
* A transaction-scoped advisory lock: Postgres releases it when the
|
|
20
|
+
* transaction ends, so a crashed proxy never leaves it held. The work runs
|
|
21
|
+
* on the ordinary client (each delete commits at once); the transaction
|
|
22
|
+
* only holds the lock.
|
|
23
|
+
*/
|
|
24
|
+
withEvictionLock(work: () => Promise<void>): Promise<boolean>;
|
|
25
|
+
}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { Prisma } from "#prisma";
|
|
2
|
+
import { POLICY_ANY, } from "./store.js";
|
|
3
|
+
/** How long one eviction pass may hold the lock (its transaction) before Postgres releases it. */
|
|
4
|
+
const EVICTION_LOCK_MS = 15 * 60_000;
|
|
5
|
+
/**
|
|
6
|
+
* Only the scope's own fields (callers may pass a wider object, such as a
|
|
7
|
+
* derived key), plus the fixed POLICY_ANY every row is written and matched with.
|
|
8
|
+
*/
|
|
9
|
+
const scopeOf = ({ ecosystem, repository, projectPath, workerImage }) => ({
|
|
10
|
+
ecosystem,
|
|
11
|
+
repository,
|
|
12
|
+
projectPath,
|
|
13
|
+
workerImage,
|
|
14
|
+
policyHash: POLICY_ANY,
|
|
15
|
+
});
|
|
16
|
+
const toSnapshot = ({ policyHash: _policyHash, ...row }) => ({
|
|
17
|
+
...row,
|
|
18
|
+
sizeBytes: Number(row.sizeBytes),
|
|
19
|
+
unpackedBytes: row.unpackedBytes === null ? null : Number(row.unpackedBytes),
|
|
20
|
+
});
|
|
21
|
+
export class PrismaDependencyCacheStore {
|
|
22
|
+
db;
|
|
23
|
+
constructor(db) {
|
|
24
|
+
this.db = db;
|
|
25
|
+
}
|
|
26
|
+
async session(runId) {
|
|
27
|
+
const session = await this.db.codingProxySession.findUnique({
|
|
28
|
+
where: { runId },
|
|
29
|
+
select: { dependencyCacheLockHashes: true, dependencyCacheSealedAt: true, createdAt: true },
|
|
30
|
+
});
|
|
31
|
+
return session
|
|
32
|
+
? {
|
|
33
|
+
lockHashes: session.dependencyCacheLockHashes,
|
|
34
|
+
sealedAt: session.dependencyCacheSealedAt,
|
|
35
|
+
createdAt: session.createdAt,
|
|
36
|
+
}
|
|
37
|
+
: null;
|
|
38
|
+
}
|
|
39
|
+
async seal(runId, now) {
|
|
40
|
+
await this.db.codingProxySession.updateMany({
|
|
41
|
+
where: { runId, dependencyCacheSealedAt: null },
|
|
42
|
+
data: { dependencyCacheSealedAt: now },
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
async sealForModelRequest(runId, now) {
|
|
46
|
+
await this.db.codingProxySession.updateMany({
|
|
47
|
+
where: { runId, dependencyCacheSealedAt: null, run: { dependencyCache: true } },
|
|
48
|
+
data: { dependencyCacheSealedAt: now },
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
async findByIdentity(identity) {
|
|
52
|
+
const row = await this.db.dependencySnapshot.findUnique({
|
|
53
|
+
where: {
|
|
54
|
+
ecosystem_repository_projectPath_lockHash_workerImage_policyHash: {
|
|
55
|
+
...scopeOf(identity),
|
|
56
|
+
lockHash: identity.lockHash,
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
});
|
|
60
|
+
return row ? toSnapshot(row) : null;
|
|
61
|
+
}
|
|
62
|
+
async findNearest(scope, excludeLockHash) {
|
|
63
|
+
const row = await this.db.dependencySnapshot.findFirst({
|
|
64
|
+
where: { ...scopeOf(scope), lockHash: { not: excludeLockHash } },
|
|
65
|
+
orderBy: [{ createdAt: "desc" }, { id: "desc" }],
|
|
66
|
+
});
|
|
67
|
+
return row ? toSnapshot(row) : null;
|
|
68
|
+
}
|
|
69
|
+
async hasUploaded(runId, ecosystem, projectPath) {
|
|
70
|
+
const row = await this.db.dependencySnapshot.findFirst({
|
|
71
|
+
where: { createdByRunId: runId, projectPath, ecosystem },
|
|
72
|
+
select: { id: true },
|
|
73
|
+
});
|
|
74
|
+
return row !== null;
|
|
75
|
+
}
|
|
76
|
+
async insert(snapshot) {
|
|
77
|
+
try {
|
|
78
|
+
await this.db.dependencySnapshot.create({
|
|
79
|
+
data: {
|
|
80
|
+
...scopeOf(snapshot),
|
|
81
|
+
lockHash: snapshot.lockHash,
|
|
82
|
+
blobKey: snapshot.blobKey,
|
|
83
|
+
sha256: snapshot.sha256,
|
|
84
|
+
sizeBytes: BigInt(snapshot.sizeBytes),
|
|
85
|
+
archiveScanVersion: snapshot.archiveScanVersion,
|
|
86
|
+
archiveEntries: snapshot.archiveEntries,
|
|
87
|
+
unpackedBytes: snapshot.unpackedBytes === null ? null : BigInt(snapshot.unpackedBytes),
|
|
88
|
+
createdByRunId: snapshot.createdByRunId,
|
|
89
|
+
},
|
|
90
|
+
});
|
|
91
|
+
return "inserted";
|
|
92
|
+
}
|
|
93
|
+
catch (error) {
|
|
94
|
+
if (error instanceof Prisma.PrismaClientKnownRequestError && error.code === "P2002")
|
|
95
|
+
return "exists";
|
|
96
|
+
throw error;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
async touch(id, now) {
|
|
100
|
+
await this.db.dependencySnapshot.updateMany({ where: { id }, data: { lastUsedAt: now } });
|
|
101
|
+
}
|
|
102
|
+
async delete(id) {
|
|
103
|
+
await this.db.dependencySnapshot.deleteMany({ where: { id } });
|
|
104
|
+
}
|
|
105
|
+
async deleteIfBlob(id, blobKey) {
|
|
106
|
+
const { count } = await this.db.dependencySnapshot.deleteMany({ where: { id, blobKey } });
|
|
107
|
+
return count > 0;
|
|
108
|
+
}
|
|
109
|
+
async listForEviction() {
|
|
110
|
+
const rows = await this.db.dependencySnapshot.findMany({
|
|
111
|
+
select: { id: true, repository: true, blobKey: true, sizeBytes: true, createdAt: true, lastUsedAt: true },
|
|
112
|
+
});
|
|
113
|
+
return rows.map((row) => ({ ...row, sizeBytes: Number(row.sizeBytes) }));
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* A transaction-scoped advisory lock: Postgres releases it when the
|
|
117
|
+
* transaction ends, so a crashed proxy never leaves it held. The work runs
|
|
118
|
+
* on the ordinary client (each delete commits at once); the transaction
|
|
119
|
+
* only holds the lock.
|
|
120
|
+
*/
|
|
121
|
+
async withEvictionLock(work) {
|
|
122
|
+
return this.db.$transaction(async (tx) => {
|
|
123
|
+
const [row] = await tx.$queryRaw `
|
|
124
|
+
SELECT pg_try_advisory_xact_lock(hashtext('wardby.dependency-cache-eviction')) AS locked`;
|
|
125
|
+
if (!row?.locked)
|
|
126
|
+
return false;
|
|
127
|
+
await work();
|
|
128
|
+
return true;
|
|
129
|
+
}, { maxWait: 10_000, timeout: EVICTION_LOCK_MS });
|
|
130
|
+
}
|
|
131
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The coding proxy's dependency cache: `/cache/npm/*`, authenticated by the
|
|
3
|
+
* run's registry-only token.
|
|
4
|
+
*
|
|
5
|
+
* Trust rules (the whole point of this file):
|
|
6
|
+
* - Every key is derived here, from the run's records and a lockfile hash the
|
|
7
|
+
* proxy computed while approving a plan for this session. A worker names
|
|
8
|
+
* only its project folder, which is validated, and which lockfile (one of
|
|
9
|
+
* the session's approved hashes) it is asking about.
|
|
10
|
+
* - Only trusted wardby code runs before the run's seal; the agent runs after
|
|
11
|
+
* it with the same token. The worker seals before it starts the agent, and
|
|
12
|
+
* the proxy seals on the run's first model request whatever the worker did
|
|
13
|
+
* (ModelRequestSeal). After the seal every route except the (idempotent)
|
|
14
|
+
* seal answers 409 `sealed`, and an upload still streaming when the seal
|
|
15
|
+
* lands is discarded.
|
|
16
|
+
* - A run uploads at most once per project, only within its upload window,
|
|
17
|
+
* and only for its derived key. Each upload writes its own blob object
|
|
18
|
+
* (a unique key), so no upload can replace a committed snapshot's bytes;
|
|
19
|
+
* an upload that does not end in its own row deletes its own object.
|
|
20
|
+
* - The sha256 is computed while the upload streams and stored in Postgres;
|
|
21
|
+
* downloads carry it so the worker verifies before extracting.
|
|
22
|
+
*/
|
|
23
|
+
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
24
|
+
import { type BlobStore } from "../../storage/types.js";
|
|
25
|
+
import type { RegistryStore } from "../registry/store.js";
|
|
26
|
+
import type { DependencyCacheStore } from "./store.js";
|
|
27
|
+
/** Uploads streaming at once across every run. */
|
|
28
|
+
export declare const DEFAULT_MAX_CONCURRENT_UPLOADS = 4;
|
|
29
|
+
export interface DependencyCacheServiceOptions {
|
|
30
|
+
registry: Pick<RegistryStore, "findRunByRegistryTokenHash">;
|
|
31
|
+
store: DependencyCacheStore;
|
|
32
|
+
blobs: BlobStore;
|
|
33
|
+
maxSnapshotBytes: number;
|
|
34
|
+
uploadMs: number;
|
|
35
|
+
now?: () => Date;
|
|
36
|
+
maxConcurrentUploads?: number;
|
|
37
|
+
}
|
|
38
|
+
export declare class DependencyCacheService {
|
|
39
|
+
private readonly options;
|
|
40
|
+
readonly uploadMs: number;
|
|
41
|
+
private readonly now;
|
|
42
|
+
/** `runId\0projectPath` and key ids of uploads streaming right now (per replica). */
|
|
43
|
+
private readonly uploading;
|
|
44
|
+
private activeUploads;
|
|
45
|
+
constructor(options: DependencyCacheServiceOptions);
|
|
46
|
+
/**
|
|
47
|
+
* Answers one `/cache/...` request. `token` is the registry token the
|
|
48
|
+
* request carried. `releaseBodyTimer` lifts the server's per-request body
|
|
49
|
+
* timer; it is called only for an authenticated, eligible upload that holds
|
|
50
|
+
* its upload slot (the upload window bounds it from then on).
|
|
51
|
+
*/
|
|
52
|
+
handle(request: IncomingMessage, response: ServerResponse, token: string, releaseBodyTimer?: () => void): Promise<void>;
|
|
53
|
+
private route;
|
|
54
|
+
/** When the run's upload window closes (epoch ms): the one deadline lookups report and uploads enforce. */
|
|
55
|
+
private uploadDeadline;
|
|
56
|
+
private lookup;
|
|
57
|
+
/**
|
|
58
|
+
* Serves the run's derived key (by its keyId) or the snapshot a lookup
|
|
59
|
+
* offers as nearest for the same project and lockfile, recomputed now with
|
|
60
|
+
* the same query: nothing is remembered between requests, and no other id
|
|
61
|
+
* is ever served.
|
|
62
|
+
*/
|
|
63
|
+
private download;
|
|
64
|
+
/** Removes a snapshot whose blob failed its row's checksum, so the key can be rebuilt. */
|
|
65
|
+
private dropCorrupt;
|
|
66
|
+
private upload;
|
|
67
|
+
private storeUpload;
|
|
68
|
+
/** Removes an upload's own blob object (unique to the upload, so no row refers to it). */
|
|
69
|
+
private discard;
|
|
70
|
+
}
|