@wardby/cli 0.2.1 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +53 -4
- package/README.md +52 -13
- package/dist/claude-coding-worker/driver.d.ts +6 -1
- package/dist/claude-coding-worker/driver.js +27 -1
- package/dist/claude-coding-worker/main.js +2 -0
- package/dist/claude-coding-worker/tool-socket.d.ts +9 -0
- package/dist/claude-coding-worker/tool-socket.js +26 -0
- package/dist/cli-help.d.ts +1 -1
- package/dist/cli-help.js +14 -4
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +284 -36
- package/dist/coding/base-commit.d.ts +6 -0
- package/dist/coding/base-commit.js +12 -0
- package/dist/coding/collect-exclude.d.ts +25 -0
- package/dist/coding/collect-exclude.js +76 -0
- package/dist/coding/profile.d.ts +67 -22
- package/dist/coding/profile.js +58 -25
- package/dist/coding/protected-path-wording.d.ts +24 -0
- package/dist/coding/protected-path-wording.js +55 -0
- package/dist/coding/protected-paths.d.ts +31 -0
- package/dist/coding/protected-paths.js +48 -0
- package/dist/coding/protocol.d.ts +91 -4
- package/dist/coding/protocol.js +123 -12
- package/dist/coding/provider.d.ts +6 -1
- package/dist/coding/provider.js +16 -9
- package/dist/coding/registry/adapters.d.ts +2 -0
- package/dist/coding/registry/adapters.js +9 -0
- package/dist/coding/registry/allowlist.d.ts +9 -0
- package/dist/coding/registry/allowlist.js +28 -0
- package/dist/coding/registry/json-scan.d.ts +55 -0
- package/dist/coding/registry/json-scan.js +191 -0
- package/dist/coding/registry/lockfiles.d.ts +6 -0
- package/dist/coding/registry/lockfiles.js +89 -0
- package/dist/coding/registry/npm-lockfile.d.ts +9 -0
- package/dist/coding/registry/npm-lockfile.js +73 -0
- package/dist/coding/registry/npm-plan.d.ts +29 -0
- package/dist/coding/registry/npm-plan.js +289 -0
- package/dist/coding/registry/npm.d.ts +7 -0
- package/dist/coding/registry/npm.js +252 -0
- package/dist/coding/registry/pypi.d.ts +4 -0
- package/dist/coding/registry/pypi.js +210 -0
- package/dist/coding/registry/report.d.ts +37 -0
- package/dist/coding/registry/report.js +31 -0
- package/dist/coding/registry/token.d.ts +4 -0
- package/dist/coding/registry/token.js +7 -0
- package/dist/coding/registry/types.d.ts +286 -0
- package/dist/coding/registry/types.js +19 -0
- package/dist/coding/registry/worker-config.d.ts +14 -0
- package/dist/coding/registry/worker-config.js +31 -0
- package/dist/coding/services/builtins.d.ts +22 -0
- package/dist/coding/services/builtins.js +85 -0
- package/dist/coding/services/catalog.d.ts +443 -0
- package/dist/coding/services/catalog.js +159 -0
- package/dist/coding/services/declaration.d.ts +11 -0
- package/dist/coding/services/declaration.js +114 -0
- package/dist/coding/services/note.d.ts +8 -0
- package/dist/coding/services/note.js +15 -0
- package/dist/coding/services/resolve.d.ts +25 -0
- package/dist/coding/services/resolve.js +50 -0
- package/dist/coding/services/wording.d.ts +25 -0
- package/dist/coding/services/wording.js +70 -0
- package/dist/coding-proxy/main.js +24 -4
- package/dist/coding-worker/artifact.d.ts +6 -0
- package/dist/coding-worker/debug-trace.d.ts +37 -0
- package/dist/coding-worker/debug-trace.js +117 -0
- package/dist/coding-worker/driver.d.ts +13 -1
- package/dist/coding-worker/driver.js +102 -13
- package/dist/coding-worker/errors.js +8 -0
- package/dist/coding-worker/main.js +10 -2
- package/dist/coding-worker/sdk.d.ts +2 -2
- package/dist/coding-worker/sdk.js +7 -2
- package/dist/coding-worker/types.d.ts +3 -0
- package/dist/config/providers.d.ts +66 -0
- package/dist/config/providers.js +137 -0
- package/dist/core/attribution.d.ts +101 -0
- package/dist/core/attribution.js +208 -0
- package/dist/core/budget-groups.d.ts +120 -10
- package/dist/core/budget-groups.js +133 -24
- package/dist/core/budget-wording.d.ts +22 -0
- package/dist/core/budget-wording.js +55 -0
- package/dist/core/coding-queue.d.ts +3 -0
- package/dist/core/coding-queue.js +6 -2
- package/dist/core/coding-service-status.d.ts +11 -0
- package/dist/core/coding-service-status.js +17 -0
- package/dist/core/cost-report.d.ts +88 -0
- package/dist/core/cost-report.js +248 -0
- package/dist/core/datastores.js +18 -2
- package/dist/core/db.d.ts +5 -1
- package/dist/core/db.js +8 -2
- package/dist/core/dispatch.d.ts +74 -7
- package/dist/core/dispatch.js +382 -47
- package/dist/core/engine-native.js +44 -14
- package/dist/core/glob.d.ts +10 -0
- package/dist/core/glob.js +33 -0
- package/dist/core/grants.d.ts +86 -0
- package/dist/core/grants.js +126 -0
- package/dist/core/host-events.d.ts +72 -0
- package/dist/core/host-events.js +597 -0
- package/dist/core/host-identity-links.d.ts +54 -0
- package/dist/core/host-identity-links.js +189 -0
- package/dist/core/host-status.d.ts +78 -0
- package/dist/core/host-status.js +228 -0
- package/dist/core/in-flight-runs.d.ts +8 -0
- package/dist/core/in-flight-runs.js +56 -0
- package/dist/core/issue-bridge.d.ts +60 -0
- package/dist/core/issue-bridge.js +189 -0
- package/dist/core/issue-dedupe.d.ts +70 -0
- package/dist/core/issue-dedupe.js +255 -0
- package/dist/core/issue-events.d.ts +42 -0
- package/dist/core/issue-events.js +155 -0
- package/dist/core/issue-status.d.ts +29 -0
- package/dist/core/issue-status.js +241 -0
- package/dist/core/issue-tracker-tools.d.ts +64 -0
- package/dist/core/issue-tracker-tools.js +850 -0
- package/dist/core/model-usage.d.ts +10 -0
- package/dist/core/model-usage.js +24 -0
- package/dist/core/provider-wording.d.ts +12 -0
- package/dist/core/provider-wording.js +44 -0
- package/dist/core/reconciler.d.ts +42 -2
- package/dist/core/reconciler.js +97 -2
- package/dist/core/repo-access.d.ts +99 -0
- package/dist/core/repo-access.js +136 -0
- package/dist/core/review-host-checks.d.ts +11 -0
- package/dist/core/review-host-checks.js +41 -0
- package/dist/core/review-host-tools.d.ts +47 -0
- package/dist/core/review-host-tools.js +354 -0
- package/dist/core/run-heartbeat.d.ts +27 -0
- package/dist/core/run-heartbeat.js +54 -0
- package/dist/core/run-pricing.d.ts +61 -0
- package/dist/core/run-pricing.js +56 -0
- package/dist/core/runner.d.ts +33 -8
- package/dist/core/runner.js +410 -62
- package/dist/core/scheduler.d.ts +4 -1
- package/dist/core/scheduler.js +3 -2
- package/dist/core/secrets.d.ts +11 -2
- package/dist/core/secrets.js +25 -6
- package/dist/core/self-defects.d.ts +80 -0
- package/dist/core/self-defects.js +180 -0
- package/dist/core/subagent-memory-tools.d.ts +1 -1
- package/dist/core/subagent-memory-tools.js +16 -3
- package/dist/core/tool-admin.d.ts +81 -0
- package/dist/core/tool-admin.js +129 -0
- package/dist/core/tool-names.d.ts +42 -0
- package/dist/core/tool-names.js +67 -0
- package/dist/core/untrusted-content.d.ts +32 -0
- package/dist/core/untrusted-content.js +72 -0
- package/dist/core/webhooks.d.ts +17 -2
- package/dist/core/webhooks.js +42 -3
- package/dist/generated/prisma/browser.d.ts +166 -0
- package/dist/generated/prisma/client.d.ts +166 -0
- package/dist/generated/prisma/commonInputTypes.d.ts +152 -52
- package/dist/generated/prisma/enums.d.ts +13 -0
- package/dist/generated/prisma/enums.js +12 -1
- package/dist/generated/prisma/internal/class.d.ts +242 -0
- package/dist/generated/prisma/internal/class.js +4 -4
- package/dist/generated/prisma/internal/prismaNamespace.d.ts +2571 -577
- package/dist/generated/prisma/internal/prismaNamespace.js +312 -6
- package/dist/generated/prisma/internal/prismaNamespaceBrowser.d.ts +328 -0
- package/dist/generated/prisma/internal/prismaNamespaceBrowser.js +312 -6
- package/dist/generated/prisma/models/Agent.d.ts +682 -1
- package/dist/generated/prisma/models/AgentIssueProject.d.ts +1838 -0
- package/dist/generated/prisma/models/AgentIssueProject.js +1 -0
- package/dist/generated/prisma/models/AgentRepository.d.ts +1425 -0
- package/dist/generated/prisma/models/AgentRepository.js +1 -0
- package/dist/generated/prisma/models/AgentTool.d.ts +95 -1
- package/dist/generated/prisma/models/AuthUser.d.ts +56 -1
- package/dist/generated/prisma/models/CodingAgentProfile.d.ts +258 -7
- package/dist/generated/prisma/models/CodingProxySession.d.ts +195 -2
- package/dist/generated/prisma/models/CodingRun.d.ts +1405 -95
- package/dist/generated/prisma/models/CodingRunServiceStatus.d.ts +1404 -0
- package/dist/generated/prisma/models/CodingRunServiceStatus.js +1 -0
- package/dist/generated/prisma/models/CodingService.d.ts +1348 -0
- package/dist/generated/prisma/models/CodingService.js +1 -0
- package/dist/generated/prisma/models/HostEventDelivery.d.ts +946 -0
- package/dist/generated/prisma/models/HostEventDelivery.js +1 -0
- package/dist/generated/prisma/models/HostIdentity.d.ts +1232 -0
- package/dist/generated/prisma/models/HostIdentity.js +1 -0
- package/dist/generated/prisma/models/HostIdentityLinkRequest.d.ts +1473 -0
- package/dist/generated/prisma/models/HostIdentityLinkRequest.js +1 -0
- package/dist/generated/prisma/models/IssueFingerprint.d.ts +1183 -0
- package/dist/generated/prisma/models/IssueFingerprint.js +1 -0
- package/dist/generated/prisma/models/IssuePullRequest.d.ts +1255 -0
- package/dist/generated/prisma/models/IssuePullRequest.js +1 -0
- package/dist/generated/prisma/models/ModelCatalogEntry.d.ts +1322 -0
- package/dist/generated/prisma/models/ModelCatalogEntry.js +1 -0
- package/dist/generated/prisma/models/Principal.d.ts +455 -0
- package/dist/generated/prisma/models/RegistryAllowance.d.ts +1148 -0
- package/dist/generated/prisma/models/RegistryAllowance.js +1 -0
- package/dist/generated/prisma/models/RegistryApprovedVersion.d.ts +1219 -0
- package/dist/generated/prisma/models/RegistryApprovedVersion.js +1 -0
- package/dist/generated/prisma/models/RegistryFetch.d.ts +1428 -0
- package/dist/generated/prisma/models/RegistryFetch.js +1 -0
- package/dist/generated/prisma/models/RegistryPlanRefusal.d.ts +1294 -0
- package/dist/generated/prisma/models/RegistryPlanRefusal.js +1 -0
- package/dist/generated/prisma/models/RegistryVersionFact.d.ts +1085 -0
- package/dist/generated/prisma/models/RegistryVersionFact.js +1 -0
- package/dist/generated/prisma/models/ResourceGrant.d.ts +1437 -0
- package/dist/generated/prisma/models/ResourceGrant.js +1 -0
- package/dist/generated/prisma/models/Run.d.ts +1402 -82
- package/dist/generated/prisma/models/RunAttribution.d.ts +1259 -0
- package/dist/generated/prisma/models/RunAttribution.js +1 -0
- package/dist/generated/prisma/models/RunHostCheck.d.ts +1239 -0
- package/dist/generated/prisma/models/RunHostCheck.js +1 -0
- package/dist/generated/prisma/models/RunHostStatus.d.ts +1315 -0
- package/dist/generated/prisma/models/RunHostStatus.js +1 -0
- package/dist/generated/prisma/models/RunIssueStatus.d.ts +1199 -0
- package/dist/generated/prisma/models/RunIssueStatus.js +1 -0
- package/dist/generated/prisma/models/RunModelUsage.d.ts +1316 -0
- package/dist/generated/prisma/models/RunModelUsage.js +1 -0
- package/dist/generated/prisma/models/Tool.d.ts +15 -3
- package/dist/generated/prisma/models/WorkItem.d.ts +1408 -0
- package/dist/generated/prisma/models/WorkItem.js +1 -0
- package/dist/generated/prisma/models.d.ts +22 -0
- package/dist/help/build.d.ts +1 -0
- package/dist/help/build.js +9 -0
- package/dist/help/catalog.d.ts +24 -0
- package/dist/help/catalog.js +160 -0
- package/dist/help/cli.d.ts +2 -0
- package/dist/help/cli.js +65 -0
- package/dist/help/runtime.d.ts +3 -0
- package/dist/help/runtime.js +44 -0
- package/dist/help/search.d.ts +9 -0
- package/dist/help/search.js +104 -0
- package/dist/help-index.json +999 -0
- package/dist/import/cli-args.js +3 -2
- package/dist/import/create.d.ts +5 -0
- package/dist/import/create.js +67 -13
- package/dist/import/index.js +19 -9
- package/dist/import/neutral-schema.d.ts +18 -18
- package/dist/knowledge/check.d.ts +13 -0
- package/dist/knowledge/check.js +69 -0
- package/dist/knowledge/cli.d.ts +14 -0
- package/dist/knowledge/cli.js +67 -0
- package/dist/knowledge/concept.d.ts +54 -0
- package/dist/knowledge/concept.js +78 -0
- package/dist/knowledge/note.d.ts +11 -0
- package/dist/knowledge/note.js +39 -0
- package/dist/knowledge/relevance.d.ts +11 -0
- package/dist/knowledge/relevance.js +14 -0
- package/dist/knowledge/span-hash.d.ts +3 -0
- package/dist/knowledge/span-hash.js +16 -0
- package/dist/mcp/auth/access.d.ts +72 -0
- package/dist/mcp/auth/access.js +58 -0
- package/dist/mcp/auth/grants-cli.d.ts +149 -0
- package/dist/mcp/auth/grants-cli.js +518 -0
- package/dist/mcp/auth/host-account-cli.d.ts +2 -0
- package/dist/mcp/auth/host-account-cli.js +47 -0
- package/dist/mcp/auth/ownership.d.ts +25 -51
- package/dist/mcp/auth/ownership.js +19 -14
- package/dist/mcp/auth/repo-authorization.d.ts +22 -0
- package/dist/mcp/auth/repo-authorization.js +51 -0
- package/dist/mcp/auth/resource-server.d.ts +33 -2
- package/dist/mcp/auth/resource-server.js +99 -3
- package/dist/mcp/auth/self-hosted/browser.js +2 -2
- package/dist/mcp/auth/self-hosted/cli.js +47 -13
- package/dist/mcp/auth/self-hosted/credentials.d.ts +23 -5
- package/dist/mcp/auth/self-hosted/credentials.js +62 -4
- package/dist/mcp/auth/self-hosted/session.d.ts +7 -6
- package/dist/mcp/context.d.ts +30 -1
- package/dist/mcp/errors.d.ts +25 -6
- package/dist/mcp/errors.js +98 -0
- package/dist/mcp/host-events/deliveries.d.ts +9 -0
- package/dist/mcp/host-events/deliveries.js +17 -0
- package/dist/mcp/host-events/github-ingress.d.ts +38 -0
- package/dist/mcp/host-events/github-ingress.js +83 -0
- package/dist/mcp/host-events/github-user-callback.d.ts +18 -0
- package/dist/mcp/host-events/github-user-callback.js +50 -0
- package/dist/mcp/host-events/jira-ingress.d.ts +29 -0
- package/dist/mcp/host-events/jira-ingress.js +92 -0
- package/dist/mcp/index.d.ts +10 -1
- package/dist/mcp/index.js +187 -19
- package/dist/mcp/server.js +32 -11
- package/dist/mcp/tools/agents.js +519 -50
- package/dist/mcp/tools/budget-groups.js +3 -3
- package/dist/mcp/tools/cost-report.d.ts +8 -0
- package/dist/mcp/tools/cost-report.js +60 -0
- package/dist/mcp/tools/datastore.js +22 -15
- package/dist/mcp/tools/grants.d.ts +2 -0
- package/dist/mcp/tools/grants.js +239 -0
- package/dist/mcp/tools/help.d.ts +5 -0
- package/dist/mcp/tools/help.js +67 -0
- package/dist/mcp/tools/host-accounts.d.ts +2 -0
- package/dist/mcp/tools/host-accounts.js +114 -0
- package/dist/mcp/tools/issue-projects.d.ts +2 -0
- package/dist/mcp/tools/issue-projects.js +238 -0
- package/dist/mcp/tools/memory.d.ts +7 -1
- package/dist/mcp/tools/memory.js +5 -5
- package/dist/mcp/tools/model-catalog.d.ts +22 -0
- package/dist/mcp/tools/model-catalog.js +423 -0
- package/dist/mcp/tools/repositories.d.ts +2 -0
- package/dist/mcp/tools/repositories.js +182 -0
- package/dist/mcp/tools/runs.d.ts +6 -0
- package/dist/mcp/tools/runs.js +60 -6
- package/dist/mcp/tools/scheduling.js +3 -6
- package/dist/mcp/tools/secrets.js +18 -6
- package/dist/mcp/tools/services.d.ts +2 -0
- package/dist/mcp/tools/services.js +222 -0
- package/dist/mcp/tools/subagents.js +52 -16
- package/dist/mcp/tools/tools.d.ts +41 -0
- package/dist/mcp/tools/tools.js +201 -40
- package/dist/mcp/tools/trigger.js +65 -7
- package/dist/mcp/tools/webhooks.js +15 -4
- package/dist/mcp/transport/streamable-http.d.ts +15 -0
- package/dist/mcp/transport/streamable-http.js +55 -3
- package/dist/mcp/webhooks/ingress.d.ts +2 -1
- package/dist/mcp/webhooks/ingress.js +9 -2
- package/dist/providers/auth/delegating.d.ts +19 -0
- package/dist/providers/auth/delegating.js +71 -0
- package/dist/providers/auth/index.d.ts +2 -0
- package/dist/providers/auth/index.js +11 -0
- package/dist/providers/auth/self-hosted.d.ts +10 -2
- package/dist/providers/auth/self-hosted.js +55 -6
- package/dist/providers/auth/types.d.ts +6 -0
- package/dist/providers/coding-proxy/memory-ledger.d.ts +4 -1
- package/dist/providers/coding-proxy/memory-ledger.js +31 -3
- package/dist/providers/coding-proxy/metering.d.ts +6 -1
- package/dist/providers/coding-proxy/metering.js +41 -4
- package/dist/providers/coding-proxy/prisma-ledger.d.ts +17 -0
- package/dist/providers/coding-proxy/prisma-ledger.js +106 -8
- package/dist/providers/coding-proxy/proxy.d.ts +25 -2
- package/dist/providers/coding-proxy/proxy.js +561 -38
- package/dist/providers/coding-proxy/registry/audit.d.ts +131 -0
- package/dist/providers/coding-proxy/registry/audit.js +380 -0
- package/dist/providers/coding-proxy/registry/bounded-fetch.d.ts +16 -0
- package/dist/providers/coding-proxy/registry/bounded-fetch.js +77 -0
- package/dist/providers/coding-proxy/registry/plan.d.ts +58 -0
- package/dist/providers/coding-proxy/registry/plan.js +304 -0
- package/dist/providers/coding-proxy/registry/prisma-store.d.ts +34 -0
- package/dist/providers/coding-proxy/registry/prisma-store.js +151 -0
- package/dist/providers/coding-proxy/registry/service.d.ts +213 -0
- package/dist/providers/coding-proxy/registry/service.js +1137 -0
- package/dist/providers/coding-proxy/registry/store.d.ts +127 -0
- package/dist/providers/coding-proxy/registry/store.js +70 -0
- package/dist/providers/coding-proxy/runtime.d.ts +17 -0
- package/dist/providers/coding-proxy/runtime.js +63 -0
- package/dist/providers/coding-proxy/secure-fetch.js +0 -1
- package/dist/providers/coding-proxy/server.d.ts +11 -0
- package/dist/providers/coding-proxy/server.js +163 -0
- package/dist/providers/coding-proxy/types.d.ts +33 -1
- package/dist/providers/coding-proxy/types.js +12 -1
- package/dist/providers/engine/types.d.ts +29 -0
- package/dist/providers/executor/build.d.ts +2 -2
- package/dist/providers/executor/composition.d.ts +3 -0
- package/dist/providers/executor/composition.js +28 -1
- package/dist/providers/executor/container.d.ts +134 -4
- package/dist/providers/executor/container.js +394 -37
- package/dist/providers/executor/dbos.d.ts +4 -3
- package/dist/providers/executor/dbos.js +7 -5
- package/dist/providers/executor/in-process.d.ts +2 -3
- package/dist/providers/executor/routing.d.ts +13 -0
- package/dist/providers/executor/routing.js +18 -0
- package/dist/providers/executor/types.d.ts +34 -0
- package/dist/providers/issue-tracker/adf.d.ts +31 -0
- package/dist/providers/issue-tracker/adf.js +181 -0
- package/dist/providers/issue-tracker/index.d.ts +5 -0
- package/dist/providers/issue-tracker/index.js +12 -0
- package/dist/providers/issue-tracker/jira-client.d.ts +41 -0
- package/dist/providers/issue-tracker/jira-client.js +151 -0
- package/dist/providers/issue-tracker/jira-events.d.ts +3 -0
- package/dist/providers/issue-tracker/jira-events.js +98 -0
- package/dist/providers/issue-tracker/jira.d.ts +116 -0
- package/dist/providers/issue-tracker/jira.js +502 -0
- package/dist/providers/issue-tracker/types.d.ts +269 -0
- package/dist/providers/issue-tracker/types.js +16 -0
- package/dist/providers/jobs/claude-tool-setup.d.ts +23 -0
- package/dist/providers/jobs/claude-tool-setup.js +50 -0
- package/dist/providers/jobs/collect-prune.d.ts +7 -0
- package/dist/providers/jobs/collect-prune.js +27 -0
- package/dist/providers/jobs/docker-isolation.d.ts +48 -3
- package/dist/providers/jobs/docker-isolation.js +213 -22
- package/dist/providers/jobs/docker-services.d.ts +35 -0
- package/dist/providers/jobs/docker-services.js +191 -0
- package/dist/providers/jobs/docker.d.ts +53 -2
- package/dist/providers/jobs/docker.js +307 -32
- package/dist/providers/jobs/fake-kubernetes-api.d.ts +1 -0
- package/dist/providers/jobs/fake-kubernetes-api.js +12 -3
- package/dist/providers/jobs/kubernetes-isolation.d.ts +17 -2
- package/dist/providers/jobs/kubernetes-isolation.js +238 -57
- package/dist/providers/jobs/kubernetes-platform.d.ts +10 -4
- package/dist/providers/jobs/kubernetes-platform.js +11 -5
- package/dist/providers/jobs/kubernetes-preflight.js +3 -0
- package/dist/providers/jobs/kubernetes.d.ts +24 -2
- package/dist/providers/jobs/kubernetes.js +153 -22
- package/dist/providers/jobs/service-state.d.ts +22 -0
- package/dist/providers/jobs/service-state.js +17 -0
- package/dist/providers/jobs/types.d.ts +18 -0
- package/dist/providers/llm/anthropic.d.ts +3 -3
- package/dist/providers/llm/anthropic.js +3 -5
- package/dist/providers/llm/bedrock.d.ts +3 -3
- package/dist/providers/llm/bedrock.js +3 -8
- package/dist/providers/llm/catalog-lookup.d.ts +10 -0
- package/dist/providers/llm/catalog-lookup.js +15 -0
- package/dist/providers/llm/catalog-shipped.d.ts +18 -0
- package/dist/providers/llm/catalog-shipped.js +197 -0
- package/dist/providers/llm/catalog-store.d.ts +58 -0
- package/dist/providers/llm/catalog-store.js +138 -0
- package/dist/providers/llm/catalog-types.d.ts +66 -0
- package/dist/providers/llm/catalog-types.js +64 -0
- package/dist/providers/llm/catalog.d.ts +61 -0
- package/dist/providers/llm/catalog.js +147 -0
- package/dist/providers/llm/claude-messages.d.ts +5 -1
- package/dist/providers/llm/claude-messages.js +1 -0
- package/dist/providers/llm/claude-provider.d.ts +10 -12
- package/dist/providers/llm/claude-provider.js +15 -6
- package/dist/providers/llm/index.d.ts +9 -6
- package/dist/providers/llm/index.js +8 -5
- package/dist/providers/llm/openai.d.ts +14 -5
- package/dist/providers/llm/openai.js +24 -14
- package/dist/providers/llm/pricing-core.d.ts +5 -3
- package/dist/providers/llm/registration.js +8 -12
- package/dist/providers/llm/routing.d.ts +21 -11
- package/dist/providers/llm/routing.js +47 -13
- package/dist/providers/llm/types.d.ts +12 -0
- package/dist/providers/llm/types.js +8 -1
- package/dist/providers/review-host/diff-lines.d.ts +16 -0
- package/dist/providers/review-host/diff-lines.js +59 -0
- package/dist/providers/review-host/github-events.d.ts +6 -0
- package/dist/providers/review-host/github-events.js +226 -0
- package/dist/providers/review-host/github-user-auth.d.ts +38 -0
- package/dist/providers/review-host/github-user-auth.js +128 -0
- package/dist/providers/review-host/github.d.ts +57 -0
- package/dist/providers/review-host/github.js +568 -0
- package/dist/providers/review-host/index.d.ts +12 -0
- package/dist/providers/review-host/index.js +30 -0
- package/dist/providers/review-host/review-format.d.ts +19 -0
- package/dist/providers/review-host/review-format.js +59 -0
- package/dist/providers/review-host/types.d.ts +309 -0
- package/dist/providers/review-host/types.js +24 -0
- package/dist/providers/vcs/git.d.ts +16 -6
- package/dist/providers/vcs/git.js +78 -34
- package/dist/providers/vcs/github.d.ts +94 -3
- package/dist/providers/vcs/github.js +223 -17
- package/dist/providers/vcs/types.d.ts +48 -4
- package/dist/quickstart/index.d.ts +2 -0
- package/dist/quickstart/index.js +36 -8
- package/dist/sandbox/fetch-policy.d.ts +20 -2
- package/dist/sandbox/fetch-policy.js +64 -3
- package/dist/sandbox/host-functions.d.ts +9 -1
- package/dist/sandbox/host-functions.js +12 -5
- package/dist/serve.js +8 -2
- package/dist/viewer/api-schema.d.ts +2757 -0
- package/dist/viewer/api-schema.js +165 -0
- package/dist/viewer/build-schemas.d.ts +2 -0
- package/dist/viewer/build-schemas.js +18 -0
- package/dist/viewer/event-bus.d.ts +38 -0
- package/dist/viewer/event-bus.js +232 -0
- package/dist/viewer/graph.d.ts +40 -0
- package/dist/viewer/graph.js +243 -0
- package/dist/viewer/http.d.ts +30 -0
- package/dist/viewer/http.js +133 -0
- package/dist/viewer/run-detail.d.ts +4 -0
- package/dist/viewer/run-detail.js +61 -0
- package/dist/wardby-bin.js +11 -0
- package/docs/README.md +40 -0
- package/docs/agent-recipes.md +383 -0
- package/docs/architecture-runtime.md +90 -0
- package/docs/assets/brand/wardby-icon-512.png +0 -0
- package/docs/assets/brand/wardby-icon.svg +16 -0
- package/docs/assets/brand/wardby-mascot-profile-512.png +0 -0
- package/docs/assets/brand/wardby-mascot.png +0 -0
- package/docs/assets/brand/wardby-mascot.svg +5 -0
- package/docs/assets/wardby-workflow.svg +106 -0
- package/docs/code-review-agents.md +508 -0
- package/docs/coding-agent-setup.md +175 -0
- package/docs/coding-packages.md +455 -0
- package/docs/coding-services.md +300 -0
- package/docs/coding-worker-byo-images.md +98 -0
- package/docs/coding-worker-isolation.md +1068 -0
- package/docs/getting-started-gke.md +632 -0
- package/docs/getting-started-identity-provider.md +319 -0
- package/docs/getting-started.md +147 -0
- package/docs/jira-agents.md +649 -0
- package/docs/knowledge.md +387 -0
- package/docs/models.md +221 -0
- package/docs/observability.md +53 -0
- package/docs/release-verification.md +66 -0
- package/docs/security-deployment.md +667 -0
- package/docs/viewer-api.md +142 -0
- package/help/admin-viewer.md +39 -0
- package/help/agent-recipes.md +173 -0
- package/help/architecture-agent.md +189 -0
- package/help/builder-agent.md +80 -0
- package/help/code-review-agents.md +37 -0
- package/help/coding-packages.md +31 -0
- package/help/coding-services.md +71 -0
- package/help/cost-attribution.md +67 -0
- package/help/creating-agents.md +90 -0
- package/help/deploy-gke.md +45 -0
- package/help/deployment-targets.md +39 -0
- package/help/errors/budget-group-exhausted.md +25 -0
- package/help/errors/docker-isolation-unsupported.md +26 -0
- package/help/errors/model-unavailable.md +63 -0
- package/help/errors/protected-path.md +45 -0
- package/help/errors/repo-access.md +27 -0
- package/help/errors/service-declaration-invalid.md +27 -0
- package/help/errors/service-declaration-unavailable.md +25 -0
- package/help/errors/service-launcher-unsupported.md +30 -0
- package/help/errors/service-not-allowed.md +27 -0
- package/help/errors/service-unknown.md +23 -0
- package/help/errors/service-unready.md +49 -0
- package/help/getting-started.md +32 -0
- package/help/github.md +48 -0
- package/help/identity-and-access.md +44 -0
- package/help/jira.md +135 -0
- package/help/knowledge.md +47 -0
- package/help/mcp.md +30 -0
- package/help/models.md +90 -0
- package/help/native-capabilities.md +30 -0
- package/help/observability.md +41 -0
- package/help/operating-agents.md +36 -0
- package/help/security.md +27 -0
- package/help/troubleshooting/budgets.md +32 -0
- package/help/troubleshooting/coding-workers.md +47 -0
- package/help/troubleshooting/repository-access.md +27 -0
- package/package.json +14 -3
- package/prisma/migrations/20260925010000_coding_collect_exclude/migration.sql +8 -0
- package/prisma/migrations/20260925015000_allowed_egress_default/migration.sql +8 -0
- package/prisma/migrations/20260925020000_coding_package_registry/migration.sql +55 -0
- package/prisma/migrations/20260925030000_tool_name_per_owner/migration.sql +11 -0
- package/prisma/migrations/20260926010000_run_trigger_host_event/migration.sql +7 -0
- package/prisma/migrations/20260926020000_code_review_hosts/migration.sql +53 -0
- package/prisma/migrations/20260926030000_auth_user_roles/migration.sql +9 -0
- package/prisma/migrations/20260926040000_agent_effort/migration.sql +6 -0
- package/prisma/migrations/20260926050000_registry_lockfile_plan/migration.sql +32 -0
- package/prisma/migrations/20260926050000_repo_access_authorization/migration.sql +82 -0
- package/prisma/migrations/20260926060000_registry_plan_refusal/migration.sql +19 -0
- package/prisma/migrations/20260926100000_registry_plan_refusal_published_at/migration.sql +5 -0
- package/prisma/migrations/20260926190000_run_host_status/migration.sql +18 -0
- package/prisma/migrations/20260926210000_run_host_status_at_dispatch/migration.sql +5 -0
- package/prisma/migrations/20260927010000_resource_grants/migration.sql +72 -0
- package/prisma/migrations/20260927020000_proxy_session_budget_exhausted/migration.sql +3 -0
- package/prisma/migrations/20260927030000_coding_debug_trace/migration.sql +4 -0
- package/prisma/migrations/20260927040000_proxy_session_upstream_failure/migration.sql +3 -0
- package/prisma/migrations/20260927050000_coding_run_services/migration.sql +74 -0
- package/prisma/migrations/20260928000000_coding_run_tool_image/migration.sql +5 -0
- package/prisma/migrations/20260930000000_jira_issue_projects/migration.sql +34 -0
- package/prisma/migrations/20261001000000_jira_phase2_allowlists/migration.sql +3 -0
- package/prisma/migrations/20261001010000_jira_link_types_allowlist/migration.sql +2 -0
- package/prisma/migrations/20261002000000_jira_coding_bridge/migration.sql +28 -0
- package/prisma/migrations/20261002010000_jira_issue_creation/migration.sql +25 -0
- package/prisma/migrations/20261003000000_issue_cost_attribution/migration.sql +56 -0
- package/prisma/migrations/20261003010000_coding_run_service_status/migration.sql +23 -0
- package/prisma/migrations/20261003020000_viewer_notify/migration.sql +54 -0
- package/prisma/migrations/20261003030000_viewer_notify_fixes/migration.sql +47 -0
- package/prisma/migrations/20261003040000_viewer_indexes/migration.sql +12 -0
- package/prisma/migrations/20261004000000_model_catalog/migration.sql +26 -0
- package/prisma/schema.prisma +663 -21
- package/dist/mcp/tools/models.d.ts +0 -8
- package/dist/mcp/tools/models.js +0 -15
- package/dist/providers/llm/pricing-anthropic.d.ts +0 -4
- package/dist/providers/llm/pricing-anthropic.js +0 -30
- package/dist/providers/llm/pricing-bedrock-claude.d.ts +0 -12
- package/dist/providers/llm/pricing-bedrock-claude.js +0 -37
- package/dist/providers/llm/pricing.d.ts +0 -30
- package/dist/providers/llm/pricing.js +0 -74
|
@@ -1,14 +1,27 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
import { mkdir, realpath, rename, rm, writeFile } from "node:fs/promises";
|
|
3
3
|
import { join, resolve, sep } from "node:path";
|
|
4
|
+
import { withBaseCommit } from "../../coding/base-commit.js";
|
|
5
|
+
import { normalizeCollectExclusions } from "../../coding/collect-exclude.js";
|
|
6
|
+
import { codingProviderForModelProvider } from "../../coding/provider.js";
|
|
4
7
|
import { CodingProfileSchema } from "../../coding/profile.js";
|
|
8
|
+
import { parseStoredServices, storedServiceNames, workerServices } from "../../coding/services/catalog.js";
|
|
9
|
+
import { MAX_SERVICE_DECLARATION_BYTES, SERVICE_DECLARATION_PATH } from "../../coding/services/declaration.js";
|
|
10
|
+
import { SERVICE_UNREADY_CATEGORY, SERVICE_UNREADY_ERROR, serviceUnreadyName, serviceUnreadySentence, } from "../../coding/services/wording.js";
|
|
11
|
+
import { PROTECTED_PATH_CATEGORY, protectedPathFromError, protectedPathSentence, } from "../../coding/protected-path-wording.js";
|
|
12
|
+
import { budgetSentence } from "../../core/budget-wording.js";
|
|
13
|
+
import { fileSelfDefect } from "../../core/self-defects.js";
|
|
14
|
+
import { classifyProviderFailure, providerClassOfCategory, providerSentence, } from "../../core/provider-wording.js";
|
|
5
15
|
import { logger } from "../../core/logger.js";
|
|
16
|
+
import { requiredLevel } from "../../core/repo-access.js";
|
|
6
17
|
import { ensurePrivateDirectory } from "../../core/private-directory.js";
|
|
7
18
|
import { codingRunObserver } from "../../coding/observability.js";
|
|
8
19
|
import { CODING_PROTOCOL_VERSION, CodingRunResultSchema, CodingTaskInputSchema, parseCodingAgentOutputJson, redactAndTruncate, } from "../../coding/protocol.js";
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import { isImmutableDockerImage } from "../jobs/docker-isolation.js";
|
|
20
|
+
import { parseStoredEntry } from "../llm/catalog-types.js";
|
|
21
|
+
import { resolveCodingEntry } from "../../core/run-pricing.js";
|
|
22
|
+
import { CODING_PROXY_ALIAS, CODING_PROXY_PORT, isImmutableDockerImage } from "../jobs/docker-isolation.js";
|
|
23
|
+
import { normalizeRegistryLockfiles } from "../../coding/registry/lockfiles.js";
|
|
24
|
+
import { ISSUE_KEY, ISSUE_TRACKER_NAMES } from "../issue-tracker/types.js";
|
|
12
25
|
import { HEARTBEAT_TIMEOUT_MS } from "../../core/timing.js";
|
|
13
26
|
const containerLog = logger.child({ module: "container-executor" });
|
|
14
27
|
const TERMINAL_STATUSES = new Set(["succeeded", "failed", "refused", "lost", "budget_exhausted", "cancelled"]);
|
|
@@ -33,6 +46,19 @@ function proxyProtocol(provider) {
|
|
|
33
46
|
return "anthropic-messages";
|
|
34
47
|
throw new Error("coding_provider_unsupported");
|
|
35
48
|
}
|
|
49
|
+
/**
|
|
50
|
+
* Claude Code's commands run in its tool runner, which never reports back through the agent, so the
|
|
51
|
+
* host rewrites registry-proxy download URLs in collected lockfiles itself (the Codex driver does it
|
|
52
|
+
* in the worker). Best effort and symlink-safe (lockfiles.ts); a Codex workspace is left alone.
|
|
53
|
+
*/
|
|
54
|
+
export async function normalizeCollectedLockfiles(provider, workspacePath) {
|
|
55
|
+
if (provider !== "claude-code")
|
|
56
|
+
return [];
|
|
57
|
+
return normalizeRegistryLockfiles({
|
|
58
|
+
workspace: workspacePath,
|
|
59
|
+
proxyBaseUrl: `http://${CODING_PROXY_ALIAS}:${CODING_PROXY_PORT}`,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
36
62
|
export class PrismaContainerExecutionStore {
|
|
37
63
|
db;
|
|
38
64
|
options;
|
|
@@ -40,10 +66,22 @@ export class PrismaContainerExecutionStore {
|
|
|
40
66
|
this.db = db;
|
|
41
67
|
this.options = options;
|
|
42
68
|
}
|
|
69
|
+
/** Best effort (bounded, never throws): only called by the write that made the row terminal, so it files once. */
|
|
70
|
+
async selfDefect(run) {
|
|
71
|
+
if (this.hasTrackers)
|
|
72
|
+
await fileSelfDefect(this.db, this.options.issueTrackers, run);
|
|
73
|
+
}
|
|
74
|
+
/** An empty registry (no Jira site configured) means no self-defect queries at all. */
|
|
75
|
+
get hasTrackers() {
|
|
76
|
+
return Object.values(this.options.issueTrackers ?? {}).some(Boolean);
|
|
77
|
+
}
|
|
43
78
|
async load(runId) {
|
|
44
79
|
const row = await this.db.run.findUnique({
|
|
45
80
|
where: { id: runId },
|
|
46
|
-
include: {
|
|
81
|
+
include: {
|
|
82
|
+
agent: { include: { codingProfile: true, budgetGroup: { select: { name: true } } } },
|
|
83
|
+
codingRun: { include: { proxySession: true } },
|
|
84
|
+
},
|
|
47
85
|
});
|
|
48
86
|
if (!row?.codingRun)
|
|
49
87
|
return null;
|
|
@@ -60,10 +98,14 @@ export class PrismaContainerExecutionStore {
|
|
|
60
98
|
provider: row.codingRun.provider,
|
|
61
99
|
model: row.codingRun.model,
|
|
62
100
|
timeoutSec: row.codingRun.timeoutSec,
|
|
63
|
-
allowedEgress: row.codingRun.allowedEgress,
|
|
64
101
|
protectedPaths: row.codingRun.protectedPaths,
|
|
102
|
+
collectExclude: row.codingRun.collectExclude,
|
|
65
103
|
rootCodingRunId: row.codingRun.rootCodingRunId,
|
|
104
|
+
issueProvider: row.codingRun.issueProvider,
|
|
105
|
+
issueKey: row.codingRun.issueKey,
|
|
66
106
|
budgetUsd: Number(row.codingRun.budgetReservedUsd),
|
|
107
|
+
agentBudgetUsd: Number(row.agent.budgetUsd),
|
|
108
|
+
budgetGroupName: row.agent.budgetGroup?.name ?? null,
|
|
67
109
|
tokensIn: row.tokensIn,
|
|
68
110
|
tokensOut: row.tokensOut,
|
|
69
111
|
costUsd: Number(row.costUsd),
|
|
@@ -74,7 +116,15 @@ export class PrismaContainerExecutionStore {
|
|
|
74
116
|
proxySessionId: row.codingRun.proxySession?.id ?? null,
|
|
75
117
|
result: row.codingRun.result,
|
|
76
118
|
workerImage: row.codingRun.workerImage,
|
|
119
|
+
toolImage: row.codingRun.toolImage,
|
|
77
120
|
workspaceDiskMb: row.codingRun.workspaceDiskMb,
|
|
121
|
+
debugTrace: row.codingRun.debugTrace,
|
|
122
|
+
services: row.codingRun.services,
|
|
123
|
+
failureCategory: row.codingRun.failureCategory,
|
|
124
|
+
profileRepository: row.agent.codingProfile?.repository ?? null,
|
|
125
|
+
repositoryAuthorizedVia: row.agent.codingProfile?.repositoryAuthorizedVia ?? null,
|
|
126
|
+
pricingVersion: row.pricingVersion,
|
|
127
|
+
pricingSnapshot: row.pricingSnapshot,
|
|
78
128
|
};
|
|
79
129
|
}
|
|
80
130
|
async claimProvisioning(runId, claimId) {
|
|
@@ -187,13 +237,14 @@ export class PrismaContainerExecutionStore {
|
|
|
187
237
|
});
|
|
188
238
|
}
|
|
189
239
|
async complete(runId, status, result) {
|
|
190
|
-
|
|
240
|
+
const finishedAt = new Date();
|
|
241
|
+
const finished = await this.db.$transaction(async (tx) => {
|
|
191
242
|
const run = await tx.run.findUnique({ where: { id: runId }, include: { codingRun: true } });
|
|
192
243
|
if (!run?.codingRun)
|
|
193
244
|
throw new Error("coding_run_not_found");
|
|
194
245
|
if (TERMINAL_STATUSES.has(run.status)) {
|
|
195
246
|
if (run.codingRun.result && stableJson(run.codingRun.result) === stableJson(result))
|
|
196
|
-
return;
|
|
247
|
+
return null;
|
|
197
248
|
throw new Error("coding_run_terminal_conflict");
|
|
198
249
|
}
|
|
199
250
|
await tx.codingRun.update({
|
|
@@ -205,26 +256,40 @@ export class PrismaContainerExecutionStore {
|
|
|
205
256
|
data: {
|
|
206
257
|
status,
|
|
207
258
|
finalText: result.summary,
|
|
208
|
-
finishedAt
|
|
259
|
+
finishedAt,
|
|
209
260
|
heartbeatAt: new Date(),
|
|
210
261
|
error: null,
|
|
211
262
|
},
|
|
212
263
|
});
|
|
264
|
+
return { id: runId, agentId: run.agentId };
|
|
213
265
|
});
|
|
266
|
+
if (finished && status === "budget_exhausted") {
|
|
267
|
+
await this.selfDefect({ ...finished, status, error: null, finishedAt });
|
|
268
|
+
}
|
|
214
269
|
}
|
|
215
270
|
async terminate(runId, status, error, audit) {
|
|
216
|
-
|
|
271
|
+
const finishedAt = new Date();
|
|
272
|
+
const agentId = await this.db.$transaction(async (tx) => {
|
|
217
273
|
const updated = await tx.run.updateMany({
|
|
218
274
|
where: { id: runId, status: { in: ["pending", "running"] } },
|
|
219
|
-
data: { status, error, finishedAt
|
|
275
|
+
data: { status, error, finishedAt, heartbeatAt: new Date() },
|
|
220
276
|
});
|
|
221
|
-
if (updated.count
|
|
277
|
+
if (!updated.count)
|
|
278
|
+
return null;
|
|
279
|
+
if (audit) {
|
|
222
280
|
await tx.codingRun.update({
|
|
223
281
|
where: { runId },
|
|
224
282
|
data: { failureCategory: audit.failureCategory, diagnosticId: audit.diagnosticId },
|
|
225
283
|
});
|
|
226
284
|
}
|
|
285
|
+
if (!this.hasTrackers)
|
|
286
|
+
return null;
|
|
287
|
+
const row = await tx.run.findUnique({ where: { id: runId }, select: { agentId: true } });
|
|
288
|
+
return row?.agentId ?? null;
|
|
227
289
|
});
|
|
290
|
+
// After commit, so the failure category is readable; cancelled/refused are skipped inside fileSelfDefect.
|
|
291
|
+
if (agentId)
|
|
292
|
+
await this.selfDefect({ id: runId, agentId, status, error, finishedAt });
|
|
228
293
|
}
|
|
229
294
|
}
|
|
230
295
|
/** Plain capabilities live only for the short provisioning window. */
|
|
@@ -247,6 +312,9 @@ export class RunCapabilityVault {
|
|
|
247
312
|
}
|
|
248
313
|
class PreflightError extends Error {
|
|
249
314
|
}
|
|
315
|
+
/** A refusal by repository authorization; its message is the failure category's prefix (repo_access_*). */
|
|
316
|
+
class RepoAccessError extends PreflightError {
|
|
317
|
+
}
|
|
250
318
|
export class ContainerExecutor {
|
|
251
319
|
options;
|
|
252
320
|
artifactRoot;
|
|
@@ -273,6 +341,12 @@ export class ContainerExecutor {
|
|
|
273
341
|
if (image !== undefined && !isImmutableDockerImage(image))
|
|
274
342
|
throw new Error("coding_worker_image_invalid");
|
|
275
343
|
}
|
|
344
|
+
for (const versions of Object.values(options.claudeToolRunnerImages ?? {})) {
|
|
345
|
+
for (const image of Object.values(versions)) {
|
|
346
|
+
if (!isImmutableDockerImage(image))
|
|
347
|
+
throw new Error("coding_worker_image_invalid");
|
|
348
|
+
}
|
|
349
|
+
}
|
|
276
350
|
if (!/^[A-Za-z][A-Za-z0-9_.:-]{0,199}$/.test(options.credentialRef)) {
|
|
277
351
|
throw new Error("coding_credential_ref_invalid");
|
|
278
352
|
}
|
|
@@ -397,6 +471,9 @@ export class ContainerExecutor {
|
|
|
397
471
|
if ((await this.options.store.claimProvisioning(runId, claimId)) !== "claimed")
|
|
398
472
|
return;
|
|
399
473
|
}
|
|
474
|
+
// A launched run (recovered after a restart) is in flight: a transient
|
|
475
|
+
// host error is retried once before it is stopped.
|
|
476
|
+
await this.authorizeRepository(run, { inFlight: Boolean(handle) });
|
|
400
477
|
try {
|
|
401
478
|
workspace = await this.options.vcs.recoverWorkspace(prepared);
|
|
402
479
|
if (!workspace && handle)
|
|
@@ -425,11 +502,12 @@ export class ContainerExecutor {
|
|
|
425
502
|
allowedModels: [run.model],
|
|
426
503
|
deadlineAt,
|
|
427
504
|
budgetUsd: run.budgetUsd,
|
|
505
|
+
terms: this.modelTerms(run),
|
|
428
506
|
});
|
|
429
507
|
spendEnabled = true;
|
|
430
508
|
sessionId = session.id;
|
|
431
509
|
this.options.capabilities.set(runId, session.capability);
|
|
432
|
-
const inputArtifact = await this.writeInput(run, deadlineAt);
|
|
510
|
+
const inputArtifact = await this.writeInput(run, deadlineAt, workspace.baseCommit);
|
|
433
511
|
const beforeLaunch = await this.requireCurrent(runId);
|
|
434
512
|
if (TERMINAL_STATUSES.has(beforeLaunch.status))
|
|
435
513
|
throw new Error("coding_run_no_longer_active");
|
|
@@ -474,8 +552,13 @@ export class ContainerExecutor {
|
|
|
474
552
|
await this.options.sessions.cancelSession(sessionId).catch(() => undefined);
|
|
475
553
|
if (handle)
|
|
476
554
|
await this.options.jobs.stop(handle, "executor_failure").catch(() => undefined);
|
|
477
|
-
const
|
|
478
|
-
const
|
|
555
|
+
const outOfBudget = spendEnabled && (await this.outOfBudget(sessionId));
|
|
556
|
+
const status = outOfBudget
|
|
557
|
+
? "budget_exhausted"
|
|
558
|
+
: !spendEnabled && error instanceof PreflightError
|
|
559
|
+
? "refused"
|
|
560
|
+
: "failed";
|
|
561
|
+
const failure = outOfBudget ? this.budgetFailure(error) : this.failure(error);
|
|
479
562
|
this.emit({
|
|
480
563
|
stage: "stopping",
|
|
481
564
|
runId,
|
|
@@ -487,9 +570,17 @@ export class ContainerExecutor {
|
|
|
487
570
|
this.terminal(run, status, failure.audit);
|
|
488
571
|
if (handle)
|
|
489
572
|
await this.options.jobs.remove(handle).catch(() => undefined);
|
|
573
|
+
// A service sidecar that never became ready (kubernetes.ts waitForKeeper) is named on the host.
|
|
574
|
+
const unreadyService = outOfBudget ? null : serviceUnreadyName(error);
|
|
575
|
+
const protectedPath = outOfBudget ? null : protectedPathFromError(error);
|
|
490
576
|
if (workspace) {
|
|
491
577
|
await this.options.vcs.cleanup(workspace).catch(() => undefined);
|
|
492
|
-
await this.options.vcs.notifyContinuationFinished?.(workspace, "failed", {
|
|
578
|
+
await this.options.vcs.notifyContinuationFinished?.(workspace, outOfBudget ? "budget_exhausted" : "failed", {
|
|
579
|
+
agentName: run.agentName,
|
|
580
|
+
...(outOfBudget ? { budgetSentence: this.budgetSentence(run) } : {}),
|
|
581
|
+
...(unreadyService ? { serviceSentence: serviceUnreadySentence([unreadyService]) } : {}),
|
|
582
|
+
...(protectedPath ? { protectedPathSentence: protectedPathSentence(protectedPath) } : {}),
|
|
583
|
+
});
|
|
493
584
|
}
|
|
494
585
|
this.emit({ stage: "cleanup", runId, jobId: handle?.id, cleanupSucceeded: true });
|
|
495
586
|
}
|
|
@@ -531,18 +622,70 @@ export class ContainerExecutor {
|
|
|
531
622
|
clearInterval(timer);
|
|
532
623
|
}
|
|
533
624
|
}
|
|
625
|
+
/**
|
|
626
|
+
* The run may use its repository only if the agent's current owner still
|
|
627
|
+
* has write on it, or the profile's approval (admin or grandfathered)
|
|
628
|
+
* covers exactly this repository. A profile changed since dispatch no
|
|
629
|
+
* longer covers the run's repository, so that falls back to the owner's
|
|
630
|
+
* access. Throws RepoAccessError (refused before anything is cloned).
|
|
631
|
+
*/
|
|
632
|
+
async authorizeRepository(run, opts) {
|
|
633
|
+
const sameRepository = run.profileRepository !== null && run.profileRepository === run.repository;
|
|
634
|
+
const decision = await this.options.repoAccess.authorizeUse({
|
|
635
|
+
ownerId: run.ownerId,
|
|
636
|
+
provider: "github",
|
|
637
|
+
repository: run.repository,
|
|
638
|
+
required: requiredLevel("coding"),
|
|
639
|
+
authorizedVia: sameRepository ? run.repositoryAuthorizedVia : "host_permission",
|
|
640
|
+
retryTransient: opts.inFlight,
|
|
641
|
+
});
|
|
642
|
+
if (!decision.ok)
|
|
643
|
+
throw new RepoAccessError(`repo_access_${decision.reason}`);
|
|
644
|
+
}
|
|
645
|
+
/** The originating issue for the PR title/body: control-plane data from the CodingRun row, key re-validated. */
|
|
646
|
+
issueFor(run) {
|
|
647
|
+
const { issueProvider, issueKey } = run;
|
|
648
|
+
if (!issueProvider || !issueKey || !ISSUE_KEY.test(issueKey))
|
|
649
|
+
return {};
|
|
650
|
+
let url;
|
|
651
|
+
try {
|
|
652
|
+
url = this.options.issueUrl?.(issueProvider, issueKey);
|
|
653
|
+
}
|
|
654
|
+
catch {
|
|
655
|
+
url = undefined;
|
|
656
|
+
}
|
|
657
|
+
const trackerName = Object.hasOwn(ISSUE_TRACKER_NAMES, issueProvider)
|
|
658
|
+
? ISSUE_TRACKER_NAMES[issueProvider]
|
|
659
|
+
: undefined;
|
|
660
|
+
return {
|
|
661
|
+
issue: {
|
|
662
|
+
key: issueKey,
|
|
663
|
+
...(url?.startsWith("https://") ? { url } : {}),
|
|
664
|
+
...(trackerName ? { trackerName } : {}),
|
|
665
|
+
},
|
|
666
|
+
};
|
|
667
|
+
}
|
|
668
|
+
/** The run's stored model terms; a run from before the catalog resolves them from the current catalog. */
|
|
669
|
+
modelTerms(run) {
|
|
670
|
+
if (run.provider !== "codex" && run.provider !== "claude-code")
|
|
671
|
+
throw new Error("coding_provider_unsupported");
|
|
672
|
+
const stored = parseStoredEntry(run.pricingSnapshot);
|
|
673
|
+
if (stored && run.pricingVersion) {
|
|
674
|
+
if (stored.modelId !== run.model || codingProviderForModelProvider(stored.provider) !== run.provider) {
|
|
675
|
+
throw new Error("coding_run_pricing_mismatch");
|
|
676
|
+
}
|
|
677
|
+
return { version: run.pricingVersion, entry: stored };
|
|
678
|
+
}
|
|
679
|
+
const entry = resolveCodingEntry(run.provider, run.model);
|
|
680
|
+
return { version: entry.priceVersion, entry: parseStoredEntry(entry) };
|
|
681
|
+
}
|
|
534
682
|
preflight(run) {
|
|
535
683
|
try {
|
|
536
684
|
if (run.status !== "pending" && run.status !== "running")
|
|
537
685
|
throw new Error("coding_run_status_invalid");
|
|
538
686
|
if (run.agentKind !== "coding" || !run.ownerId)
|
|
539
687
|
throw new Error("coding_run_ownership_invalid");
|
|
540
|
-
|
|
541
|
-
getModelPricing(run.model);
|
|
542
|
-
else if (run.provider === "claude-code")
|
|
543
|
-
getAnthropicPricing(run.model);
|
|
544
|
-
else
|
|
545
|
-
throw new Error("coding_provider_unsupported");
|
|
688
|
+
this.modelTerms(run);
|
|
546
689
|
if (!Number.isFinite(run.budgetUsd) || run.budgetUsd <= 0 || run.costUsd > run.budgetUsd) {
|
|
547
690
|
throw new Error("coding_run_budget_invalid");
|
|
548
691
|
}
|
|
@@ -552,7 +695,6 @@ export class ContainerExecutor {
|
|
|
552
695
|
baseRef: run.baseRef,
|
|
553
696
|
defaultTask: null,
|
|
554
697
|
timeoutSec: run.timeoutSec,
|
|
555
|
-
allowedEgress: run.allowedEgress,
|
|
556
698
|
protectedPaths: run.protectedPaths,
|
|
557
699
|
});
|
|
558
700
|
const expectedHeadRunId = run.rootCodingRunId ?? run.runId;
|
|
@@ -577,6 +719,7 @@ export class ContainerExecutor {
|
|
|
577
719
|
baseRef: profile.baseRef,
|
|
578
720
|
headRef: run.headRef,
|
|
579
721
|
protectedPaths: profile.protectedPaths,
|
|
722
|
+
collectExclude: [...normalizeCollectExclusions(run.collectExclude).paths],
|
|
580
723
|
continuation: run.rootCodingRunId ? { rootRunId: run.rootCodingRunId } : undefined,
|
|
581
724
|
};
|
|
582
725
|
}
|
|
@@ -589,16 +732,34 @@ export class ContainerExecutor {
|
|
|
589
732
|
if (sessionId)
|
|
590
733
|
await this.options.sessions.cancelSession(sessionId).catch(() => undefined);
|
|
591
734
|
// For notifyContinuationFinished in the `finally` below -- defaults to
|
|
592
|
-
// "failed" and is only flipped right before
|
|
735
|
+
// "failed" and is only flipped right before a success or out-of-budget return.
|
|
593
736
|
let outcome = "failed";
|
|
594
737
|
let finishedSummary;
|
|
738
|
+
// Set when a failed job's session relayed a model-provider failure; names it on the host.
|
|
739
|
+
// Only a failed job: a transient provider error the worker retried past did not end the run.
|
|
740
|
+
let providerClass;
|
|
741
|
+
// Set when finalizeChanges refused the collected diff for touching a protected path; names it on the host.
|
|
742
|
+
let protectedPath;
|
|
595
743
|
try {
|
|
596
744
|
if (jobState !== "succeeded") {
|
|
597
745
|
const collected = await this.options.jobs.collect(handle).catch(() => null);
|
|
598
746
|
const reason = collected?.diagnostic ?? collected?.reason ?? jobState;
|
|
599
747
|
this.emit({ stage: "collected", runId: run.runId, jobId: handle.id });
|
|
748
|
+
// The worker usually fails outright when the proxy refuses a model
|
|
749
|
+
// request for budget; the session records that refusal.
|
|
750
|
+
if (await this.outOfBudget(sessionId)) {
|
|
751
|
+
const failure = this.budgetFailure(`job_${reason}`, { issues: collected?.diagnosticIssues });
|
|
752
|
+
await this.options.store.terminate(run.runId, "budget_exhausted", failure.error, failure.audit);
|
|
753
|
+
this.terminal(run, "budget_exhausted", failure.audit);
|
|
754
|
+
outcome = "budget_exhausted";
|
|
755
|
+
return;
|
|
756
|
+
}
|
|
600
757
|
const status = jobState === "lost" ? "lost" : "failed";
|
|
601
|
-
const
|
|
758
|
+
const provider = status === "failed" ? await this.providerFailure(sessionId) : null;
|
|
759
|
+
const failure = provider
|
|
760
|
+
? this.providerFailureOf(run, provider, `job_${reason}`, { issues: collected?.diagnosticIssues })
|
|
761
|
+
: this.failure(`job_${reason}`, { issues: collected?.diagnosticIssues });
|
|
762
|
+
providerClass = provider?.class;
|
|
602
763
|
await this.options.store.terminate(run.runId, status, failure.error, failure.audit);
|
|
603
764
|
this.terminal(run, status, failure.audit);
|
|
604
765
|
return;
|
|
@@ -615,6 +776,7 @@ export class ContainerExecutor {
|
|
|
615
776
|
this.emit({ stage: "budget_cutoff", runId: run.runId, jobId: handle.id });
|
|
616
777
|
await this.options.store.complete(run.runId, "budget_exhausted", this.resultFor(output, current, "budget_exhausted"));
|
|
617
778
|
this.terminal(current, "budget_exhausted");
|
|
779
|
+
outcome = "budget_exhausted";
|
|
618
780
|
return;
|
|
619
781
|
}
|
|
620
782
|
if (output.outcome === "no_changes") {
|
|
@@ -629,17 +791,25 @@ export class ContainerExecutor {
|
|
|
629
791
|
if (!workspace)
|
|
630
792
|
throw new Error("coding_workspace_lost");
|
|
631
793
|
await this.options.jobs.materializeWorkspace(handle, workspace.workspacePath);
|
|
794
|
+
await normalizeCollectedLockfiles(current.provider, workspace.workspacePath);
|
|
632
795
|
current = await this.requireCurrent(run.runId);
|
|
633
796
|
if (current.costUsd > current.budgetUsd) {
|
|
634
797
|
this.emit({ stage: "budget_cutoff", runId: run.runId, jobId: handle.id });
|
|
635
798
|
await this.options.store.complete(run.runId, "budget_exhausted", this.resultFor(output, current, "budget_exhausted"));
|
|
636
799
|
this.terminal(current, "budget_exhausted");
|
|
800
|
+
outcome = "budget_exhausted";
|
|
637
801
|
return;
|
|
638
802
|
}
|
|
803
|
+
// One more (usually cached) check right before anything is pushed: access
|
|
804
|
+
// revoked while the run was working stops the push.
|
|
805
|
+
await this.authorizeRepository(current, { inFlight: true });
|
|
806
|
+
const report = await this.loadRegistryReport(run.runId);
|
|
639
807
|
const finalized = await this.options.vcs.finalizeChanges(workspace, {
|
|
640
808
|
summary: output.summary,
|
|
641
809
|
tests: output.tests,
|
|
642
810
|
tag: output.tag,
|
|
811
|
+
...report,
|
|
812
|
+
...this.issueFor(current),
|
|
643
813
|
});
|
|
644
814
|
const result = this.resultFor(output, current, finalized.outcome, finalized);
|
|
645
815
|
await this.options.store.complete(run.runId, "succeeded", result);
|
|
@@ -651,9 +821,18 @@ export class ContainerExecutor {
|
|
|
651
821
|
finishedSummary = output.summary;
|
|
652
822
|
}
|
|
653
823
|
catch (error) {
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
824
|
+
if (await this.outOfBudget(sessionId)) {
|
|
825
|
+
const failure = this.budgetFailure(error);
|
|
826
|
+
await this.options.store.terminate(run.runId, "budget_exhausted", failure.error, failure.audit);
|
|
827
|
+
this.terminal(run, "budget_exhausted", failure.audit);
|
|
828
|
+
outcome = "budget_exhausted";
|
|
829
|
+
}
|
|
830
|
+
else {
|
|
831
|
+
const failure = this.failure(error);
|
|
832
|
+
await this.options.store.terminate(run.runId, "failed", failure.error, failure.audit);
|
|
833
|
+
this.terminal(run, "failed", failure.audit);
|
|
834
|
+
protectedPath = protectedPathFromError(error) ?? undefined;
|
|
835
|
+
}
|
|
657
836
|
}
|
|
658
837
|
finally {
|
|
659
838
|
await this.options.jobs.remove(handle).catch(() => undefined);
|
|
@@ -664,12 +843,34 @@ export class ContainerExecutor {
|
|
|
664
843
|
await this.options.vcs.notifyContinuationFinished?.(workspace, outcome, {
|
|
665
844
|
summary: finishedSummary,
|
|
666
845
|
agentName: run.agentName,
|
|
846
|
+
...(outcome === "budget_exhausted" ? { budgetSentence: this.budgetSentence(run) } : {}),
|
|
847
|
+
...(outcome === "failed" && providerClass ? { providerSentence: providerSentence(providerClass) } : {}),
|
|
848
|
+
...(outcome === "failed" && protectedPath
|
|
849
|
+
? { protectedPathSentence: protectedPathSentence(protectedPath) }
|
|
850
|
+
: {}),
|
|
667
851
|
});
|
|
668
852
|
}
|
|
669
853
|
await rm(this.artifactPath(run.runId), { recursive: true, force: true }).catch(() => undefined);
|
|
670
854
|
this.emit({ stage: "cleanup", runId: run.runId, jobId: handle.id, cleanupSucceeded: true });
|
|
671
855
|
}
|
|
672
856
|
}
|
|
857
|
+
/** The package report is informational: any failure loading it (a
|
|
858
|
+
* rejection, a synchronous throw, or a malformed result) yields no
|
|
859
|
+
* package section rather than failing finalization. */
|
|
860
|
+
async loadRegistryReport(runId) {
|
|
861
|
+
try {
|
|
862
|
+
const report = await this.options.registryReport?.(runId);
|
|
863
|
+
const packages = Array.isArray(report?.packages) ? report.packages : [];
|
|
864
|
+
const packageRefusals = Array.isArray(report?.packageRefusals) ? report.packageRefusals : [];
|
|
865
|
+
return {
|
|
866
|
+
...(packages.length > 0 ? { packages } : {}),
|
|
867
|
+
...(packageRefusals.length > 0 ? { packageRefusals } : {}),
|
|
868
|
+
};
|
|
869
|
+
}
|
|
870
|
+
catch {
|
|
871
|
+
return {};
|
|
872
|
+
}
|
|
873
|
+
}
|
|
673
874
|
resultFor(output, run, forcedOutcome, finalized) {
|
|
674
875
|
const outcome = forcedOutcome ?? (output.outcome === "budget_exhausted" ? "budget_exhausted" : "no_changes");
|
|
675
876
|
return CodingRunResultSchema.parse({
|
|
@@ -694,15 +895,12 @@ export class ContainerExecutor {
|
|
|
694
895
|
}
|
|
695
896
|
resolveCodingWorkerImage(selector) {
|
|
696
897
|
if (selector.provider === "claude-code") {
|
|
697
|
-
|
|
698
|
-
throw new Error("coding_toolchain_unsupported:claude-code");
|
|
699
|
-
}
|
|
898
|
+
this.claudeToolImage(selector);
|
|
700
899
|
const image = selector.workerImageRef ?? this.options.claudeWorkerImage;
|
|
701
|
-
if (!image
|
|
900
|
+
if (!image)
|
|
702
901
|
throw new Error("coding_provider_not_configured:claude-code");
|
|
703
|
-
if (!isImmutableDockerImage(image)
|
|
902
|
+
if (!isImmutableDockerImage(image))
|
|
704
903
|
throw new Error("coding_worker_image_invalid");
|
|
705
|
-
}
|
|
706
904
|
return image;
|
|
707
905
|
}
|
|
708
906
|
if (selector.provider !== "codex") {
|
|
@@ -722,6 +920,51 @@ export class ContainerExecutor {
|
|
|
722
920
|
}
|
|
723
921
|
return image;
|
|
724
922
|
}
|
|
923
|
+
/** Claude's tool-runner image for the agent's toolchain, at dispatch (Executor.resolveCodingToolImage); null for Codex. */
|
|
924
|
+
resolveCodingToolImage(selector) {
|
|
925
|
+
return selector.provider === "claude-code" ? this.claudeToolImage(selector) : null;
|
|
926
|
+
}
|
|
927
|
+
/** The tool runner is what runs a Claude agent's commands, so it is what the toolchain selects. */
|
|
928
|
+
claudeToolImage(selector) {
|
|
929
|
+
if (!this.options.claudeToolRunnerImage)
|
|
930
|
+
throw new Error("coding_provider_not_configured:claude-code");
|
|
931
|
+
const image = selector.toolchain === "node" && selector.toolchainVersion === null
|
|
932
|
+
? this.options.claudeToolRunnerImage
|
|
933
|
+
: selector.toolchainVersion !== null
|
|
934
|
+
? this.options.claudeToolRunnerImages?.[selector.toolchain]?.[selector.toolchainVersion]
|
|
935
|
+
: undefined;
|
|
936
|
+
if (!image)
|
|
937
|
+
throw new Error("coding_toolchain_unsupported:claude-code");
|
|
938
|
+
if (!isImmutableDockerImage(image))
|
|
939
|
+
throw new Error("coding_worker_image_invalid");
|
|
940
|
+
return image;
|
|
941
|
+
}
|
|
942
|
+
/** Coding-run services: the repository's declaration at its base ref (Executor.readCodingServiceDeclaration). */
|
|
943
|
+
async readCodingServiceDeclaration(input) {
|
|
944
|
+
if (!this.options.vcs.readRepositoryFile)
|
|
945
|
+
return null;
|
|
946
|
+
return this.options.vcs.readRepositoryFile({
|
|
947
|
+
repository: input.repository,
|
|
948
|
+
ref: input.baseRef,
|
|
949
|
+
path: SERVICE_DECLARATION_PATH,
|
|
950
|
+
maxBytes: MAX_SERVICE_DECLARATION_BYTES,
|
|
951
|
+
});
|
|
952
|
+
}
|
|
953
|
+
/** One repository file at the run's base ref (Executor.readCodingRepositoryFile). */
|
|
954
|
+
async readCodingRepositoryFile(input) {
|
|
955
|
+
if (!this.options.vcs.readRepositoryFile)
|
|
956
|
+
return null;
|
|
957
|
+
return this.options.vcs.readRepositoryFile({
|
|
958
|
+
repository: input.repository,
|
|
959
|
+
ref: input.baseRef,
|
|
960
|
+
path: input.path,
|
|
961
|
+
maxBytes: input.maxBytes,
|
|
962
|
+
});
|
|
963
|
+
}
|
|
964
|
+
/** The job launcher decides (Kubernetes and Docker start services for both providers). */
|
|
965
|
+
supportsCodingServices(provider) {
|
|
966
|
+
return this.options.jobs.supportsServicesFor?.(provider) === true;
|
|
967
|
+
}
|
|
725
968
|
async requireCurrent(runId) {
|
|
726
969
|
const current = await this.options.store.load(runId);
|
|
727
970
|
if (!current)
|
|
@@ -736,16 +979,24 @@ export class ContainerExecutor {
|
|
|
736
979
|
if (run.workspaceDiskMb && run.workspaceDiskMb > this.options.maxDiskMb) {
|
|
737
980
|
throw new Error("coding_workspace_disk_exceeds_limit");
|
|
738
981
|
}
|
|
982
|
+
const services = parseStoredServices(run.services);
|
|
983
|
+
// Dispatch refuses services this deployment can't start; this is the backstop.
|
|
984
|
+
if (services.length > 0 && !this.supportsCodingServices(provider)) {
|
|
985
|
+
throw new Error("coding_services_unsupported_launcher");
|
|
986
|
+
}
|
|
739
987
|
return {
|
|
740
988
|
kind: "coding-agent",
|
|
741
989
|
runId: run.runId,
|
|
742
990
|
provider,
|
|
743
991
|
image: run.workerImage ?? this.options.workerImage,
|
|
744
|
-
|
|
992
|
+
// A run keeps the tool image it was dispatched with; rows from before CodingRun.toolImage use the default.
|
|
993
|
+
...(provider === "claude-code" ? { toolImage: run.toolImage ?? this.options.claudeToolRunnerImage } : {}),
|
|
745
994
|
inputArtifact,
|
|
746
995
|
timeoutSec: run.timeoutSec,
|
|
747
996
|
limits: { ...this.options.limits, ...(run.workspaceDiskMb ? { diskMb: run.workspaceDiskMb } : {}) },
|
|
748
997
|
labels: {},
|
|
998
|
+
collectExclude: normalizeCollectExclusions(run.collectExclude),
|
|
999
|
+
...(services.length > 0 ? { services } : {}),
|
|
749
1000
|
};
|
|
750
1001
|
}
|
|
751
1002
|
credentialRef(provider) {
|
|
@@ -755,7 +1006,7 @@ export class ContainerExecutor {
|
|
|
755
1006
|
return this.options.anthropicCredentialRef;
|
|
756
1007
|
throw new Error("coding_provider_not_configured:claude-code");
|
|
757
1008
|
}
|
|
758
|
-
async writeInput(run, deadlineAt) {
|
|
1009
|
+
async writeInput(run, deadlineAt, baseCommit) {
|
|
759
1010
|
await ensurePrivateDirectory(this.artifactRoot);
|
|
760
1011
|
const rootReal = await realpath(this.artifactRoot);
|
|
761
1012
|
const directory = resolve(rootReal, run.runId);
|
|
@@ -766,17 +1017,26 @@ export class ContainerExecutor {
|
|
|
766
1017
|
throw new Error("coding_artifact_path_invalid");
|
|
767
1018
|
const destination = join(directory, "input.json");
|
|
768
1019
|
const temporary = join(directory, `.input-${randomUUID()}.tmp`);
|
|
1020
|
+
const services = parseStoredServices(run.services);
|
|
1021
|
+
const task = withBaseCommit(run.task, baseCommit);
|
|
1022
|
+
if (task === run.task && /^[0-9a-f]{40}$/.test(baseCommit)) {
|
|
1023
|
+
containerLog.warn({ event: "coding.base_commit_omitted", runId: run.runId }, "the coding task left no room for the base commit line");
|
|
1024
|
+
}
|
|
769
1025
|
const input = CodingTaskInputSchema.parse({
|
|
770
1026
|
schemaVersion: CODING_PROTOCOL_VERSION,
|
|
771
1027
|
runId: run.runId,
|
|
772
1028
|
repository: run.repository,
|
|
773
1029
|
baseRef: run.baseRef,
|
|
774
1030
|
headRef: run.headRef,
|
|
775
|
-
task
|
|
1031
|
+
task,
|
|
776
1032
|
model: run.model,
|
|
777
1033
|
budgetUsd: run.budgetUsd,
|
|
778
1034
|
deadlineAt: deadlineAt.toISOString(),
|
|
779
1035
|
continuationOf: run.rootCodingRunId ? { runId: run.rootCodingRunId } : undefined,
|
|
1036
|
+
// Only when true: an untraced run's input stays exactly what older workers expect.
|
|
1037
|
+
...(run.debugTrace ? { debugTrace: true } : {}),
|
|
1038
|
+
// Only when there are some: every other run's input stays exactly what older workers expect.
|
|
1039
|
+
...(services.length > 0 ? { services: workerServices(services) } : {}),
|
|
780
1040
|
});
|
|
781
1041
|
// The run directory is 0700; world-readable mode only crosses Docker's UID boundary.
|
|
782
1042
|
await writeFile(temporary, JSON.stringify(input), { flag: "wx", mode: 0o444 });
|
|
@@ -789,12 +1049,20 @@ export class ContainerExecutor {
|
|
|
789
1049
|
preflightForCleanup(run) {
|
|
790
1050
|
if (!Array.isArray(run.protectedPaths) || !run.protectedPaths.every((path) => typeof path === "string"))
|
|
791
1051
|
return null;
|
|
1052
|
+
let collectExclude;
|
|
1053
|
+
try {
|
|
1054
|
+
collectExclude = [...normalizeCollectExclusions(run.collectExclude).paths];
|
|
1055
|
+
}
|
|
1056
|
+
catch {
|
|
1057
|
+
return null;
|
|
1058
|
+
}
|
|
792
1059
|
return {
|
|
793
1060
|
runId: run.runId,
|
|
794
1061
|
repository: run.repository,
|
|
795
1062
|
baseRef: run.baseRef,
|
|
796
1063
|
headRef: run.headRef,
|
|
797
1064
|
protectedPaths: run.protectedPaths,
|
|
1065
|
+
collectExclude,
|
|
798
1066
|
continuation: run.rootCodingRunId ? { rootRunId: run.rootCodingRunId } : undefined,
|
|
799
1067
|
};
|
|
800
1068
|
}
|
|
@@ -838,11 +1106,71 @@ export class ContainerExecutor {
|
|
|
838
1106
|
// notification actually firing) -- safe to call again because
|
|
839
1107
|
// notifyContinuationFinished is find-and-update-or-no-op, never
|
|
840
1108
|
// find-or-create.
|
|
841
|
-
|
|
1109
|
+
const outcome = run.status === "succeeded" ? "succeeded" : run.status === "budget_exhausted" ? "budget_exhausted" : "failed";
|
|
1110
|
+
const providerClass = run.status === "failed" ? providerClassOfCategory(run.failureCategory) : null;
|
|
1111
|
+
const unreadyServices = run.status === "failed" && run.failureCategory === SERVICE_UNREADY_CATEGORY
|
|
1112
|
+
? storedServiceNames(run.services)
|
|
1113
|
+
: null;
|
|
1114
|
+
// No path survives to the stored run (only the category does -- see
|
|
1115
|
+
// protected-path-wording.ts), so this can only ever use the pathless sentence.
|
|
1116
|
+
const protectedPathFailed = run.status === "failed" && run.failureCategory === PROTECTED_PATH_CATEGORY;
|
|
1117
|
+
await this.options.vcs.notifyContinuationFinished?.(workspace, outcome, {
|
|
1118
|
+
agentName: run.agentName,
|
|
1119
|
+
...(outcome === "budget_exhausted" ? { budgetSentence: this.budgetSentence(run) } : {}),
|
|
1120
|
+
...(providerClass ? { providerSentence: providerSentence(providerClass) } : {}),
|
|
1121
|
+
...(unreadyServices ? { serviceSentence: serviceUnreadySentence(unreadyServices) } : {}),
|
|
1122
|
+
...(protectedPathFailed ? { protectedPathSentence: protectedPathSentence(null) } : {}),
|
|
1123
|
+
});
|
|
842
1124
|
}
|
|
843
1125
|
await rm(this.artifactPath(run.runId), { recursive: true, force: true }).catch(() => undefined);
|
|
844
1126
|
this.emit({ stage: "cleanup", runId: run.runId, jobId: run.jobHandle?.id, cleanupSucceeded: true });
|
|
845
1127
|
}
|
|
1128
|
+
/** Whether the proxy refused this run's session for budget. Never throws. */
|
|
1129
|
+
async outOfBudget(sessionId) {
|
|
1130
|
+
if (!sessionId || !this.options.sessions.budgetExhausted)
|
|
1131
|
+
return false;
|
|
1132
|
+
return this.options.sessions.budgetExhausted(sessionId).catch(() => false);
|
|
1133
|
+
}
|
|
1134
|
+
/** The model-provider failure the proxy relayed for this run's session, classified. Never throws. */
|
|
1135
|
+
async providerFailure(sessionId) {
|
|
1136
|
+
if (!sessionId || !this.options.sessions.upstreamFailure)
|
|
1137
|
+
return null;
|
|
1138
|
+
const code = await this.options.sessions.upstreamFailure(sessionId).catch(() => null);
|
|
1139
|
+
return code ? { class: classifyProviderFailure(code), code } : null;
|
|
1140
|
+
}
|
|
1141
|
+
/**
|
|
1142
|
+
* A failure the model provider's refusal caused. The persisted error names
|
|
1143
|
+
* the class only; the raw upstream code (a fixed identifier, never a message)
|
|
1144
|
+
* goes to one operator line an alert can key on, and the failure itself is
|
|
1145
|
+
* still logged under its diagnostic id.
|
|
1146
|
+
*/
|
|
1147
|
+
providerFailureOf(run, provider, error, options = {}) {
|
|
1148
|
+
const logged = this.failure(error, options);
|
|
1149
|
+
containerLog.warn({ event: "coding.provider_failure", runId: run.runId, class: provider.class, upstreamCode: provider.code }, "the model provider refused this coding run's requests");
|
|
1150
|
+
return {
|
|
1151
|
+
error: `coding_provider_${provider.class}`,
|
|
1152
|
+
audit: { failureCategory: `provider_${provider.class}`, diagnosticId: logged.audit.diagnosticId },
|
|
1153
|
+
};
|
|
1154
|
+
}
|
|
1155
|
+
/** The host-safe sentence for a run that ran out of budget. */
|
|
1156
|
+
budgetSentence(run) {
|
|
1157
|
+
return budgetSentence({
|
|
1158
|
+
runBudgetUsd: run.budgetUsd,
|
|
1159
|
+
agentBudgetUsd: run.agentBudgetUsd ?? run.budgetUsd,
|
|
1160
|
+
budgetGroupName: run.budgetGroupName,
|
|
1161
|
+
});
|
|
1162
|
+
}
|
|
1163
|
+
/**
|
|
1164
|
+
* A failure the proxy's budget refusal caused. The failure itself is still
|
|
1165
|
+
* logged under its diagnostic id, so the operator keeps the real reason.
|
|
1166
|
+
*/
|
|
1167
|
+
budgetFailure(error, options = {}) {
|
|
1168
|
+
const logged = this.failure(error, options);
|
|
1169
|
+
return {
|
|
1170
|
+
error: "coding_budget_exhausted",
|
|
1171
|
+
audit: { failureCategory: "budget", diagnosticId: logged.audit.diagnosticId },
|
|
1172
|
+
};
|
|
1173
|
+
}
|
|
846
1174
|
emit(event) {
|
|
847
1175
|
try {
|
|
848
1176
|
this.observer.emit(event);
|
|
@@ -913,7 +1241,36 @@ function safeError(error) {
|
|
|
913
1241
|
const message = error instanceof Error ? error.message : "unknown";
|
|
914
1242
|
return `coding_executor:${redactAndTruncate(message, 200).replace(/[^A-Za-z0-9_.:-]/g, "_")}`;
|
|
915
1243
|
}
|
|
1244
|
+
/**
|
|
1245
|
+
* Error-code prefixes that name their category outright, checked before the
|
|
1246
|
+
* substring heuristics below. Those heuristics filed every `github_*` error
|
|
1247
|
+
* (a failed PR lookup or create after the push, a token mint) under
|
|
1248
|
+
* "workspace" because "github" contains "git".
|
|
1249
|
+
*/
|
|
1250
|
+
const CATEGORY_BY_PREFIX = [
|
|
1251
|
+
// A service sidecar never became ready (src/providers/jobs/kubernetes.ts).
|
|
1252
|
+
[SERVICE_UNREADY_ERROR, SERVICE_UNREADY_CATEGORY],
|
|
1253
|
+
// Services on a launcher that can't start them: refused at dispatch, failed here as a backstop.
|
|
1254
|
+
["coding_services_unsupported", "preflight"],
|
|
1255
|
+
// Claude Code's tool runner failed to start or never became ready: the same category on both launchers.
|
|
1256
|
+
["docker_tool_runner_", "job"],
|
|
1257
|
+
["kubernetes_tool_runner_", "job"],
|
|
1258
|
+
// Checked first: the host could not be asked (after one retry), not a lost permission.
|
|
1259
|
+
["repo_access_check_unavailable", "repo_access_unavailable"],
|
|
1260
|
+
["repo_access_", "repo_access"],
|
|
1261
|
+
["github_", "github"],
|
|
1262
|
+
// Checked before the generic "vcs_" fallback: the collected diff touched a
|
|
1263
|
+
// path this agent may not edit, not a generic workspace/git failure.
|
|
1264
|
+
["vcs_protected_path:", PROTECTED_PATH_CATEGORY],
|
|
1265
|
+
["vcs_", "workspace"],
|
|
1266
|
+
["git_", "workspace"],
|
|
1267
|
+
];
|
|
916
1268
|
function failureCategory(error) {
|
|
1269
|
+
const raw = error instanceof Error ? error.message : typeof error === "string" ? error : "";
|
|
1270
|
+
for (const [prefix, category] of CATEGORY_BY_PREFIX) {
|
|
1271
|
+
if (raw.startsWith(prefix))
|
|
1272
|
+
return category;
|
|
1273
|
+
}
|
|
917
1274
|
const message = safeError(error);
|
|
918
1275
|
if (message.includes("preflight") || message.includes("ownership") || message.includes("image"))
|
|
919
1276
|
return "preflight";
|