@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
package/dist/mcp/tools/agents.js
CHANGED
|
@@ -1,11 +1,28 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/**
|
|
2
|
+
* Agent CRUD with scope checks in server.ts and access checks here
|
|
3
|
+
* (auth/access.ts: owner, or a grant of read < execute < write). Owner-only
|
|
4
|
+
* whatever the grants: delete_agent and repository changes; make_owner is
|
|
5
|
+
* the agents:admin escape hatch. See
|
|
6
|
+
* docs/private/2026-09-26-resource-sharing-grants-spec-and-plan.md.
|
|
7
|
+
*/
|
|
2
8
|
import { Prisma } from "#prisma";
|
|
3
9
|
import { z } from "zod";
|
|
4
10
|
import { CodingProfilePatchSchema, CodingProfileSchema } from "../../coding/profile.js";
|
|
5
11
|
import { codingProviderSupportsModel } from "../../coding/provider.js";
|
|
12
|
+
import { assertAgentModelAvailable } from "../../core/run-pricing.js";
|
|
13
|
+
import { ModelUnavailableError } from "../../providers/llm/catalog-types.js";
|
|
14
|
+
import { currentModelCatalog } from "../../providers/llm/catalog-store.js";
|
|
6
15
|
import { validateCronExpression } from "../../core/cron.js";
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
16
|
+
import { modelSupportedEfforts } from "../../providers/llm/routing.js";
|
|
17
|
+
import { PROJECT_KEY } from "../../providers/issue-tracker/types.js";
|
|
18
|
+
import { LLM_EFFORT_LEVELS, isLlmEffort } from "../../providers/llm/types.js";
|
|
19
|
+
import { requireReadableBudgetGroup } from "../auth/ownership.js";
|
|
20
|
+
import { agentAccessResolver, agentNotFound, assertAgentAccess, readableAgentsWhere, requireAgentAccess, } from "../auth/access.js";
|
|
21
|
+
import { EVERYONE_KEY, atLeast, canDelegate, deleteGrantsFor, effectiveAccess } from "../../core/grants.js";
|
|
22
|
+
import { projectTool } from "./tools.js";
|
|
23
|
+
import { requireAnyScope, requireScope } from "../auth/resource-server.js";
|
|
24
|
+
import { logger } from "../../core/logger.js";
|
|
25
|
+
import { authorizeRepositoryForSet } from "../auth/repo-authorization.js";
|
|
9
26
|
import { McpError } from "../errors.js";
|
|
10
27
|
import { textResult } from "./text-result.js";
|
|
11
28
|
// workerImageRef is the BYO-arbitrary-image escape hatch (see
|
|
@@ -15,6 +32,18 @@ import { textResult } from "./text-result.js";
|
|
|
15
32
|
function requireWorkerImageRefScope(ctx) {
|
|
16
33
|
requireScope(ctx, ctx.canonicalUri, "agents:admin");
|
|
17
34
|
}
|
|
35
|
+
// A debug trace makes the worker log prompts and repository content to its
|
|
36
|
+
// pod log, so turning it on (or off) is an admin action, gated like
|
|
37
|
+
// workerImageRef and audited.
|
|
38
|
+
function requireDebugTraceScope(ctx) {
|
|
39
|
+
requireScope(ctx, ctx.canonicalUri, "agents:admin");
|
|
40
|
+
}
|
|
41
|
+
const agentsLog = logger.child({ module: "mcp-agents" });
|
|
42
|
+
const MAX_DEBUG_TRACE_MINUTES = 24 * 60;
|
|
43
|
+
/** Package allowlists widen what a run may download, so they need their own approval. */
|
|
44
|
+
function requirePackageApproval(ctx) {
|
|
45
|
+
requireAnyScope(ctx, ctx.canonicalUri, "packages:approve", "agents:admin");
|
|
46
|
+
}
|
|
18
47
|
const MAX_AGENT_NAME_CHARS = 200;
|
|
19
48
|
const MAX_SYSTEM_PROMPT_CHARS = 64 * 1024;
|
|
20
49
|
const MAX_MODEL_CHARS = 128;
|
|
@@ -31,7 +60,24 @@ const agentFields = {
|
|
|
31
60
|
kind: z.enum(["native", "coding"]),
|
|
32
61
|
budgetGroupId: z.string().min(1).max(128),
|
|
33
62
|
memoryEnabled: z.boolean(),
|
|
63
|
+
effort: z.enum(LLM_EFFORT_LEVELS),
|
|
64
|
+
defectProjectKey: z.string().regex(PROJECT_KEY, "must be an upper-case Jira project key"),
|
|
65
|
+
defectIssueType: z.string().trim().min(1).max(100),
|
|
34
66
|
};
|
|
67
|
+
/** Self-defect config is both-or-neither; on update, both null clears it. */
|
|
68
|
+
function refineDefectPair(value, ctx) {
|
|
69
|
+
const key = value.defectProjectKey;
|
|
70
|
+
const type = value.defectIssueType;
|
|
71
|
+
if ((key === undefined) !== (type === undefined) || (key === null) !== (type === null)) {
|
|
72
|
+
ctx.addIssue({
|
|
73
|
+
code: z.ZodIssueCode.custom,
|
|
74
|
+
path: ["defectProjectKey"],
|
|
75
|
+
message: "defectProjectKey and defectIssueType must be set together (or both null to clear on update)",
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
const DEFECT_DESCRIPTION = "Self-defects (optional, both or neither with defectIssueType): the Jira project key wardby files this agent's own failures into. " +
|
|
80
|
+
"Filing needs a write link (link_issue_project) to that project whose creatableIssueTypes includes defectIssueType; it is checked at filing time and fails closed.";
|
|
35
81
|
const CreateAgentSchema = z
|
|
36
82
|
.object({
|
|
37
83
|
name: agentFields.name,
|
|
@@ -45,10 +91,15 @@ const CreateAgentSchema = z
|
|
|
45
91
|
kind: agentFields.kind.default("native"),
|
|
46
92
|
budgetGroupId: agentFields.budgetGroupId.optional(),
|
|
47
93
|
memoryEnabled: agentFields.memoryEnabled.default(false),
|
|
94
|
+
effort: agentFields.effort.optional(),
|
|
95
|
+
defectProjectKey: agentFields.defectProjectKey.optional(),
|
|
96
|
+
defectIssueType: agentFields.defectIssueType.optional(),
|
|
48
97
|
codingProfile: CodingProfileSchema.optional(),
|
|
98
|
+
repositoryAdminOverride: z.boolean().optional(),
|
|
49
99
|
})
|
|
50
100
|
.strict()
|
|
51
101
|
.superRefine((value, ctx) => {
|
|
102
|
+
refineDefectPair(value, ctx);
|
|
52
103
|
if (value.kind === "coding" && !value.codingProfile) {
|
|
53
104
|
ctx.addIssue({ code: z.ZodIssueCode.custom, path: ["codingProfile"], message: "is required for coding agents" });
|
|
54
105
|
}
|
|
@@ -59,8 +110,11 @@ const CreateAgentSchema = z
|
|
|
59
110
|
message: "is only valid for coding agents",
|
|
60
111
|
});
|
|
61
112
|
}
|
|
113
|
+
// A model the catalog doesn't offer is left to the handler, which reports
|
|
114
|
+
// model_unavailable with its reason instead of a provider mismatch.
|
|
62
115
|
if (value.kind === "coding" &&
|
|
63
116
|
value.codingProfile &&
|
|
117
|
+
currentModelCatalog().get(value.model) !== undefined &&
|
|
64
118
|
!codingProviderSupportsModel(value.codingProfile.provider, value.model)) {
|
|
65
119
|
ctx.addIssue({
|
|
66
120
|
code: z.ZodIssueCode.custom,
|
|
@@ -91,21 +145,50 @@ const UpdateAgentSchema = z
|
|
|
91
145
|
kind: agentFields.kind.optional(),
|
|
92
146
|
budgetGroupId: agentFields.budgetGroupId.nullable().optional(),
|
|
93
147
|
memoryEnabled: agentFields.memoryEnabled.optional(),
|
|
94
|
-
|
|
148
|
+
effort: agentFields.effort.nullable().optional(),
|
|
149
|
+
defectProjectKey: agentFields.defectProjectKey.nullable().optional(),
|
|
150
|
+
defectIssueType: agentFields.defectIssueType.nullable().optional(),
|
|
151
|
+
codingProfile: CodingProfilePatchSchema.extend({
|
|
152
|
+
/** Not a stored profile field: sets CodingAgentProfile.debugTraceUntil = now + minutes (null clears). */
|
|
153
|
+
debugTraceMinutes: z.number().int().min(1).max(MAX_DEBUG_TRACE_MINUTES).nullable().optional(),
|
|
154
|
+
}).optional(),
|
|
155
|
+
repositoryAdminOverride: z.boolean().optional(),
|
|
95
156
|
})
|
|
96
|
-
.strict()
|
|
157
|
+
.strict()
|
|
158
|
+
.superRefine(refineDefectPair);
|
|
159
|
+
const REPOSITORY_ADMIN_OVERRIDE = {
|
|
160
|
+
type: "boolean",
|
|
161
|
+
description: "Admins only (agents:admin with the admin role): approve codingProfile.repository without checking GitHub access, recorded as an admin approval. On an agent the admin doesn't own, only codingProfile.repository may change.",
|
|
162
|
+
};
|
|
163
|
+
/** The profile columns a repository authorization is stamped into. */
|
|
164
|
+
function profileStamp(authorization) {
|
|
165
|
+
return {
|
|
166
|
+
repositoryAuthorizedVia: authorization.authorizedVia,
|
|
167
|
+
repositoryAuthorizedById: authorization.authorizedById,
|
|
168
|
+
repositoryAuthorizedAt: authorization.authorizedAt,
|
|
169
|
+
};
|
|
170
|
+
}
|
|
97
171
|
const profileJsonSchema = {
|
|
98
172
|
type: "object",
|
|
99
173
|
additionalProperties: false,
|
|
100
174
|
properties: {
|
|
101
175
|
provider: { type: "string", enum: ["codex", "claude-code"] },
|
|
102
|
-
repository: {
|
|
176
|
+
repository: {
|
|
177
|
+
type: "string",
|
|
178
|
+
description: "owner/name on GitHub. Your linked GitHub account (link_host_account) must have write access to it, unless a wardby admin approves it (repositoryAdminOverride). Re-checked on every run.",
|
|
179
|
+
},
|
|
103
180
|
baseRef: { type: "string" },
|
|
104
181
|
defaultTask: { type: ["string", "null"] },
|
|
105
182
|
allowWebhookTaskOverride: { type: "boolean" },
|
|
106
183
|
timeoutSec: { type: "integer", minimum: 60, maximum: 7200 },
|
|
107
|
-
|
|
108
|
-
|
|
184
|
+
protectedPaths: {
|
|
185
|
+
type: "array",
|
|
186
|
+
minItems: 1,
|
|
187
|
+
maxItems: 128,
|
|
188
|
+
items: { type: "string" },
|
|
189
|
+
description: 'Glob patterns (*, **, ?) a run may not change. A leading ! makes an exception, a literal file path without wildcards: ["docs/**", "!docs/changelog.md"] protects docs/ except the changelog. Every run also protects .wardby/** except .wardby/services.yaml, whatever this list says; exceptions cannot unprotect .wardby/.',
|
|
190
|
+
},
|
|
191
|
+
collectExclude: { type: "array", maxItems: 64, items: { type: "string" } },
|
|
109
192
|
toolchain: { type: "string", enum: ["node", "node-python"] },
|
|
110
193
|
toolchainVersion: { type: ["string", "null"] },
|
|
111
194
|
workerImageRef: { type: ["string", "null"] },
|
|
@@ -115,6 +198,22 @@ const profileJsonSchema = {
|
|
|
115
198
|
maximum: 32768,
|
|
116
199
|
description: "Workspace disk size in MiB. Null uses the deployment default (CODING_DISK_MB), capped by the operator's CODING_MAX_DISK_MB.",
|
|
117
200
|
},
|
|
201
|
+
packageAllowlist: {
|
|
202
|
+
type: "object",
|
|
203
|
+
description: "Approved top-level packages per ecosystem (npm, pypi). Needs packages:approve or agents:admin.",
|
|
204
|
+
additionalProperties: { type: "array", maxItems: 256, items: { type: "string" } },
|
|
205
|
+
},
|
|
206
|
+
packagePolicy: {
|
|
207
|
+
type: "object",
|
|
208
|
+
properties: { minReleaseAgeDays: { type: "integer", minimum: 0, maximum: 30 } },
|
|
209
|
+
additionalProperties: false,
|
|
210
|
+
},
|
|
211
|
+
services: {
|
|
212
|
+
type: "array",
|
|
213
|
+
maxItems: 16,
|
|
214
|
+
items: { type: "string" },
|
|
215
|
+
description: 'Coding-run services this agent\'s runs may start: catalog names such as "postgres" (any version the catalog has). A repository asks for them in .wardby/services.yaml on its base branch; a run asking for one not listed here is refused. Empty = none.',
|
|
216
|
+
},
|
|
118
217
|
},
|
|
119
218
|
};
|
|
120
219
|
function invalidArguments(label, error) {
|
|
@@ -143,6 +242,43 @@ function validateSchedule(schedule, timezone) {
|
|
|
143
242
|
throw new McpError(400, `Invalid schedule/timezone: ${error instanceof Error ? error.message : String(error)}`);
|
|
144
243
|
}
|
|
145
244
|
}
|
|
245
|
+
/**
|
|
246
|
+
* Effort only drives the native engine, and only on a model that accepts the
|
|
247
|
+
* level; reject rather than store a setting that would silently do nothing.
|
|
248
|
+
*/
|
|
249
|
+
function validateEffort(kind, model, effort) {
|
|
250
|
+
if (effort == null)
|
|
251
|
+
return;
|
|
252
|
+
if (kind !== "native") {
|
|
253
|
+
throw new McpError(400, "effort is only valid for native agents; coding agents do not use it.");
|
|
254
|
+
}
|
|
255
|
+
const accepted = modelSupportedEfforts(model);
|
|
256
|
+
if (!isLlmEffort(effort) || !accepted.includes(effort)) {
|
|
257
|
+
throw new McpError(400, `Model "${model}" does not accept effort "${effort}". ` +
|
|
258
|
+
(accepted.length > 0
|
|
259
|
+
? `Accepted levels: ${accepted.join(", ")}.`
|
|
260
|
+
: "It accepts no effort setting; leave effort unset (or null)."));
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* A model being set (create, or update with `model`) must be runnable here:
|
|
265
|
+
* in the catalog and enabled; for a native agent, its provider must also be
|
|
266
|
+
* configured on the native router. A coding agent is checked against the
|
|
267
|
+
* catalog only: its runs go through the coding proxy, whose credentials
|
|
268
|
+
* (CODING_*_CREDENTIAL_REF) are not the native router's. An existing agent's
|
|
269
|
+
* unchanged model is not re-checked: if an admin later disabled it, the agent
|
|
270
|
+
* fails at its next run, not at an unrelated edit.
|
|
271
|
+
*/
|
|
272
|
+
function requireModelAvailable(ctx, model, kind) {
|
|
273
|
+
try {
|
|
274
|
+
assertAgentModelAvailable(model, kind === "native" ? ctx.providers.llm : undefined);
|
|
275
|
+
}
|
|
276
|
+
catch (err) {
|
|
277
|
+
if (err instanceof ModelUnavailableError)
|
|
278
|
+
throw new McpError(400, err.message);
|
|
279
|
+
throw err;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
146
282
|
function storedProfile(profile) {
|
|
147
283
|
return CodingProfileSchema.parse({
|
|
148
284
|
provider: profile.provider,
|
|
@@ -151,14 +287,27 @@ function storedProfile(profile) {
|
|
|
151
287
|
defaultTask: profile.defaultTask,
|
|
152
288
|
allowWebhookTaskOverride: profile.allowWebhookTaskOverride,
|
|
153
289
|
timeoutSec: profile.timeoutSec,
|
|
154
|
-
allowedEgress: profile.allowedEgress,
|
|
155
290
|
protectedPaths: profile.protectedPaths,
|
|
291
|
+
collectExclude: profile.collectExclude,
|
|
156
292
|
toolchain: profile.toolchain,
|
|
157
293
|
toolchainVersion: profile.toolchainVersion,
|
|
158
294
|
workerImageRef: profile.workerImageRef,
|
|
159
295
|
workspaceDiskMb: profile.workspaceDiskMb,
|
|
296
|
+
packageAllowlist: profile.packageAllowlist,
|
|
297
|
+
packagePolicy: profile.packagePolicy,
|
|
298
|
+
services: profile.services,
|
|
160
299
|
});
|
|
161
300
|
}
|
|
301
|
+
/** A service name an agent is allowed must exist in the catalog (any version); typos fail here, not at a run. */
|
|
302
|
+
async function requireCatalogServiceNames(db, names) {
|
|
303
|
+
if (names.length === 0)
|
|
304
|
+
return;
|
|
305
|
+
const known = new Set((await db.codingService.findMany({ where: { name: { in: [...names] } }, select: { name: true } })).map((row) => row.name));
|
|
306
|
+
const unknown = names.filter((name) => !known.has(name));
|
|
307
|
+
if (unknown.length > 0) {
|
|
308
|
+
throw new McpError(400, `wardby's service catalog has no service named ${unknown.map((name) => `"${name}"`).join(", ")}; list_services shows the catalog.`);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
162
311
|
export function registerAgentTools(mcp) {
|
|
163
312
|
mcp.registerTool({
|
|
164
313
|
name: "create_agent",
|
|
@@ -178,7 +327,15 @@ export function registerAgentTools(mcp) {
|
|
|
178
327
|
kind: { type: "string", enum: ["native", "coding"] },
|
|
179
328
|
budgetGroupId: { type: "string" },
|
|
180
329
|
memoryEnabled: { type: "boolean" },
|
|
330
|
+
effort: {
|
|
331
|
+
type: "string",
|
|
332
|
+
enum: [...LLM_EFFORT_LEVELS],
|
|
333
|
+
description: "Reasoning effort for native agents. Unset uses the provider default.",
|
|
334
|
+
},
|
|
335
|
+
defectProjectKey: { type: "string", pattern: PROJECT_KEY.source, description: DEFECT_DESCRIPTION },
|
|
336
|
+
defectIssueType: { type: "string", minLength: 1, maxLength: 100, description: DEFECT_DESCRIPTION },
|
|
181
337
|
codingProfile: { ...profileJsonSchema, required: ["repository"] },
|
|
338
|
+
repositoryAdminOverride: REPOSITORY_ADMIN_OVERRIDE,
|
|
182
339
|
},
|
|
183
340
|
required: ["name", "systemPrompt", "model", "budgetUsd"],
|
|
184
341
|
},
|
|
@@ -186,16 +343,40 @@ export function registerAgentTools(mcp) {
|
|
|
186
343
|
const args = parseCreateAgent(rawArgs);
|
|
187
344
|
if (args.codingProfile?.workerImageRef != null)
|
|
188
345
|
requireWorkerImageRefScope(ctx);
|
|
346
|
+
const packages = args.codingProfile;
|
|
347
|
+
if (packages &&
|
|
348
|
+
(Object.keys(packages.packageAllowlist ?? {}).length > 0 ||
|
|
349
|
+
Object.keys(packages.packagePolicy ?? {}).length > 0))
|
|
350
|
+
requirePackageApproval(ctx);
|
|
189
351
|
validateSchedule(args.schedule, args.timezone ?? "UTC");
|
|
352
|
+
requireModelAvailable(ctx, args.model, args.kind);
|
|
353
|
+
validateEffort(args.kind, args.model, args.effort);
|
|
354
|
+
if (args.codingProfile)
|
|
355
|
+
await requireCatalogServiceNames(ctx.db, args.codingProfile.services);
|
|
190
356
|
if (args.budgetGroupId) {
|
|
191
357
|
await requireReadableBudgetGroup(ctx.db, args.budgetGroupId, ctx.principal.id);
|
|
192
358
|
}
|
|
193
|
-
const { codingProfile, ...agentData } = args;
|
|
359
|
+
const { codingProfile, repositoryAdminOverride, ...agentData } = args;
|
|
360
|
+
if (repositoryAdminOverride === true && !codingProfile) {
|
|
361
|
+
throw new McpError(400, "repositoryAdminOverride only applies with a codingProfile repository.");
|
|
362
|
+
}
|
|
363
|
+
// The creator is the owner, so it's the creator's GitHub access that counts.
|
|
364
|
+
const authorization = codingProfile
|
|
365
|
+
? await authorizeRepositoryForSet(ctx, {
|
|
366
|
+
ownerId: ctx.principal.id,
|
|
367
|
+
provider: "github",
|
|
368
|
+
repository: codingProfile.repository,
|
|
369
|
+
kind: "coding",
|
|
370
|
+
adminOverride: repositoryAdminOverride === true,
|
|
371
|
+
})
|
|
372
|
+
: null;
|
|
194
373
|
const agent = await ctx.db.agent.create({
|
|
195
374
|
data: {
|
|
196
375
|
...agentData,
|
|
197
376
|
ownerId: ctx.principal.id,
|
|
198
|
-
...(codingProfile
|
|
377
|
+
...(codingProfile && authorization
|
|
378
|
+
? { codingProfile: { create: { ...codingProfile, ...profileStamp(authorization) } } }
|
|
379
|
+
: {}),
|
|
199
380
|
},
|
|
200
381
|
include: { codingProfile: true },
|
|
201
382
|
});
|
|
@@ -221,7 +402,35 @@ export function registerAgentTools(mcp) {
|
|
|
221
402
|
kind: { type: "string", enum: ["native", "coding"] },
|
|
222
403
|
budgetGroupId: { type: ["string", "null"] },
|
|
223
404
|
memoryEnabled: { type: "boolean" },
|
|
224
|
-
|
|
405
|
+
effort: {
|
|
406
|
+
type: ["string", "null"],
|
|
407
|
+
enum: [...LLM_EFFORT_LEVELS, null],
|
|
408
|
+
description: "Reasoning effort for native agents. Null clears it back to the provider default.",
|
|
409
|
+
},
|
|
410
|
+
defectProjectKey: {
|
|
411
|
+
type: ["string", "null"],
|
|
412
|
+
pattern: PROJECT_KEY.source,
|
|
413
|
+
description: `${DEFECT_DESCRIPTION} Null (together with defectIssueType) clears it.`,
|
|
414
|
+
},
|
|
415
|
+
defectIssueType: {
|
|
416
|
+
type: ["string", "null"],
|
|
417
|
+
minLength: 1,
|
|
418
|
+
maxLength: 100,
|
|
419
|
+
description: `${DEFECT_DESCRIPTION} Null (together with defectProjectKey) clears it.`,
|
|
420
|
+
},
|
|
421
|
+
codingProfile: {
|
|
422
|
+
...profileJsonSchema,
|
|
423
|
+
properties: {
|
|
424
|
+
...profileJsonSchema.properties,
|
|
425
|
+
debugTraceMinutes: {
|
|
426
|
+
type: ["integer", "null"],
|
|
427
|
+
minimum: 1,
|
|
428
|
+
maximum: MAX_DEBUG_TRACE_MINUTES,
|
|
429
|
+
description: "Admins only (agents:admin): trace every coding run of this agent for this many minutes from now; the worker writes its full stream trace, which can include prompts and repository content, to the run pod's log. Null turns it off.",
|
|
430
|
+
},
|
|
431
|
+
},
|
|
432
|
+
},
|
|
433
|
+
repositoryAdminOverride: REPOSITORY_ADMIN_OVERRIDE,
|
|
225
434
|
},
|
|
226
435
|
required: ["id"],
|
|
227
436
|
},
|
|
@@ -229,18 +438,111 @@ export function registerAgentTools(mcp) {
|
|
|
229
438
|
const args = parseUpdateAgent(rawArgs);
|
|
230
439
|
if (args.codingProfile?.workerImageRef !== undefined)
|
|
231
440
|
requireWorkerImageRefScope(ctx);
|
|
441
|
+
const debugTraceMinutes = args.codingProfile?.debugTraceMinutes;
|
|
442
|
+
if (debugTraceMinutes !== undefined)
|
|
443
|
+
requireDebugTraceScope(ctx);
|
|
444
|
+
if (args.codingProfile?.packageAllowlist !== undefined || args.codingProfile?.packagePolicy !== undefined)
|
|
445
|
+
requirePackageApproval(ctx);
|
|
446
|
+
// Repository authorization calls GitHub, so it runs before (outside) the
|
|
447
|
+
// serializable transaction, on a pre-read; the transaction then verifies
|
|
448
|
+
// the pre-read still holds. Only a changed repository (or becoming a
|
|
449
|
+
// coding agent) is re-authorized; other edits keep the existing stamp.
|
|
450
|
+
const before = await ctx.db.agent.findUnique({ where: { id: args.id }, include: { codingProfile: true } });
|
|
451
|
+
if (!before)
|
|
452
|
+
throw agentNotFound(args.id);
|
|
453
|
+
// Strict: a repository is a binding, so only the agent's owner sets
|
|
454
|
+
// one (or an admin's explicit approval, below). Not a write-grantee,
|
|
455
|
+
// not the stdio operator.
|
|
456
|
+
const isOwner = before.ownerId !== null && before.ownerId === ctx.principal.id;
|
|
457
|
+
// An admin may approve a repository (repositoryAdminOverride) on an agent
|
|
458
|
+
// they don't own, but change nothing else on it.
|
|
459
|
+
const adminEdit = args.repositoryAdminOverride === true && !isOwner;
|
|
460
|
+
if (adminEdit) {
|
|
461
|
+
requireScope(ctx, ctx.canonicalUri, "agents:admin");
|
|
462
|
+
const { id: _id, repositoryAdminOverride: _flag, codingProfile, ...others } = args;
|
|
463
|
+
const profileKeys = Object.keys(codingProfile ?? {});
|
|
464
|
+
if (Object.values(others).some((v) => v !== undefined) ||
|
|
465
|
+
profileKeys.length !== 1 ||
|
|
466
|
+
profileKeys[0] !== "repository") {
|
|
467
|
+
throw new McpError(403, "On an agent you don't own, an admin may change only codingProfile.repository (with repositoryAdminOverride).");
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
else {
|
|
471
|
+
await assertAgentAccess(ctx, before, args.id, "write");
|
|
472
|
+
}
|
|
473
|
+
const repositoryAfter = (kind, profile) => kind === "coding" ? (args.codingProfile?.repository ?? profile?.repository ?? null) : null;
|
|
474
|
+
const plannedRepository = repositoryAfter(args.kind ?? before.kind, before.codingProfile);
|
|
475
|
+
const repositoryChanges = (existing) => {
|
|
476
|
+
const next = repositoryAfter(args.kind ?? existing.kind, existing.codingProfile);
|
|
477
|
+
return next !== null && (existing.kind !== "coding" || existing.codingProfile?.repository !== next);
|
|
478
|
+
};
|
|
479
|
+
const refuseRepositoryBinding = () => {
|
|
480
|
+
throw new McpError(403, `Agent "${args.id}": only its owner can give it a repository (codingProfile.repository, or becoming a coding agent); a repository is a binding, not shared config.`);
|
|
481
|
+
};
|
|
482
|
+
if (!adminEdit && !isOwner && repositoryChanges(before))
|
|
483
|
+
refuseRepositoryBinding();
|
|
484
|
+
// The coding profile as a whole (task, base ref, protected paths,
|
|
485
|
+
// task-override opt-in, image, packages, limits...), leaving coding,
|
|
486
|
+
// and budget-group membership direct what runs do with the owner's
|
|
487
|
+
// repository access and money: owner-only, like the repository itself
|
|
488
|
+
// (review I1/M1/M6). Write keeps name, prompt, model, budget amount,
|
|
489
|
+
// turns, effort, memory and schedule.
|
|
490
|
+
const ownerOnlyChange = (existingKind) => args.codingProfile !== undefined ||
|
|
491
|
+
(args.kind !== undefined && args.kind !== existingKind) ||
|
|
492
|
+
args.budgetGroupId !== undefined;
|
|
493
|
+
const refuseOwnerOnly = () => {
|
|
494
|
+
throw new McpError(403, `Agent "${args.id}": only its owner can change its coding profile, kind or budget group.`);
|
|
495
|
+
};
|
|
496
|
+
if (!adminEdit && !isOwner && ownerOnlyChange(before.kind))
|
|
497
|
+
refuseOwnerOnly();
|
|
498
|
+
const authorization = plannedRepository !== null && repositoryChanges(before)
|
|
499
|
+
? await authorizeRepositoryForSet(ctx, {
|
|
500
|
+
ownerId: before.ownerId,
|
|
501
|
+
provider: "github",
|
|
502
|
+
repository: plannedRepository,
|
|
503
|
+
kind: "coding",
|
|
504
|
+
adminOverride: args.repositoryAdminOverride === true,
|
|
505
|
+
})
|
|
506
|
+
: null;
|
|
507
|
+
if (args.repositoryAdminOverride === true && !authorization) {
|
|
508
|
+
throw new McpError(400, "repositoryAdminOverride only applies when the coding repository changes.");
|
|
509
|
+
}
|
|
510
|
+
const debugTraceUntil = debugTraceMinutes === undefined
|
|
511
|
+
? undefined
|
|
512
|
+
: debugTraceMinutes === null
|
|
513
|
+
? null
|
|
514
|
+
: new Date(Date.now() + debugTraceMinutes * 60_000);
|
|
232
515
|
const agent = await ctx.db.$transaction(async (tx) => {
|
|
233
516
|
const existing = await tx.agent.findUnique({ where: { id: args.id }, include: { codingProfile: true } });
|
|
234
517
|
if (!existing)
|
|
235
|
-
throw
|
|
236
|
-
|
|
518
|
+
throw agentNotFound(args.id);
|
|
519
|
+
if (!adminEdit) {
|
|
520
|
+
await assertAgentAccess(ctx, existing, args.id, "write", tx);
|
|
521
|
+
if (!isOwner && repositoryChanges(existing))
|
|
522
|
+
refuseRepositoryBinding();
|
|
523
|
+
if (!isOwner && ownerOnlyChange(existing.kind))
|
|
524
|
+
refuseOwnerOnly();
|
|
525
|
+
}
|
|
526
|
+
if (existing.ownerId !== before.ownerId ||
|
|
527
|
+
(repositoryChanges(existing) &&
|
|
528
|
+
(!authorization ||
|
|
529
|
+
repositoryAfter(args.kind ?? existing.kind, existing.codingProfile) !== plannedRepository))) {
|
|
530
|
+
throw new McpError(409, `Agent "${args.id}" changed while it was being updated; try again.`);
|
|
531
|
+
}
|
|
237
532
|
if (args.budgetGroupId) {
|
|
238
|
-
await requireReadableBudgetGroup(
|
|
533
|
+
await requireReadableBudgetGroup(tx, args.budgetGroupId, ctx.principal.id);
|
|
239
534
|
}
|
|
240
535
|
const nextKind = args.kind ?? existing.kind;
|
|
241
536
|
if (nextKind === "native" && args.codingProfile) {
|
|
242
537
|
throw new McpError(400, "A coding profile is only valid for coding agents.");
|
|
243
538
|
}
|
|
539
|
+
if (args.model !== undefined)
|
|
540
|
+
requireModelAvailable(ctx, args.model, nextKind);
|
|
541
|
+
// Only re-checked when the model, effort or kind changes: an agent whose
|
|
542
|
+
// model was since disabled can still take unrelated edits.
|
|
543
|
+
if (args.model !== undefined || args.effort !== undefined || nextKind !== existing.kind) {
|
|
544
|
+
validateEffort(nextKind, args.model ?? existing.model, args.effort !== undefined ? args.effort : existing.effort);
|
|
545
|
+
}
|
|
244
546
|
let nextProfile = null;
|
|
245
547
|
if (nextKind === "coding") {
|
|
246
548
|
const attachedTools = await tx.agentTool.count({ where: { agentId: args.id } });
|
|
@@ -248,12 +550,18 @@ export function registerAgentTools(mcp) {
|
|
|
248
550
|
throw new McpError(400, "Detach all native sandbox tools before changing an agent to coding.");
|
|
249
551
|
}
|
|
250
552
|
const currentProfile = existing.codingProfile ? storedProfile(existing.codingProfile) : undefined;
|
|
251
|
-
const
|
|
553
|
+
const { debugTraceMinutes: _minutes, ...profilePatch } = args.codingProfile ?? {};
|
|
554
|
+
const profileResult = CodingProfileSchema.safeParse({ ...currentProfile, ...profilePatch });
|
|
252
555
|
if (!profileResult.success)
|
|
253
556
|
throw invalidArguments("coding profile", profileResult.error);
|
|
254
557
|
nextProfile = profileResult.data;
|
|
558
|
+
if (args.codingProfile?.services !== undefined)
|
|
559
|
+
await requireCatalogServiceNames(tx, nextProfile.services);
|
|
255
560
|
const nextModel = args.model ?? existing.model;
|
|
256
|
-
|
|
561
|
+
const pairingChanges = args.model !== undefined ||
|
|
562
|
+
existing.kind !== "coding" ||
|
|
563
|
+
existing.codingProfile?.provider !== nextProfile.provider;
|
|
564
|
+
if (pairingChanges && !codingProviderSupportsModel(nextProfile.provider, nextModel)) {
|
|
257
565
|
throw new McpError(400, `Model "${nextModel}" is not supported by coding provider "${nextProfile.provider}".`);
|
|
258
566
|
}
|
|
259
567
|
}
|
|
@@ -264,14 +572,16 @@ export function registerAgentTools(mcp) {
|
|
|
264
572
|
if (nextKind === "coding" && nextSchedule && nextScheduleEnabled && !nextProfile?.defaultTask) {
|
|
265
573
|
throw new McpError(400, "A default task is required for an enabled coding-agent schedule.");
|
|
266
574
|
}
|
|
267
|
-
const { id, codingProfile: _profilePatch, ...updates } = args;
|
|
575
|
+
const { id, codingProfile: _profilePatch, repositoryAdminOverride: _override, ...updates } = args;
|
|
576
|
+
const stamp = authorization ? profileStamp(authorization) : {};
|
|
577
|
+
const trace = debugTraceUntil === undefined ? {} : { debugTraceUntil };
|
|
268
578
|
const profileMutation = nextKind === "native"
|
|
269
579
|
? existing.codingProfile
|
|
270
580
|
? { delete: true }
|
|
271
581
|
: undefined
|
|
272
582
|
: existing.codingProfile
|
|
273
|
-
? { update: nextProfile }
|
|
274
|
-
: { create: nextProfile };
|
|
583
|
+
? { update: { ...nextProfile, ...stamp, ...trace } }
|
|
584
|
+
: { create: { ...nextProfile, ...stamp, ...trace } };
|
|
275
585
|
return tx.agent.update({
|
|
276
586
|
where: { id },
|
|
277
587
|
data: {
|
|
@@ -281,6 +591,14 @@ export function registerAgentTools(mcp) {
|
|
|
281
591
|
include: { codingProfile: true },
|
|
282
592
|
});
|
|
283
593
|
}, { isolationLevel: Prisma.TransactionIsolationLevel.Serializable });
|
|
594
|
+
if (debugTraceUntil !== undefined) {
|
|
595
|
+
agentsLog.info({
|
|
596
|
+
event: "coding.debug_trace.set",
|
|
597
|
+
agentId: agent.id,
|
|
598
|
+
until: debugTraceUntil?.toISOString() ?? null,
|
|
599
|
+
by: ctx.principal.id,
|
|
600
|
+
}, "coding debug trace set");
|
|
601
|
+
}
|
|
284
602
|
return textResult(agent);
|
|
285
603
|
},
|
|
286
604
|
});
|
|
@@ -289,11 +607,9 @@ export function registerAgentTools(mcp) {
|
|
|
289
607
|
scope: "agents:read",
|
|
290
608
|
inputSchema: { type: "object", properties: {}, additionalProperties: false },
|
|
291
609
|
handler: async (_args, ctx) => {
|
|
292
|
-
const
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
});
|
|
296
|
-
return textResult(agents);
|
|
610
|
+
const [where, accessOf] = await Promise.all([readableAgentsWhere(ctx), agentAccessResolver(ctx)]);
|
|
611
|
+
const agents = await ctx.db.agent.findMany({ where, include: { codingProfile: true } });
|
|
612
|
+
return textResult(agents.map((agent) => ({ ...agent, access: accessOf(agent) })));
|
|
297
613
|
},
|
|
298
614
|
});
|
|
299
615
|
mcp.registerTool({
|
|
@@ -306,14 +622,20 @@ export function registerAgentTools(mcp) {
|
|
|
306
622
|
additionalProperties: false,
|
|
307
623
|
},
|
|
308
624
|
handler: async (args, ctx) => {
|
|
309
|
-
const agent = await ctx.db.agent.findUnique({
|
|
625
|
+
const { agent, access } = await assertAgentAccess(ctx, await ctx.db.agent.findUnique({
|
|
310
626
|
where: { id: args.id },
|
|
311
|
-
include: { tools: { include: { tool: true } }, codingProfile: true },
|
|
627
|
+
include: { tools: { include: { tool: true } }, codingProfile: true, repositories: true },
|
|
628
|
+
}), args.id, "read");
|
|
629
|
+
// A7: a tool's code and schema are its owner's, whoever can read the
|
|
630
|
+
// agent (the agent's owner included). Capabilities stay visible: config.
|
|
631
|
+
return textResult({
|
|
632
|
+
...agent,
|
|
633
|
+
access,
|
|
634
|
+
tools: agent.tools.map((attachment) => ({
|
|
635
|
+
...attachment,
|
|
636
|
+
tool: projectTool(attachment.tool, ctx.principal.id),
|
|
637
|
+
})),
|
|
312
638
|
});
|
|
313
|
-
if (!agent || !canRead(agent.ownerId, ctx.principal.id)) {
|
|
314
|
-
throw new McpError(404, `Agent "${args.id}" not found.`);
|
|
315
|
-
}
|
|
316
|
-
return textResult(agent);
|
|
317
639
|
},
|
|
318
640
|
});
|
|
319
641
|
mcp.registerTool({
|
|
@@ -326,9 +648,13 @@ export function registerAgentTools(mcp) {
|
|
|
326
648
|
additionalProperties: false,
|
|
327
649
|
},
|
|
328
650
|
handler: async (args, ctx) => {
|
|
329
|
-
await
|
|
651
|
+
await requireAgentAccess(ctx, args.id, "owner");
|
|
330
652
|
try {
|
|
331
|
-
|
|
653
|
+
// Grants have no FK to the resource: removed in the same transaction.
|
|
654
|
+
await ctx.db.$transaction(async (tx) => {
|
|
655
|
+
await deleteGrantsFor(tx, "agent", args.id);
|
|
656
|
+
await tx.agent.delete({ where: { id: args.id } });
|
|
657
|
+
});
|
|
332
658
|
}
|
|
333
659
|
catch (err) {
|
|
334
660
|
// Run.agentId has no onDelete rule, so Postgres refuses to delete an
|
|
@@ -343,33 +669,176 @@ export function registerAgentTools(mcp) {
|
|
|
343
669
|
return textResult({ deleted: args.id });
|
|
344
670
|
},
|
|
345
671
|
});
|
|
346
|
-
// Deliberately bypasses
|
|
347
|
-
//
|
|
348
|
-
//
|
|
349
|
-
//
|
|
350
|
-
//
|
|
351
|
-
//
|
|
352
|
-
//
|
|
672
|
+
// Deliberately bypasses access checks entirely: that's the point of this
|
|
673
|
+
// tool, the audited agents:admin escape hatch. Gated on agents:admin, a
|
|
674
|
+
// step up from agents:write, so an ordinary caller can never reach it
|
|
675
|
+
// regardless of what they own. The new owner must exist; releasing an
|
|
676
|
+
// agent to owner-less is gone (sharing is done with grants). Everything
|
|
677
|
+
// bound to the agent belongs to its owner, so a transfer drops what the
|
|
678
|
+
// new owner doesn't own (resource-sharing grants spec §3.6, R2-1).
|
|
353
679
|
mcp.registerTool({
|
|
354
680
|
name: "make_owner",
|
|
355
681
|
scope: "agents:admin",
|
|
682
|
+
description: "Admins: gives an agent a new owner (an existing principal id). Secret and datastore bindings the new owner doesn't own are removed; tool capabilities the new owner never granted are suspended until they re-run attach_tool; sub-agent edges the new owner can't delegate are removed; between two owners every grant on the agent is reset (adopting an owner-less agent keeps them). Returns what changed.",
|
|
356
683
|
inputSchema: {
|
|
357
684
|
type: "object",
|
|
358
685
|
additionalProperties: false,
|
|
359
|
-
properties: {
|
|
686
|
+
properties: {
|
|
687
|
+
agentId: { type: "string" },
|
|
688
|
+
ownerId: { type: ["string", "null"] },
|
|
689
|
+
keepEveryoneExecute: {
|
|
690
|
+
type: "boolean",
|
|
691
|
+
description: "When adopting an owner-less agent: keep an everyone grant at execute instead of lowering it to read. Anyone could then run it with the new owner's secrets and capabilities.",
|
|
692
|
+
},
|
|
693
|
+
},
|
|
360
694
|
required: ["agentId", "ownerId"],
|
|
361
695
|
},
|
|
362
696
|
handler: async (args, ctx) => {
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
throw new McpError(404, `Agent "${args.agentId}" not found.`);
|
|
366
|
-
if (args.ownerId !== null) {
|
|
367
|
-
const principal = await ctx.db.principal.findUnique({ where: { id: args.ownerId } });
|
|
368
|
-
if (!principal)
|
|
369
|
-
throw new McpError(400, `Principal "${args.ownerId}" not found.`);
|
|
697
|
+
if (args.ownerId === null) {
|
|
698
|
+
throw new McpError(400, "make_owner no longer releases an agent to owner-less; share it with grant_access instead (everyone at execute, or named principals).");
|
|
370
699
|
}
|
|
371
|
-
const
|
|
372
|
-
|
|
700
|
+
const newOwner = args.ownerId;
|
|
701
|
+
const principal = await ctx.db.principal.findUnique({ where: { id: newOwner } });
|
|
702
|
+
if (!principal)
|
|
703
|
+
throw new McpError(400, `Principal "${newOwner}" not found.`);
|
|
704
|
+
const result = await ctx.db.$transaction(async (tx) => {
|
|
705
|
+
const agent = await tx.agent.findUnique({ where: { id: args.agentId }, include: { codingProfile: true } });
|
|
706
|
+
if (!agent)
|
|
707
|
+
throw agentNotFound(args.agentId);
|
|
708
|
+
// Admin and grandfathered repository approvals were granted to the
|
|
709
|
+
// agent under its current owner. Moving it away from that owner
|
|
710
|
+
// turns them into ordinary checks of the next owner's own GitHub
|
|
711
|
+
// access, so an approval never travels with the agent. An
|
|
712
|
+
// owner-less agent getting its first owner keeps them: that is the
|
|
713
|
+
// pre-upgrade step for owner-less agents with repositories.
|
|
714
|
+
const revoked = [];
|
|
715
|
+
const transfer = agent.ownerId !== null && agent.ownerId !== newOwner;
|
|
716
|
+
if (transfer) {
|
|
717
|
+
const approved = { in: ["admin", "grandfathered"] };
|
|
718
|
+
const reset = { authorizedVia: "host_permission", authorizedById: null, authorizedAt: new Date() };
|
|
719
|
+
const links = await tx.agentRepository.findMany({
|
|
720
|
+
where: { agentId: agent.id, authorizedVia: approved },
|
|
721
|
+
orderBy: { repository: "asc" },
|
|
722
|
+
});
|
|
723
|
+
await tx.agentRepository.updateMany({ where: { agentId: agent.id, authorizedVia: approved }, data: reset });
|
|
724
|
+
revoked.push(...links.map((l) => l.repository));
|
|
725
|
+
const profile = await tx.codingAgentProfile.updateMany({
|
|
726
|
+
where: { agentId: agent.id, repositoryAuthorizedVia: approved },
|
|
727
|
+
data: {
|
|
728
|
+
repositoryAuthorizedVia: reset.authorizedVia,
|
|
729
|
+
repositoryAuthorizedById: null,
|
|
730
|
+
repositoryAuthorizedAt: reset.authorizedAt,
|
|
731
|
+
},
|
|
732
|
+
});
|
|
733
|
+
if (profile.count > 0 && agent.codingProfile)
|
|
734
|
+
revoked.push(agent.codingProfile.repository);
|
|
735
|
+
}
|
|
736
|
+
// Grants: between two owners every grant is reset (the new owner
|
|
737
|
+
// decides who shares it); adopting an owner-less agent keeps them,
|
|
738
|
+
// but lowers an everyone grant to read unless keepEveryoneExecute:
|
|
739
|
+
// the agent may regain the new owner's secrets and capabilities
|
|
740
|
+
// (review I3).
|
|
741
|
+
const grantsReset = transfer ? await deleteGrantsFor(tx, "agent", agent.id) : 0;
|
|
742
|
+
let everyoneGrant = null;
|
|
743
|
+
if (agent.ownerId === null) {
|
|
744
|
+
const everyone = await tx.resourceGrant.findFirst({
|
|
745
|
+
where: { resourceType: "agent", resourceId: agent.id, granteeKey: EVERYONE_KEY },
|
|
746
|
+
});
|
|
747
|
+
if (everyone) {
|
|
748
|
+
everyoneGrant = { before: everyone.level, after: everyone.level };
|
|
749
|
+
if (everyone.level !== "read" && args.keepEveryoneExecute !== true) {
|
|
750
|
+
await tx.resourceGrant.update({
|
|
751
|
+
where: { id: everyone.id },
|
|
752
|
+
data: { level: "read", source: "operator", grantedById: ctx.principal.id },
|
|
753
|
+
});
|
|
754
|
+
everyoneGrant.after = "read";
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
const updated = await tx.agent.update({ where: { id: args.agentId }, data: { ownerId: newOwner } });
|
|
759
|
+
// Secret/datastore bindings the new owner doesn't own: removed.
|
|
760
|
+
const secrets = await tx.agentSecret.findMany({
|
|
761
|
+
where: { agentId: agent.id },
|
|
762
|
+
include: { secret: { select: { ownerId: true } } },
|
|
763
|
+
});
|
|
764
|
+
const foreignSecrets = secrets.filter((b) => b.secret.ownerId !== newOwner);
|
|
765
|
+
if (foreignSecrets.length > 0) {
|
|
766
|
+
await tx.agentSecret.deleteMany({
|
|
767
|
+
where: { agentId: agent.id, secretId: { in: foreignSecrets.map((b) => b.secretId) } },
|
|
768
|
+
});
|
|
769
|
+
}
|
|
770
|
+
const datastores = await tx.agentDatastore.findMany({
|
|
771
|
+
where: { agentId: agent.id },
|
|
772
|
+
include: { datastore: { select: { ownerId: true } } },
|
|
773
|
+
});
|
|
774
|
+
const foreignDatastores = datastores.filter((b) => b.datastore.ownerId !== newOwner);
|
|
775
|
+
if (foreignDatastores.length > 0) {
|
|
776
|
+
await tx.agentDatastore.deleteMany({
|
|
777
|
+
where: { agentId: agent.id, datastoreId: { in: foreignDatastores.map((b) => b.datastoreId) } },
|
|
778
|
+
});
|
|
779
|
+
}
|
|
780
|
+
const bindingsRemoved = [
|
|
781
|
+
...foreignSecrets.map((b) => ({ kind: "secret", boundName: b.boundName, resourceId: b.secretId })),
|
|
782
|
+
...foreignDatastores.map((b) => ({ kind: "datastore", boundName: b.boundName, resourceId: b.datastoreId })),
|
|
783
|
+
];
|
|
784
|
+
// Tool capabilities: rows aren't rewritten; the runner ignores any
|
|
785
|
+
// the new owner never stamped (capabilitiesGrantedById).
|
|
786
|
+
const attachments = await tx.agentTool.findMany({
|
|
787
|
+
where: { agentId: agent.id },
|
|
788
|
+
include: { tool: { select: { id: true, name: true } } },
|
|
789
|
+
});
|
|
790
|
+
const toolCapabilitiesSuspended = attachments
|
|
791
|
+
.filter((at) => at.capabilitiesGrantedById !== newOwner)
|
|
792
|
+
.map((at) => ({ toolId: at.tool.id, toolName: at.tool.name }));
|
|
793
|
+
// Sub-agent edges (both directions) the new owner can't delegate.
|
|
794
|
+
const edges = await tx.agentSubAgent.findMany({
|
|
795
|
+
where: { OR: [{ parentAgentId: agent.id }, { childAgentId: agent.id }] },
|
|
796
|
+
include: {
|
|
797
|
+
parent: { select: { id: true, ownerId: true } },
|
|
798
|
+
child: { select: { id: true, ownerId: true } },
|
|
799
|
+
},
|
|
800
|
+
});
|
|
801
|
+
const subAgentEdgesRemoved = [];
|
|
802
|
+
for (const edge of edges) {
|
|
803
|
+
const parent = edge.parentAgentId === agent.id ? { ownerId: newOwner } : edge.parent;
|
|
804
|
+
const child = edge.childAgentId === agent.id ? { id: agent.id, ownerId: newOwner } : edge.child;
|
|
805
|
+
if (!(await canDelegate(tx, parent, child))) {
|
|
806
|
+
subAgentEdgesRemoved.push({
|
|
807
|
+
parentAgentId: edge.parentAgentId,
|
|
808
|
+
childAgentId: edge.childAgentId,
|
|
809
|
+
boundName: edge.boundName,
|
|
810
|
+
});
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
for (const edge of subAgentEdgesRemoved) {
|
|
814
|
+
await tx.agentSubAgent.deleteMany({
|
|
815
|
+
where: { parentAgentId: edge.parentAgentId, childAgentId: edge.childAgentId },
|
|
816
|
+
});
|
|
817
|
+
}
|
|
818
|
+
// Webhooks others created stay, but fire only while their creator
|
|
819
|
+
// may still trigger the agent (core/webhooks.ts).
|
|
820
|
+
const webhooks = await tx.webhook.findMany({ where: { agentId: agent.id } });
|
|
821
|
+
const webhooksInactive = [];
|
|
822
|
+
for (const webhook of webhooks) {
|
|
823
|
+
if (webhook.ownerId === newOwner)
|
|
824
|
+
continue;
|
|
825
|
+
const creatorAccess = await effectiveAccess(tx, "agent", updated, webhook.ownerId);
|
|
826
|
+
if (!atLeast("agent", creatorAccess, "execute")) {
|
|
827
|
+
webhooksInactive.push({ id: webhook.id, createdBy: webhook.ownerId });
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
return {
|
|
831
|
+
...updated,
|
|
832
|
+
repositoryApprovalsRevoked: revoked,
|
|
833
|
+
bindingsRemoved,
|
|
834
|
+
toolCapabilitiesSuspended,
|
|
835
|
+
subAgentEdgesRemoved,
|
|
836
|
+
grantsReset,
|
|
837
|
+
everyoneGrant,
|
|
838
|
+
webhooksInactive,
|
|
839
|
+
};
|
|
840
|
+
}, { isolationLevel: Prisma.TransactionIsolationLevel.Serializable });
|
|
841
|
+
return textResult(result);
|
|
373
842
|
},
|
|
374
843
|
});
|
|
375
844
|
}
|