@wardby/cli 0.7.0 → 0.9.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 +5 -5
- package/README.md +6 -1
- package/deploy/local/docker-compose.quickstart-coding.yml +11 -0
- package/dist/claude-coding-worker/driver.d.ts +12 -0
- package/dist/claude-coding-worker/driver.js +29 -7
- package/dist/claude-coding-worker/main.js +5 -1
- package/dist/claude-coding-worker/sdk.js +17 -1
- package/dist/claude-plugin/command.d.ts +64 -0
- package/dist/claude-plugin/command.js +379 -0
- package/dist/cli-help.d.ts +1 -1
- package/dist/cli-help.js +2 -1
- package/dist/cli.js +71 -3
- package/dist/coding/code-host.d.ts +16 -0
- package/dist/coding/code-host.js +60 -0
- package/dist/coding/mcp-token.d.ts +12 -0
- package/dist/coding/mcp-token.js +26 -0
- package/dist/coding/protocol.js +2 -0
- package/dist/coding/redact.js +6 -1
- package/dist/coding/transcript/recorder.d.ts +5 -4
- package/dist/coding/transcript/recorder.js +3 -9
- package/dist/coding-worker/debug-trace.d.ts +3 -1
- package/dist/coding-worker/debug-trace.js +4 -2
- package/dist/coding-worker/driver.js +8 -1
- package/dist/coding-worker/main.js +5 -1
- package/dist/coding-worker/mcp-config.d.ts +27 -0
- package/dist/coding-worker/mcp-config.js +32 -0
- package/dist/coding-worker/sdk.d.ts +36 -0
- package/dist/coding-worker/sdk.js +28 -1
- package/dist/coding-worker/types.d.ts +4 -0
- package/dist/config/mcp-call-limit.d.ts +8 -0
- package/dist/config/mcp-call-limit.js +18 -0
- package/dist/config/mcp-servers.d.ts +8 -0
- package/dist/config/mcp-servers.js +12 -0
- package/dist/config/test-simulators.d.ts +10 -0
- package/dist/config/test-simulators.js +28 -0
- package/dist/config/tool-timeout.d.ts +1 -0
- package/dist/config/tool-timeout.js +18 -0
- package/dist/core/coding-mcp.d.ts +36 -0
- package/dist/core/coding-mcp.js +253 -0
- package/dist/core/coding-repository.d.ts +46 -0
- package/dist/core/coding-repository.js +92 -0
- package/dist/core/delegation-slots.d.ts +27 -0
- package/dist/core/delegation-slots.js +44 -0
- package/dist/core/dispatch.d.ts +6 -2
- package/dist/core/dispatch.js +39 -11
- package/dist/core/engine-native.d.ts +2 -0
- package/dist/core/engine-native.js +13 -0
- package/dist/core/error-code.js +2 -0
- package/dist/core/grants.d.ts +28 -3
- package/dist/core/grants.js +46 -4
- package/dist/core/host-events.js +7 -3
- package/dist/core/host-status.js +2 -2
- package/dist/core/issue-status.js +4 -3
- package/dist/core/listen-address.d.ts +10 -0
- package/dist/core/listen-address.js +14 -0
- package/dist/core/mark-ready.d.ts +1 -1
- package/dist/core/mark-ready.js +1 -1
- package/dist/core/mcp-client/client.d.ts +47 -0
- package/dist/core/mcp-client/client.js +141 -0
- package/dist/core/mcp-client/jsonrpc.d.ts +15 -0
- package/dist/core/mcp-client/jsonrpc.js +54 -0
- package/dist/core/mcp-client/results.d.ts +13 -0
- package/dist/core/mcp-client/results.js +42 -0
- package/dist/core/mcp-servers.d.ts +90 -0
- package/dist/core/mcp-servers.js +230 -0
- package/dist/core/mcp-tools.d.ts +108 -0
- package/dist/core/mcp-tools.js +287 -0
- package/dist/core/merge-order-check.js +38 -19
- package/dist/core/notification-dispatcher.js +2 -1
- package/dist/core/power-idle.d.ts +8 -2
- package/dist/core/power-idle.js +17 -3
- package/dist/core/power.d.ts +5 -0
- package/dist/core/power.js +5 -0
- package/dist/core/related-pull-requests.js +26 -10
- package/dist/core/retention-days.d.ts +5 -0
- package/dist/core/retention-days.js +14 -0
- package/dist/core/review-fix.js +2 -2
- package/dist/core/review-host-tools.js +3 -1
- package/dist/core/run-steps/config.d.ts +25 -0
- package/dist/core/run-steps/config.js +40 -0
- package/dist/core/run-steps/control-chars.d.ts +16 -0
- package/dist/core/run-steps/control-chars.js +26 -0
- package/dist/core/run-steps/detail.d.ts +32 -0
- package/dist/core/run-steps/detail.js +240 -0
- package/dist/core/run-steps/extract.d.ts +52 -0
- package/dist/core/run-steps/extract.js +467 -0
- package/dist/core/run-steps/ingress.d.ts +51 -0
- package/dist/core/run-steps/ingress.js +160 -0
- package/dist/core/run-steps/last-step.d.ts +15 -0
- package/dist/core/run-steps/last-step.js +35 -0
- package/dist/core/run-steps/native.d.ts +48 -0
- package/dist/core/run-steps/native.js +164 -0
- package/dist/core/run-steps/seen.d.ts +16 -0
- package/dist/core/run-steps/seen.js +35 -0
- package/dist/core/run-steps/summarize.d.ts +57 -0
- package/dist/core/run-steps/summarize.js +304 -0
- package/dist/core/run-steps/sweep.d.ts +21 -0
- package/dist/core/run-steps/sweep.js +41 -0
- package/dist/core/run-steps/types.d.ts +89 -0
- package/dist/core/run-steps/types.js +39 -0
- package/dist/core/run-steps/wire.d.ts +35 -0
- package/dist/core/run-steps/wire.js +47 -0
- package/dist/core/run-steps/writer.d.ts +49 -0
- package/dist/core/run-steps/writer.js +279 -0
- package/dist/core/runner.d.ts +70 -16
- package/dist/core/runner.js +296 -74
- package/dist/core/secret-broker-config.d.ts +2 -0
- package/dist/core/secret-broker-config.js +1 -1
- package/dist/core/secret-guard.d.ts +5 -2
- package/dist/core/secret-guard.js +22 -7
- package/dist/core/secrets.d.ts +11 -1
- package/dist/core/secrets.js +27 -1
- package/dist/core/serializable-retry.d.ts +18 -0
- package/dist/core/serializable-retry.js +43 -0
- package/dist/core/tool-admin.d.ts +5 -1
- package/dist/core/tool-admin.js +7 -2
- package/dist/core/tool-names.d.ts +13 -0
- package/dist/core/tool-names.js +20 -0
- package/dist/core/webhooks.d.ts +4 -0
- package/dist/core/webhooks.js +68 -49
- package/dist/core/workflow-run-events.js +2 -1
- package/dist/generated/prisma/browser.d.ts +39 -0
- package/dist/generated/prisma/client.d.ts +39 -0
- package/dist/generated/prisma/commonInputTypes.d.ts +80 -50
- package/dist/generated/prisma/enums.d.ts +6 -0
- package/dist/generated/prisma/enums.js +5 -0
- package/dist/generated/prisma/internal/class.d.ts +77 -0
- package/dist/generated/prisma/internal/class.js +4 -4
- package/dist/generated/prisma/internal/prismaNamespace.d.ts +632 -1
- package/dist/generated/prisma/internal/prismaNamespace.js +94 -3
- package/dist/generated/prisma/internal/prismaNamespaceBrowser.d.ts +98 -0
- package/dist/generated/prisma/internal/prismaNamespaceBrowser.js +94 -3
- package/dist/generated/prisma/models/Agent.d.ts +372 -1
- package/dist/generated/prisma/models/AgentMcpServer.d.ts +1412 -0
- package/dist/generated/prisma/models/AgentMcpServer.js +1 -0
- package/dist/generated/prisma/models/AgentRepository.d.ts +40 -1
- package/dist/generated/prisma/models/AuthUser.d.ts +1 -1
- package/dist/generated/prisma/models/CodingAgentProfile.d.ts +0 -7
- package/dist/generated/prisma/models/CodingProxySession.d.ts +159 -2
- package/dist/generated/prisma/models/CodingRun.d.ts +76 -1
- package/dist/generated/prisma/models/HostProvider.d.ts +2176 -0
- package/dist/generated/prisma/models/HostProvider.js +1 -0
- package/dist/generated/prisma/models/HostProviderCredential.d.ts +1285 -0
- package/dist/generated/prisma/models/HostProviderCredential.js +1 -0
- package/dist/generated/prisma/models/HostProviderOperation.d.ts +1470 -0
- package/dist/generated/prisma/models/HostProviderOperation.js +1 -0
- package/dist/generated/prisma/models/HostProviderTrustedAccount.d.ts +1145 -0
- package/dist/generated/prisma/models/HostProviderTrustedAccount.js +1 -0
- package/dist/generated/prisma/models/McpServer.d.ts +1500 -0
- package/dist/generated/prisma/models/McpServer.js +1 -0
- package/dist/generated/prisma/models/NativeGatewayCall.d.ts +39 -1
- package/dist/generated/prisma/models/Principal.d.ts +167 -0
- package/dist/generated/prisma/models/ResourceGrant.d.ts +1 -1
- package/dist/generated/prisma/models/Run.d.ts +503 -1
- package/dist/generated/prisma/models/RunStep.d.ts +1453 -0
- package/dist/generated/prisma/models/RunStep.js +1 -0
- package/dist/generated/prisma/models/Secret.d.ts +387 -0
- package/dist/generated/prisma/models/Tool.d.ts +223 -1
- package/dist/generated/prisma/models.d.ts +7 -0
- package/dist/help/search.js +12 -3
- package/dist/help-index.json +812 -52
- package/dist/import/create.js +16 -1
- package/dist/import/neutral-schema.d.ts +1 -0
- package/dist/import/neutral-schema.js +2 -0
- package/dist/mcp/auth/access.d.ts +1 -0
- package/dist/mcp/auth/grants-cli.d.ts +6 -4
- package/dist/mcp/auth/grants-cli.js +42 -3
- package/dist/mcp/auth/resource-server.d.ts +3 -1
- package/dist/mcp/auth/resource-server.js +17 -2
- package/dist/mcp/context.d.ts +6 -0
- package/dist/mcp/index.js +21 -0
- package/dist/mcp/tools/agents.js +135 -20
- package/dist/mcp/tools/grants.js +66 -28
- package/dist/mcp/tools/host-providers.d.ts +2 -0
- package/dist/mcp/tools/host-providers.js +215 -0
- package/dist/mcp/tools/mcp-servers.d.ts +26 -0
- package/dist/mcp/tools/mcp-servers.js +481 -0
- package/dist/mcp/tools/power.js +2 -2
- package/dist/mcp/tools/repositories.js +50 -10
- package/dist/mcp/tools/run-access.d.ts +7 -0
- package/dist/mcp/tools/run-access.js +14 -0
- package/dist/mcp/tools/run-steps.d.ts +16 -0
- package/dist/mcp/tools/run-steps.js +85 -0
- package/dist/mcp/tools/runs.js +186 -18
- package/dist/mcp/tools/secret-elicitation.d.ts +2 -0
- package/dist/mcp/tools/secret-elicitation.js +25 -2
- package/dist/mcp/tools/secrets.js +38 -4
- package/dist/mcp/tools/tools.d.ts +6 -4
- package/dist/mcp/tools/tools.js +40 -8
- package/dist/mcp/tools/trigger.js +28 -6
- package/dist/mcp/transport/http-limits.d.ts +2 -0
- package/dist/mcp/transport/http-limits.js +6 -2
- package/dist/mcp/webhooks/ingress.js +10 -0
- package/dist/native-worker/gateway-server.d.ts +18 -1
- package/dist/native-worker/gateway-server.js +38 -9
- package/dist/native-worker/gateway.d.ts +5 -0
- package/dist/native-worker/gateway.js +13 -1
- package/dist/native-worker/http-server.d.ts +6 -4
- package/dist/native-worker/http-server.js +53 -8
- package/dist/native-worker/ledger.d.ts +6 -2
- package/dist/native-worker/ledger.js +5 -4
- package/dist/native-worker/protocol.d.ts +1 -0
- package/dist/native-worker/protocol.js +4 -1
- package/dist/providers/coding-proxy/capability-hash.d.ts +2 -0
- package/dist/providers/coding-proxy/capability-hash.js +5 -0
- package/dist/providers/coding-proxy/mcp-route.d.ts +34 -0
- package/dist/providers/coding-proxy/mcp-route.js +166 -0
- package/dist/providers/coding-proxy/memory-ledger.d.ts +2 -0
- package/dist/providers/coding-proxy/memory-ledger.js +16 -0
- package/dist/providers/coding-proxy/prisma-ledger.d.ts +1 -0
- package/dist/providers/coding-proxy/prisma-ledger.js +36 -2
- package/dist/providers/coding-proxy/proxy.d.ts +33 -1
- package/dist/providers/coding-proxy/proxy.js +266 -75
- package/dist/providers/coding-proxy/run-step-forwarder.d.ts +42 -0
- package/dist/providers/coding-proxy/run-step-forwarder.js +206 -0
- package/dist/providers/coding-proxy/runtime.d.ts +13 -0
- package/dist/providers/coding-proxy/runtime.js +87 -4
- package/dist/providers/coding-proxy/server.d.ts +3 -0
- package/dist/providers/coding-proxy/server.js +10 -0
- package/dist/providers/coding-proxy/types.d.ts +18 -0
- package/dist/providers/engine/types.d.ts +26 -0
- package/dist/providers/executor/composition.js +5 -0
- package/dist/providers/executor/container.d.ts +47 -1
- package/dist/providers/executor/container.js +84 -8
- package/dist/providers/host-tools/invoke.d.ts +37 -0
- package/dist/providers/host-tools/invoke.js +117 -0
- package/dist/providers/host-tools/operations.d.ts +65 -0
- package/dist/providers/host-tools/operations.js +149 -0
- package/dist/providers/host-tools/registry.d.ts +79 -0
- package/dist/providers/host-tools/registry.js +362 -0
- package/dist/providers/host-tools/request-guard.d.ts +17 -0
- package/dist/providers/host-tools/request-guard.js +99 -0
- package/dist/providers/host-tools/self-test.d.ts +64 -0
- package/dist/providers/host-tools/self-test.js +142 -0
- package/dist/providers/jobs/docker-isolation.d.ts +2 -2
- package/dist/providers/jobs/docker-isolation.js +30 -8
- package/dist/providers/jobs/docker.d.ts +8 -0
- package/dist/providers/jobs/docker.js +33 -9
- package/dist/providers/jobs/kubernetes-isolation.d.ts +3 -1
- package/dist/providers/jobs/kubernetes-isolation.js +15 -2
- package/dist/providers/jobs/kubernetes.d.ts +13 -0
- package/dist/providers/jobs/kubernetes.js +28 -5
- package/dist/providers/jobs/types.d.ts +6 -0
- package/dist/providers/review-host/types.d.ts +6 -4
- package/dist/providers/review-host/types.js +1 -1
- package/dist/providers/vcs/git.d.ts +6 -2
- package/dist/providers/vcs/git.js +15 -4
- package/dist/providers/vcs/github.d.ts +2 -71
- package/dist/providers/vcs/github.js +8 -11
- package/dist/providers/vcs/index.d.ts +1 -1
- package/dist/providers/vcs/index.js +1 -1
- package/dist/providers/vcs/remote.d.ts +4 -0
- package/dist/providers/vcs/remote.js +7 -1
- package/dist/providers/vcs/routing.d.ts +12 -6
- package/dist/providers/vcs/routing.js +21 -6
- package/dist/providers/vcs/types.d.ts +71 -3
- package/dist/quickstart/index.d.ts +5 -0
- package/dist/quickstart/index.js +21 -0
- package/dist/quickstart-images.json +1 -1
- package/dist/sandbox/eval-core.d.ts +12 -2
- package/dist/sandbox/eval-core.js +20 -4
- package/dist/sandbox/host-functions.d.ts +4 -0
- package/dist/sandbox/host-functions.js +7 -2
- package/dist/sandbox/limits.d.ts +4 -1
- package/dist/sandbox/limits.js +4 -1
- package/dist/sandbox/run-in-sandbox.d.ts +6 -0
- package/dist/sandbox/run-in-sandbox.js +14 -2
- package/dist/sandbox/safe-fetch.d.ts +2 -0
- package/dist/sandbox/safe-fetch.js +1 -1
- package/dist/sandbox/secret-broker.d.ts +1 -0
- package/dist/sandbox/secret-broker.js +1 -1
- package/dist/sandbox/tool-timeout.d.ts +32 -0
- package/dist/sandbox/tool-timeout.js +52 -0
- package/dist/sandbox/user-tool.d.ts +8 -1
- package/dist/sandbox/user-tool.js +9 -3
- package/dist/serve.js +26 -18
- package/dist/viewer/api-schema.d.ts +66 -0
- package/dist/viewer/api-schema.js +31 -0
- package/dist/viewer/graph.js +2 -1
- package/dist/viewer/openapi.js +2 -1
- package/dist/viewer/run-detail.d.ts +2 -0
- package/dist/viewer/run-detail.js +26 -1
- package/dist/wardby-bin.js +5 -0
- package/docs/README.md +11 -1
- package/docs/agent-recipes.md +37 -4
- package/docs/architecture-runtime.md +1 -1
- package/docs/claude-code-plugin.md +208 -0
- package/docs/code-review-agents.md +6 -0
- package/docs/coding-agent-setup.md +94 -1
- package/docs/coding-worker-byo-images.md +4 -2
- package/docs/coding-worker-isolation.md +94 -7
- package/docs/debugging-agent-runs.md +354 -0
- package/docs/getting-started-gke.md +33 -11
- package/docs/getting-started-identity-provider.md +43 -37
- package/docs/getting-started.md +8 -0
- package/docs/host-providers.md +294 -0
- package/docs/mcp-servers.md +263 -0
- package/docs/native-sandbox.md +31 -9
- package/docs/security-deployment.md +161 -31
- package/docs/tool-secrets.md +3 -0
- package/docs/viewer-api.md +27 -12
- package/docs/viewer-api.openapi.yml +78 -2
- package/help/admin-viewer.md +5 -0
- package/help/agent-recipes.md +33 -2
- package/help/brokered-secrets.md +3 -0
- package/help/build-claude-code-image.md +1 -1
- package/help/claude-code-run-pane.md +63 -0
- package/help/deploy-gke.md +5 -0
- package/help/errors/coding-mcp-gateway-unavailable.md +46 -0
- package/help/errors/coding-mcp-pins-unavailable.md +35 -0
- package/help/errors/database-auth-invalid.md +1 -1
- package/help/errors/host-provider-error.md +40 -0
- package/help/errors/host-tool-failed.md +36 -0
- package/help/errors/mcp-call-limit.md +22 -0
- package/help/errors/mcp-call-timeout.md +22 -0
- package/help/errors/mcp-server-auth-failed.md +22 -0
- package/help/errors/mcp-server-credentials-unavailable.md +45 -0
- package/help/errors/mcp-server-error.md +28 -0
- package/help/errors/mcp-server-unreachable.md +22 -0
- package/help/errors/mcp-servers-off.md +21 -0
- package/help/errors/mcp-tool-not-allowed.md +26 -0
- package/help/errors/mcp-tool-removed.md +22 -0
- package/help/errors/mcp-unavailable.md +25 -0
- package/help/errors/native-sandbox-capacity.md +1 -1
- package/help/errors/native-sandbox-deadline-exceeded.md +1 -1
- package/help/errors/native-sandbox-docker-failed.md +1 -1
- package/help/errors/native-sandbox-gateway-error.md +1 -1
- package/help/errors/native-sandbox-gateway-unavailable.md +1 -1
- package/help/errors/native-sandbox-image-not-pinned.md +1 -1
- package/help/errors/native-sandbox-isolation-mismatch.md +1 -1
- package/help/errors/native-sandbox-network-unenforced.md +1 -1
- package/help/errors/native-sandbox-requires-catalog.md +1 -1
- package/help/errors/native-sandbox-unavailable.md +1 -1
- package/help/errors/native-sandbox-warm-delivery-failed.md +1 -1
- package/help/errors/native-sandbox-worker-exited.md +1 -1
- package/help/errors/native-sandbox-worker-lost.md +1 -1
- package/help/errors/native-sandbox-worker-unready.md +1 -1
- package/help/errors/repository-not-listed.md +36 -0
- package/help/errors/repository-required.md +36 -0
- package/help/errors/tool-timeout.md +38 -0
- package/help/host-providers.md +40 -0
- package/help/identity-and-access.md +4 -1
- package/help/low-power-mode.md +19 -8
- package/help/mcp-servers.md +73 -0
- package/help/mcp.md +9 -2
- package/help/native-capabilities.md +27 -1
- package/help/native-sandbox.md +1 -1
- package/help/operating-agents.md +16 -0
- package/help/related-pull-requests.md +7 -3
- package/help/repo-instructions-and-skills.md +3 -1
- package/help/resume-runs-locally.md +7 -0
- package/help/run-steps.md +140 -0
- package/help/stop-run.md +1 -1
- package/help/troubleshooting/coding-workers.md +9 -1
- package/help/troubleshooting/debugging-agent-runs.md +29 -0
- package/package.json +2 -1
- package/prisma/migrations/20261102000000_host_providers/migration.sql +56 -0
- package/prisma/migrations/20261104000000_coding_agent_repository_list/migration.sql +5 -0
- package/prisma/migrations/20261106000000_tool_timeout/migration.sql +4 -0
- package/prisma/migrations/20261107000000_mcp_servers/migration.sql +33 -0
- package/prisma/migrations/20261108000000_coding_mcp/migration.sql +5 -0
- package/prisma/migrations/20261109000000_task_run_index/migration.sql +3 -0
- package/prisma/migrations/20261110000000_run_steps/migration.sql +26 -0
- package/prisma/migrations/20261111000000_run_steps_viewer_notify/migration.sql +59 -0
- package/prisma/migrations/20261112000000_run_steps_detail/migration.sql +9 -0
- package/prisma/schema.prisma +211 -14
- package/scripts/git-askpass.sh +1 -1
package/dist/mcp/tools/agents.js
CHANGED
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import { Prisma } from "#prisma";
|
|
9
9
|
import { z } from "zod";
|
|
10
|
+
import { RUN_STEPS_LEVELS, loadRunStepsLevel, runStepsAboveCeilingWarning, } from "../../core/run-steps/config.js";
|
|
11
|
+
import { codeProviderOf } from "../../coding/code-host.js";
|
|
10
12
|
import { CodingProfilePatchSchema, CodingProfileSchema } from "../../coding/profile.js";
|
|
11
13
|
import { DEFAULT_CLAUDE_MAX_OUTPUT_TOKENS, DEFAULT_CLAUDE_MAX_TURNS, MAX_CLAUDE_MAX_OUTPUT_TOKENS, MIN_CLAUDE_MAX_OUTPUT_TOKENS, DEFAULT_DEBUG_TRACE_MAX_MB, MAX_CODING_TURNS, MAX_DEBUG_TRACE_MAX_MB, isLocalRepository, } from "../../coding/protocol.js";
|
|
12
14
|
import { loadBlobStoreConfig, loadContainerExecutorConfig } from "../../config/providers.js";
|
|
@@ -18,12 +20,15 @@ import { ModelUnavailableError } from "../../providers/llm/catalog-types.js";
|
|
|
18
20
|
import { currentModelCatalog } from "../../providers/llm/catalog-store.js";
|
|
19
21
|
import { validateCronExpression } from "../../core/cron.js";
|
|
20
22
|
import { scheduleStartFields } from "../../core/schedule-start.js";
|
|
23
|
+
import { preferIndexScans, withSerializableRetry } from "../../core/serializable-retry.js";
|
|
21
24
|
import { modelSupportedEfforts } from "../../providers/llm/routing.js";
|
|
25
|
+
import { agentMcpServerViews, codexUnavailableMcpTools, codexWriteToolsWarning } from "./mcp-servers.js";
|
|
26
|
+
import { isCodexCoding } from "../../core/mcp-tools.js";
|
|
22
27
|
import { PROJECT_KEY } from "../../providers/issue-tracker/types.js";
|
|
23
28
|
import { LLM_EFFORT_LEVELS, isLlmEffort } from "../../providers/llm/types.js";
|
|
24
29
|
import { requireReadableBudgetGroup } from "../auth/ownership.js";
|
|
25
30
|
import { agentAccessResolver, agentNotFound, assertAgentAccess, readableAgentsWhere, requireAgentAccess, } from "../auth/access.js";
|
|
26
|
-
import { EVERYONE_KEY, atLeast, canDelegate, deleteGrantsFor, effectiveAccess, isPublicWritable, } from "../../core/grants.js";
|
|
31
|
+
import { EVERYONE_KEY, atLeast, canDelegate, deleteGrantsFor, effectiveAccess, isPublicWritable, filterUnusableMcpServers, } from "../../core/grants.js";
|
|
27
32
|
import { projectTool } from "./tools.js";
|
|
28
33
|
import { withZod3ToolWarnings } from "../../core/zod3-window.js";
|
|
29
34
|
import { requireAnyScope, requireScope } from "../auth/resource-server.js";
|
|
@@ -120,6 +125,27 @@ function withProfileWarnings(agent) {
|
|
|
120
125
|
const warnings = profileWarnings(profile, now);
|
|
121
126
|
return warnings.length ? { ...shown, warnings } : shown;
|
|
122
127
|
}
|
|
128
|
+
// No JSON-schema enum on purpose: the zod check then refuses a bad value naming off, summary and detail.
|
|
129
|
+
const RUN_STEPS_DESCRIPTION = "Run steps level for this agent's runs (owner only): off records nothing, summary one line per tool call or message " +
|
|
130
|
+
"(the default), detail also the tool input, the end of each tool's output and the model's text, readable only by the " +
|
|
131
|
+
"agent's owner and the run's starter. A run records the lower of this and the instance's WARDBY_RUN_STEPS, fixed " +
|
|
132
|
+
"when it starts. detail stores tool output: secrets the run was given are removed, but a secret that exists only in " +
|
|
133
|
+
"a file the run read can remain. Null clears it (summary).";
|
|
134
|
+
/** For a warning only: an invalid WARDBY_RUN_STEPS stopped start-up already; never fails the tool call. */
|
|
135
|
+
function runStepsCeiling() {
|
|
136
|
+
try {
|
|
137
|
+
return loadRunStepsLevel(process.env);
|
|
138
|
+
}
|
|
139
|
+
catch {
|
|
140
|
+
return undefined;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
/** An agent as returned to operators: a warning when its runSteps is above the instance's ceiling. */
|
|
144
|
+
function withRunStepsWarning(agent) {
|
|
145
|
+
const ceiling = runStepsCeiling();
|
|
146
|
+
const warning = ceiling ? runStepsAboveCeilingWarning(agent.runSteps, ceiling) : undefined;
|
|
147
|
+
return warning ? { ...agent, warnings: [...(agent.warnings ?? []), warning] } : agent;
|
|
148
|
+
}
|
|
123
149
|
const agentFields = {
|
|
124
150
|
name: z.string().trim().min(1).max(MAX_AGENT_NAME_CHARS),
|
|
125
151
|
systemPrompt: z.string().min(1).max(MAX_SYSTEM_PROMPT_CHARS),
|
|
@@ -136,6 +162,7 @@ const agentFields = {
|
|
|
136
162
|
budgetGroupId: z.string().min(1).max(128),
|
|
137
163
|
memoryEnabled: z.boolean(),
|
|
138
164
|
effort: z.enum(LLM_EFFORT_LEVELS),
|
|
165
|
+
runSteps: z.enum(RUN_STEPS_LEVELS),
|
|
139
166
|
nativeExecutionMode: z.enum(NATIVE_EXECUTION_MODES),
|
|
140
167
|
defectProjectKey: z.string().regex(PROJECT_KEY, "must be an upper-case Jira project key"),
|
|
141
168
|
defectIssueType: z.string().trim().min(1).max(100),
|
|
@@ -171,6 +198,7 @@ const CreateAgentSchema = z
|
|
|
171
198
|
budgetGroupId: agentFields.budgetGroupId.optional(),
|
|
172
199
|
memoryEnabled: agentFields.memoryEnabled.default(false),
|
|
173
200
|
effort: agentFields.effort.optional(),
|
|
201
|
+
runSteps: agentFields.runSteps.optional(),
|
|
174
202
|
nativeExecutionMode: agentFields.nativeExecutionMode.optional(),
|
|
175
203
|
defectProjectKey: agentFields.defectProjectKey.optional(),
|
|
176
204
|
defectIssueType: agentFields.defectIssueType.optional(),
|
|
@@ -229,6 +257,7 @@ const UpdateAgentSchema = z
|
|
|
229
257
|
budgetGroupId: agentFields.budgetGroupId.nullable().optional(),
|
|
230
258
|
memoryEnabled: agentFields.memoryEnabled.optional(),
|
|
231
259
|
effort: agentFields.effort.nullable().optional(),
|
|
260
|
+
runSteps: agentFields.runSteps.nullable().optional(),
|
|
232
261
|
nativeExecutionMode: agentFields.nativeExecutionMode.optional(),
|
|
233
262
|
defectProjectKey: agentFields.defectProjectKey.nullable().optional(),
|
|
234
263
|
defectIssueType: agentFields.defectIssueType.nullable().optional(),
|
|
@@ -491,7 +520,7 @@ export function registerAgentTools(mcp) {
|
|
|
491
520
|
type: "integer",
|
|
492
521
|
minimum: 1,
|
|
493
522
|
maximum: MAX_DELEGATIONS_PER_RUN,
|
|
494
|
-
description: "Native agents with sub-agents: how many delegate_to_* calls one run may make,
|
|
523
|
+
description: "Native agents with sub-agents: how many delegate_to_* calls one run may make, to each sub-agent and repository at most once " +
|
|
495
524
|
"(one after another, or together with parallelDelegations). Default 1: route to exactly one.",
|
|
496
525
|
},
|
|
497
526
|
parallelDelegations: { type: "boolean", description: PARALLEL_DELEGATIONS_DESCRIPTION },
|
|
@@ -506,6 +535,7 @@ export function registerAgentTools(mcp) {
|
|
|
506
535
|
enum: [...LLM_EFFORT_LEVELS],
|
|
507
536
|
description: "Reasoning effort for native agents. Unset uses the provider default.",
|
|
508
537
|
},
|
|
538
|
+
runSteps: { type: "string", description: RUN_STEPS_DESCRIPTION },
|
|
509
539
|
nativeExecutionMode: {
|
|
510
540
|
type: "string",
|
|
511
541
|
enum: [...NATIVE_EXECUTION_MODES],
|
|
@@ -550,7 +580,7 @@ export function registerAgentTools(mcp) {
|
|
|
550
580
|
const authorization = codingProfile
|
|
551
581
|
? await authorizeRepositoryForSet(ctx, {
|
|
552
582
|
ownerId: ctx.principal.id,
|
|
553
|
-
provider:
|
|
583
|
+
provider: codeProviderOf(codingProfile.repository),
|
|
554
584
|
repository: codingProfile.repository,
|
|
555
585
|
kind: "coding",
|
|
556
586
|
adminOverride: repositoryAdminOverride === true,
|
|
@@ -571,7 +601,7 @@ export function registerAgentTools(mcp) {
|
|
|
571
601
|
},
|
|
572
602
|
include: { codingProfile: true },
|
|
573
603
|
});
|
|
574
|
-
return textResult(withProfileWarnings(withOperatorMode(agent)));
|
|
604
|
+
return textResult(withRunStepsWarning(withProfileWarnings(withOperatorMode(agent))));
|
|
575
605
|
},
|
|
576
606
|
});
|
|
577
607
|
mcp.registerTool({
|
|
@@ -597,7 +627,7 @@ export function registerAgentTools(mcp) {
|
|
|
597
627
|
type: "integer",
|
|
598
628
|
minimum: 1,
|
|
599
629
|
maximum: MAX_DELEGATIONS_PER_RUN,
|
|
600
|
-
description: "Native agents with sub-agents: how many delegate_to_* calls one run may make,
|
|
630
|
+
description: "Native agents with sub-agents: how many delegate_to_* calls one run may make, to each sub-agent and repository at most once " +
|
|
601
631
|
"(one after another, or together with parallelDelegations). Default 1: route to exactly one.",
|
|
602
632
|
},
|
|
603
633
|
parallelDelegations: { type: "boolean", description: PARALLEL_DELEGATIONS_DESCRIPTION },
|
|
@@ -612,6 +642,7 @@ export function registerAgentTools(mcp) {
|
|
|
612
642
|
enum: [...LLM_EFFORT_LEVELS, null],
|
|
613
643
|
description: "Reasoning effort for native agents. Null clears it back to the provider default.",
|
|
614
644
|
},
|
|
645
|
+
runSteps: { type: ["string", "null"], description: RUN_STEPS_DESCRIPTION },
|
|
615
646
|
nativeExecutionMode: {
|
|
616
647
|
type: "string",
|
|
617
648
|
enum: [...NATIVE_EXECUTION_MODES],
|
|
@@ -726,19 +757,21 @@ export function registerAgentTools(mcp) {
|
|
|
726
757
|
// and budget-group membership direct what runs do with the owner's
|
|
727
758
|
// repository access and money: owner-only, like the repository itself
|
|
728
759
|
// (review I1/M1/M6). Write keeps name, prompt, model, budget amount,
|
|
729
|
-
// turns, effort, memory and schedule.
|
|
760
|
+
// turns, effort, memory and schedule. The run steps level is the owner's too:
|
|
761
|
+
// detail records tool output that only the owner and the run's starter may read.
|
|
730
762
|
const ownerOnlyChange = (existingKind) => args.codingProfile !== undefined ||
|
|
731
763
|
(args.kind !== undefined && args.kind !== existingKind) ||
|
|
732
|
-
args.budgetGroupId !== undefined
|
|
764
|
+
args.budgetGroupId !== undefined ||
|
|
765
|
+
args.runSteps !== undefined;
|
|
733
766
|
const refuseOwnerOnly = () => {
|
|
734
|
-
throw new McpError(403, `Agent "${args.id}": only its owner can change its coding profile, kind or
|
|
767
|
+
throw new McpError(403, `Agent "${args.id}": only its owner can change its coding profile, kind, budget group or run steps level.`);
|
|
735
768
|
};
|
|
736
769
|
if (!adminEdit && !isOwner && ownerOnlyChange(before.kind))
|
|
737
770
|
refuseOwnerOnly();
|
|
738
771
|
const authorization = plannedRepository !== null && repositoryChanges(before)
|
|
739
772
|
? await authorizeRepositoryForSet(ctx, {
|
|
740
773
|
ownerId: before.ownerId,
|
|
741
|
-
provider:
|
|
774
|
+
provider: codeProviderOf(plannedRepository),
|
|
742
775
|
repository: plannedRepository,
|
|
743
776
|
kind: "coding",
|
|
744
777
|
adminOverride: args.repositoryAdminOverride === true,
|
|
@@ -774,6 +807,17 @@ export function registerAgentTools(mcp) {
|
|
|
774
807
|
await requireReadableBudgetGroup(tx, args.budgetGroupId, ctx.principal.id);
|
|
775
808
|
}
|
|
776
809
|
const nextKind = args.kind ?? existing.kind;
|
|
810
|
+
if (nextKind !== existing.kind) {
|
|
811
|
+
// A write link without triggers is a coding agent's extra repository
|
|
812
|
+
// and a native agent's plain write access; changing kind would
|
|
813
|
+
// silently reinterpret it.
|
|
814
|
+
const reinterpreted = await tx.agentRepository.count({
|
|
815
|
+
where: { agentId: args.id, access: "write", triggers: { isEmpty: true } },
|
|
816
|
+
});
|
|
817
|
+
if (reinterpreted > 0) {
|
|
818
|
+
throw new McpError(400, `Agent "${args.id}" has write repository links without triggers, which mean different things on native and coding agents; unlink them before changing its kind.`);
|
|
819
|
+
}
|
|
820
|
+
}
|
|
777
821
|
// A coding repository is an owner binding (agentOwnerBindings): an
|
|
778
822
|
// agent everyone can write never gets one, as attach_* refuse theirs.
|
|
779
823
|
if (((nextKind === "coding" && existing.kind !== "coding") || repositoryChanges(existing)) &&
|
|
@@ -892,16 +936,59 @@ export function registerAgentTools(mcp) {
|
|
|
892
936
|
by: ctx.principal.id,
|
|
893
937
|
}, "coding package audit mode set");
|
|
894
938
|
}
|
|
895
|
-
|
|
939
|
+
const shown = withRunStepsWarning(withProfileWarnings(withOperatorMode(agent)));
|
|
940
|
+
// Becoming Codex (a provider switch, or a native agent becoming coding) leaves out every attached
|
|
941
|
+
// MCP tool the server doesn't mark read-only: say which, so the owner isn't surprised by a run.
|
|
942
|
+
const wasCodex = before.kind === "coding" && isCodexCoding(before.codingProfile?.provider);
|
|
943
|
+
if (agent.kind === "coding" && isCodexCoding(agent.codingProfile?.provider) && !wasCodex) {
|
|
944
|
+
const writers = await codexUnavailableMcpTools(ctx.db, agent.id);
|
|
945
|
+
if (writers.length > 0) {
|
|
946
|
+
return textResult({ ...shown, warnings: [...(shown.warnings ?? []), codexWriteToolsWarning(writers)] });
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
return textResult(shown);
|
|
896
950
|
},
|
|
897
951
|
});
|
|
898
952
|
mcp.registerTool({
|
|
899
953
|
name: "list_agents",
|
|
900
954
|
scope: "agents:read",
|
|
901
|
-
description: "Lists the agents you can see. Each row has an `access` field, your level on that agent: owner > write > execute > read. When showing the agents as a table, include `access` as a column."
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
955
|
+
description: "Lists the agents you can see. Each row has an `access` field, your level on that agent: owner > write > execute > read. When showing the agents as a table, include `access` as a column. " +
|
|
956
|
+
"summary: true returns only id, name, kind, model, codingProvider, budgetUsd, ownerId, runSteps and access per agent, for listings that don't need the full configuration. " +
|
|
957
|
+
"all: true lists every agent in the deployment (admins only, admin:view); an agent you have no grant on shows access `none`.",
|
|
958
|
+
inputSchema: {
|
|
959
|
+
type: "object",
|
|
960
|
+
properties: { summary: { type: "boolean" }, all: { type: "boolean" } },
|
|
961
|
+
additionalProperties: false,
|
|
962
|
+
},
|
|
963
|
+
handler: async (args, ctx) => {
|
|
964
|
+
if (args.all === true && !ctx.operator)
|
|
965
|
+
requireScope(ctx, ctx.canonicalUri, "admin:view");
|
|
966
|
+
const [where, accessOf] = await Promise.all([
|
|
967
|
+
args.all === true ? Promise.resolve({}) : readableAgentsWhere(ctx),
|
|
968
|
+
agentAccessResolver(ctx),
|
|
969
|
+
]);
|
|
970
|
+
if (args.summary === true) {
|
|
971
|
+
const agents = await ctx.db.agent.findMany({
|
|
972
|
+
where,
|
|
973
|
+
select: {
|
|
974
|
+
id: true,
|
|
975
|
+
name: true,
|
|
976
|
+
kind: true,
|
|
977
|
+
model: true,
|
|
978
|
+
budgetUsd: true,
|
|
979
|
+
ownerId: true,
|
|
980
|
+
runSteps: true,
|
|
981
|
+
codingProfile: { select: { provider: true } },
|
|
982
|
+
},
|
|
983
|
+
orderBy: { name: "asc" },
|
|
984
|
+
});
|
|
985
|
+
return textResult(agents.map(({ codingProfile, budgetUsd, ...agent }) => ({
|
|
986
|
+
...agent,
|
|
987
|
+
codingProvider: codingProfile?.provider ?? null,
|
|
988
|
+
budgetUsd: Number(budgetUsd),
|
|
989
|
+
access: accessOf(agent),
|
|
990
|
+
})));
|
|
991
|
+
}
|
|
905
992
|
const agents = await ctx.db.agent.findMany({ where, include: { codingProfile: true } });
|
|
906
993
|
return textResult(agents.map((agent) => ({ ...withOperatorMode(agent), access: accessOf(agent) })));
|
|
907
994
|
},
|
|
@@ -923,8 +1010,9 @@ export function registerAgentTools(mcp) {
|
|
|
923
1010
|
// A7: a tool's code and schema are its owner's, whoever can read the
|
|
924
1011
|
// agent (the agent's owner included). Capabilities stay visible: config.
|
|
925
1012
|
return textResult({
|
|
926
|
-
...withProfileWarnings(withOperatorMode(agent)),
|
|
1013
|
+
...withRunStepsWarning(withProfileWarnings(withOperatorMode(agent))),
|
|
927
1014
|
access,
|
|
1015
|
+
mcpServers: await agentMcpServerViews(ctx.db, agent.id),
|
|
928
1016
|
// Zod 3 window: the caller's own tools carry their warnings (see core/zod3-window.ts).
|
|
929
1017
|
tools: await Promise.all(agent.tools.map(async (attachment) => ({
|
|
930
1018
|
...attachment,
|
|
@@ -974,7 +1062,7 @@ export function registerAgentTools(mcp) {
|
|
|
974
1062
|
mcp.registerTool({
|
|
975
1063
|
name: "make_owner",
|
|
976
1064
|
scope: "agents:admin",
|
|
977
|
-
description: "Admins: gives an agent a new owner (an existing principal id). Secret and
|
|
1065
|
+
description: "Admins: gives an agent a new owner (an existing principal id). Secret, datastore and MCP server 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.",
|
|
978
1066
|
inputSchema: {
|
|
979
1067
|
type: "object",
|
|
980
1068
|
additionalProperties: false,
|
|
@@ -996,7 +1084,9 @@ export function registerAgentTools(mcp) {
|
|
|
996
1084
|
const principal = await ctx.db.principal.findUnique({ where: { id: newOwner } });
|
|
997
1085
|
if (!principal)
|
|
998
1086
|
throw new McpError(400, `Principal "${newOwner}" not found.`);
|
|
999
|
-
|
|
1087
|
+
// Bounded retry: a concurrent grant or attach on the same rows aborts one side (Serializable).
|
|
1088
|
+
const result = await withSerializableRetry(() => ctx.db.$transaction(async (tx) => {
|
|
1089
|
+
await preferIndexScans(tx);
|
|
1000
1090
|
const agent = await tx.agent.findUnique({ where: { id: args.agentId }, include: { codingProfile: true } });
|
|
1001
1091
|
if (!agent)
|
|
1002
1092
|
throw agentNotFound(args.agentId);
|
|
@@ -1015,7 +1105,10 @@ export function registerAgentTools(mcp) {
|
|
|
1015
1105
|
where: { agentId: agent.id, authorizedVia: approved },
|
|
1016
1106
|
orderBy: { repository: "asc" },
|
|
1017
1107
|
});
|
|
1018
|
-
await tx.agentRepository.updateMany({
|
|
1108
|
+
await tx.agentRepository.updateMany({
|
|
1109
|
+
where: { agentId: agent.id, authorizedVia: approved },
|
|
1110
|
+
data: reset,
|
|
1111
|
+
});
|
|
1019
1112
|
revoked.push(...links.map((l) => l.repository));
|
|
1020
1113
|
const profile = await tx.codingAgentProfile.updateMany({
|
|
1021
1114
|
where: { agentId: agent.id, repositoryAuthorizedVia: approved },
|
|
@@ -1072,9 +1165,31 @@ export function registerAgentTools(mcp) {
|
|
|
1072
1165
|
where: { agentId: agent.id, datastoreId: { in: foreignDatastores.map((b) => b.datastoreId) } },
|
|
1073
1166
|
});
|
|
1074
1167
|
}
|
|
1168
|
+
// MCP server attachments: the server's brokered secrets act for its
|
|
1169
|
+
// owner, so a server the new owner neither owns nor holds use on is detached.
|
|
1170
|
+
const mcpServers = await tx.agentMcpServer.findMany({
|
|
1171
|
+
where: { agentId: agent.id },
|
|
1172
|
+
include: { server: { select: { id: true, name: true, ownerId: true } } },
|
|
1173
|
+
orderBy: { serverId: "asc" },
|
|
1174
|
+
});
|
|
1175
|
+
const foreignMcpServers = await filterUnusableMcpServers(tx, () => newOwner, mcpServers);
|
|
1176
|
+
if (foreignMcpServers.length > 0) {
|
|
1177
|
+
await tx.agentMcpServer.deleteMany({
|
|
1178
|
+
where: { agentId: agent.id, serverId: { in: foreignMcpServers.map((b) => b.serverId) } },
|
|
1179
|
+
});
|
|
1180
|
+
}
|
|
1075
1181
|
const bindingsRemoved = [
|
|
1076
1182
|
...foreignSecrets.map((b) => ({ kind: "secret", boundName: b.boundName, resourceId: b.secretId })),
|
|
1077
|
-
...foreignDatastores.map((b) => ({
|
|
1183
|
+
...foreignDatastores.map((b) => ({
|
|
1184
|
+
kind: "datastore",
|
|
1185
|
+
boundName: b.boundName,
|
|
1186
|
+
resourceId: b.datastoreId,
|
|
1187
|
+
})),
|
|
1188
|
+
...foreignMcpServers.map((b) => ({
|
|
1189
|
+
kind: "mcp_server",
|
|
1190
|
+
boundName: b.server.name,
|
|
1191
|
+
resourceId: b.serverId,
|
|
1192
|
+
})),
|
|
1078
1193
|
];
|
|
1079
1194
|
// Tool capabilities: rows aren't rewritten; the runner ignores any
|
|
1080
1195
|
// the new owner never stamped (capabilitiesGrantedById).
|
|
@@ -1132,7 +1247,7 @@ export function registerAgentTools(mcp) {
|
|
|
1132
1247
|
everyoneGrant,
|
|
1133
1248
|
webhooksInactive,
|
|
1134
1249
|
};
|
|
1135
|
-
}, { isolationLevel: Prisma.TransactionIsolationLevel.Serializable });
|
|
1250
|
+
}, { isolationLevel: Prisma.TransactionIsolationLevel.Serializable }));
|
|
1136
1251
|
return textResult(result);
|
|
1137
1252
|
},
|
|
1138
1253
|
});
|
package/dist/mcp/tools/grants.js
CHANGED
|
@@ -1,18 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* grant_access / revoke_access / list_access: the owner's sharing controls
|
|
3
|
-
* (resource-sharing grants spec §3.11).
|
|
4
|
-
* resource types are refused as "not shareable yet".
|
|
3
|
+
* (resource-sharing grants spec §3.11). Agents and MCP servers are shareable;
|
|
4
|
+
* other resource types are refused as "not shareable yet".
|
|
5
5
|
*
|
|
6
6
|
* - Only the resource's owner (or the stdio operator) grants and revokes.
|
|
7
7
|
* Admins get no implicit access: they may only list_access, for incident
|
|
8
8
|
* response ("who can reach this?"), and reassign with make_owner.
|
|
9
9
|
* - One grant per resource and grantee, so granting again replaces the
|
|
10
10
|
* level (also downgrades). Everyone grants are capped below write.
|
|
11
|
-
* - Takes effect on the next check; runs already in flight keep going.
|
|
11
|
+
* - Takes effect on the next check; runs already in flight keep going. For
|
|
12
|
+
* MCP servers the check is every tool call: revoking or lowering a grant
|
|
13
|
+
* below use also removes the attachments of agents whose owner can no
|
|
14
|
+
* longer use the server, in the same transaction.
|
|
12
15
|
*/
|
|
13
16
|
import { z } from "zod";
|
|
14
17
|
import { Prisma } from "#prisma";
|
|
15
|
-
import { EVERYONE_KEY, EVERYONE_MAX, LEVELS, agentOwnerBindings, SHAREABLE_TYPES, accessRank, atLeast, isLevel, isResourceType, principalGranteeKey, } from "../../core/grants.js";
|
|
18
|
+
import { EVERYONE_KEY, EVERYONE_MAX, LEVELS, agentOwnerBindings, SHAREABLE_TYPES, accessRank, atLeast, isLevel, isResourceType, principalGranteeKey, pruneUnusableMcpAttachments, } from "../../core/grants.js";
|
|
19
|
+
import { preferIndexScans, withSerializableRetry } from "../../core/serializable-retry.js";
|
|
16
20
|
import { agentNotFound, assertAgentAccess, agentAccess } from "../auth/access.js";
|
|
17
21
|
import { permissionsOf } from "../auth/resource-server.js";
|
|
18
22
|
import { McpError } from "../errors.js";
|
|
@@ -54,10 +58,17 @@ async function resolveGrantee(db, grantee) {
|
|
|
54
58
|
}
|
|
55
59
|
return principal;
|
|
56
60
|
}
|
|
57
|
-
/** Grant management is the owner's (or the stdio operator's).
|
|
61
|
+
/** Grant management is the owner's (or the stdio operator's). Agents and MCP servers. */
|
|
58
62
|
async function requireOwnedResource(ctx, db, type, id) {
|
|
59
|
-
|
|
60
|
-
|
|
63
|
+
if (type === "mcp_server") {
|
|
64
|
+
const server = await db.mcpServer.findUnique({ where: { id } });
|
|
65
|
+
// Missing and someone else's look the same. The stdio operator owns everything.
|
|
66
|
+
if (!server || (!ctx.operator && server.ownerId !== ctx.principal.id)) {
|
|
67
|
+
throw new McpError(404, `MCP server "${id}" not found.`);
|
|
68
|
+
}
|
|
69
|
+
return server;
|
|
70
|
+
}
|
|
71
|
+
// Only "agent" and "mcp_server" reach here (shareableType).
|
|
61
72
|
const row = await db.agent.findUnique({ where: { id } });
|
|
62
73
|
return (await assertAgentAccess(ctx, row, id, "owner", db)).agent;
|
|
63
74
|
}
|
|
@@ -65,17 +76,21 @@ async function requireOwnedResource(ctx, db, type, id) {
|
|
|
65
76
|
* The ownership check and the write in one Serializable transaction (review
|
|
66
77
|
* M3): a concurrent make_owner, which resets grants in its own transaction,
|
|
67
78
|
* conflicts instead of letting the old owner's grant land on the new
|
|
68
|
-
* owner's agent.
|
|
79
|
+
* owner's agent. A conflict is retried a bounded number of times
|
|
80
|
+
* (withSerializableRetry); the last one still reaches the caller as a 409.
|
|
69
81
|
*/
|
|
70
82
|
function inSerializable(ctx, fn) {
|
|
71
|
-
return ctx.db.$transaction((tx) =>
|
|
83
|
+
return withSerializableRetry(() => ctx.db.$transaction(async (tx) => {
|
|
84
|
+
await preferIndexScans(tx);
|
|
85
|
+
return fn(tx);
|
|
86
|
+
}, { isolationLevel: Prisma.TransactionIsolationLevel.Serializable }));
|
|
72
87
|
}
|
|
73
88
|
function isAdmin(ctx) {
|
|
74
89
|
return ctx.scopes.has("agents:admin") && permissionsOf(ctx.roles).has("agents:admin");
|
|
75
90
|
}
|
|
76
91
|
const TYPE_PROPERTIES = {
|
|
77
92
|
// No enum: another type gets a clear "not shareable yet" from the handler.
|
|
78
|
-
resourceType: { type: "string", description: '
|
|
93
|
+
resourceType: { type: "string", description: 'Agents and MCP servers are shareable: "agent" or "mcp_server".' },
|
|
79
94
|
resourceId: { type: "string" },
|
|
80
95
|
};
|
|
81
96
|
const GRANTEE_PROPERTY = {
|
|
@@ -100,21 +115,22 @@ export function registerGrantTools(mcp) {
|
|
|
100
115
|
mcp.registerTool({
|
|
101
116
|
name: "grant_access",
|
|
102
117
|
scope: "agents:write",
|
|
103
|
-
description: "Shares an agent you own with one principal or with everyone, at a level: " +
|
|
118
|
+
description: "Shares an agent or MCP server you own with one principal or with everyone, at a level: " +
|
|
104
119
|
"read (see its config, never secret values or other owners' tool code); " +
|
|
105
120
|
"execute (also trigger runs and see the runs they triggered); " +
|
|
106
121
|
"write (also change its name, prompt, model, budget amount, max turns, effort, memory on/off and schedule, attach tools they can use and detach tools, and create webhooks). " +
|
|
107
122
|
"Owner-only whatever the level: the coding profile (task, base ref, protected paths, task-override opt-in, image, packages, limits), kind, budget group, sub-agents, secret/datastore/repository bindings, tool capabilities, memory and datastore contents, grants, delete. " +
|
|
108
123
|
"Consequences: execute lets them run your agent with your tools, secrets, datastores and repository, and every run shares the agent's memory; they supply no task text unless it's a coding agent with allowWebhookTaskOverride. " +
|
|
109
124
|
"Write lets them rewrite what those runs do: the prompt can make a run use everything the agent can already reach (the capabilities you granted, its memory, its linked repositories), and on a coding agent the prompt is part of every coding task, so write directs work done with your GitHub access. " +
|
|
110
|
-
"
|
|
125
|
+
"MCP servers (resourceType mcp_server): read < use < write; use on an MCP server lets them attach it to their own agents; its tools then run with your server's credentials. Write adds nothing beyond use: only the owner updates, refreshes, deletes or re-grants a server. Lowering a grant to read (including everyone from use to read) detaches the server from every agent whose owner can no longer use it, and their runs' next call to it is refused. " +
|
|
126
|
+
"Granting again replaces the level. Everyone may get write only on a native agent with no secrets, datastores, repositories, sub-agents, MCP servers or tool capabilities. The grantee must have signed in once.",
|
|
111
127
|
inputSchema: {
|
|
112
128
|
type: "object",
|
|
113
129
|
additionalProperties: false,
|
|
114
130
|
properties: {
|
|
115
131
|
...TYPE_PROPERTIES,
|
|
116
132
|
grantee: GRANTEE_PROPERTY,
|
|
117
|
-
level: { type: "string", description: "Agents: read < execute < write." },
|
|
133
|
+
level: { type: "string", description: "Agents: read < execute < write. MCP servers: read < use < write." },
|
|
118
134
|
},
|
|
119
135
|
required: ["resourceType", "resourceId", "grantee", "level"],
|
|
120
136
|
},
|
|
@@ -124,7 +140,7 @@ export function registerGrantTools(mcp) {
|
|
|
124
140
|
if (!isLevel(type, args.level)) {
|
|
125
141
|
throw new McpError(400, `Level "${args.level}" is not valid for ${type} (${LEVELS[type].join(" < ")}).`);
|
|
126
142
|
}
|
|
127
|
-
const { resource, principal, grant } = await inSerializable(ctx, async (tx) => {
|
|
143
|
+
const { resource, principal, grant, attachmentsRemoved } = await inSerializable(ctx, async (tx) => {
|
|
128
144
|
const resource = await requireOwnedResource(ctx, tx, type, args.resourceId);
|
|
129
145
|
const principal = await resolveGrantee(tx, args.grantee);
|
|
130
146
|
if (principal && (principal.id === resource.ownerId || principal.id === ctx.principal.id)) {
|
|
@@ -154,7 +170,11 @@ export function registerGrantTools(mcp) {
|
|
|
154
170
|
},
|
|
155
171
|
update: { level: args.level, source: "owner", grantedById: ctx.principal.id },
|
|
156
172
|
});
|
|
157
|
-
|
|
173
|
+
// Below use, agents whose owner can no longer use the server lose it now (C1).
|
|
174
|
+
const attachmentsRemoved = type === "mcp_server" && !atLeast(type, args.level, "use")
|
|
175
|
+
? await pruneUnusableMcpAttachments(tx, resource.id)
|
|
176
|
+
: 0;
|
|
177
|
+
return { resource, principal, grant, attachmentsRemoved };
|
|
158
178
|
});
|
|
159
179
|
return textResult({
|
|
160
180
|
granted: true,
|
|
@@ -167,13 +187,14 @@ export function registerGrantTools(mcp) {
|
|
|
167
187
|
? { principalId: principal.id, subject: principal.subject }
|
|
168
188
|
: { principalId: principal.id },
|
|
169
189
|
level: grant.level,
|
|
190
|
+
...(attachmentsRemoved > 0 ? { attachmentsRemoved } : {}),
|
|
170
191
|
});
|
|
171
192
|
},
|
|
172
193
|
});
|
|
173
194
|
mcp.registerTool({
|
|
174
195
|
name: "revoke_access",
|
|
175
196
|
scope: "agents:write",
|
|
176
|
-
description: "Removes a grant on an agent you own. Idempotent. Takes effect on the next check (trigger, delegation, webhook fire); runs already in flight keep going.",
|
|
197
|
+
description: "Removes a grant on an agent or MCP server you own. Idempotent. Takes effect on the next check (trigger, delegation, webhook fire); runs already in flight keep going. For an MCP server the check is every tool call: the server is detached from every agent whose owner can no longer use it (your own agents are untouched), and a run's next call to it is refused with mcp_tool_not_allowed.",
|
|
177
198
|
inputSchema: {
|
|
178
199
|
type: "object",
|
|
179
200
|
additionalProperties: false,
|
|
@@ -183,20 +204,25 @@ export function registerGrantTools(mcp) {
|
|
|
183
204
|
handler: async (rawArgs, ctx) => {
|
|
184
205
|
const args = parse(RevokeSchema, "revoke_access", rawArgs);
|
|
185
206
|
const type = shareableType(args.resourceType);
|
|
186
|
-
const { count } = await inSerializable(ctx, async (tx) => {
|
|
207
|
+
const { count, attachmentsRemoved } = await inSerializable(ctx, async (tx) => {
|
|
187
208
|
const resource = await requireOwnedResource(ctx, tx, type, args.resourceId);
|
|
188
209
|
const granteeKey = args.grantee === "everyone"
|
|
189
210
|
? EVERYONE_KEY
|
|
190
211
|
: principalGranteeKey((await resolveGrantee(tx, args.grantee)).id);
|
|
191
|
-
|
|
212
|
+
const { count } = await tx.resourceGrant.deleteMany({
|
|
213
|
+
where: { resourceType: type, resourceId: resource.id, granteeKey },
|
|
214
|
+
});
|
|
215
|
+
// Agents whose owner can no longer use the server lose it now (C1).
|
|
216
|
+
const attachmentsRemoved = type === "mcp_server" ? await pruneUnusableMcpAttachments(tx, resource.id) : 0;
|
|
217
|
+
return { count, attachmentsRemoved };
|
|
192
218
|
});
|
|
193
|
-
return textResult({ revoked: count > 0 });
|
|
219
|
+
return textResult({ revoked: count > 0, ...(attachmentsRemoved > 0 ? { attachmentsRemoved } : {}) });
|
|
194
220
|
},
|
|
195
221
|
});
|
|
196
222
|
mcp.registerTool({
|
|
197
223
|
name: "list_access",
|
|
198
224
|
scope: "agents:read",
|
|
199
|
-
description: "Lists who an agent is shared with (grant metadata only). For its owner, the stdio operator, and admins (incident response).",
|
|
225
|
+
description: "Lists who an agent or MCP server is shared with (grant metadata only). For its owner, the stdio operator, and admins (incident response).",
|
|
200
226
|
inputSchema: {
|
|
201
227
|
type: "object",
|
|
202
228
|
additionalProperties: false,
|
|
@@ -206,16 +232,28 @@ export function registerGrantTools(mcp) {
|
|
|
206
232
|
handler: async (rawArgs, ctx) => {
|
|
207
233
|
const args = parse(ListSchema, "list_access", rawArgs);
|
|
208
234
|
const type = shareableType(args.resourceType);
|
|
209
|
-
|
|
210
|
-
if (
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
235
|
+
let resource;
|
|
236
|
+
if (type === "mcp_server") {
|
|
237
|
+
const server = await ctx.db.mcpServer.findUnique({ where: { id: args.resourceId } });
|
|
238
|
+
// Owner, stdio operator or admin; anyone else sees a missing server.
|
|
239
|
+
if (!server || (!ctx.operator && !isAdmin(ctx) && server.ownerId !== ctx.principal.id)) {
|
|
240
|
+
throw new McpError(404, `MCP server "${args.resourceId}" not found.`);
|
|
241
|
+
}
|
|
242
|
+
resource = server;
|
|
243
|
+
}
|
|
244
|
+
else {
|
|
245
|
+
const agent = await ctx.db.agent.findUnique({ where: { id: args.resourceId } });
|
|
246
|
+
if (!agent)
|
|
215
247
|
throw agentNotFound(args.resourceId);
|
|
216
|
-
if (
|
|
217
|
-
|
|
248
|
+
if (!isAdmin(ctx)) {
|
|
249
|
+
const access = await agentAccess(ctx, agent);
|
|
250
|
+
if (!atLeast("agent", access, "read"))
|
|
251
|
+
throw agentNotFound(args.resourceId);
|
|
252
|
+
if (access !== "owner") {
|
|
253
|
+
throw new McpError(403, `Agent "${agent.id}": only its owner (or an admin) can list who it is shared with.`);
|
|
254
|
+
}
|
|
218
255
|
}
|
|
256
|
+
resource = agent;
|
|
219
257
|
}
|
|
220
258
|
const grants = await ctx.db.resourceGrant.findMany({
|
|
221
259
|
where: { resourceType: type, resourceId: resource.id },
|