@wardby/cli 0.7.0 → 0.8.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 +6 -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 +61 -0
- package/dist/claude-plugin/command.js +376 -0
- package/dist/cli-help.d.ts +1 -1
- package/dist/cli-help.js +2 -1
- package/dist/cli.js +47 -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 +1 -1
- 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 +36 -11
- package/dist/core/error-code.js +1 -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/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/related-pull-requests.js +26 -10
- package/dist/core/review-fix.js +2 -2
- package/dist/core/review-host-tools.js +3 -1
- package/dist/core/runner.d.ts +66 -16
- package/dist/core/runner.js +281 -73
- 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 +31 -0
- package/dist/generated/prisma/client.d.ts +31 -0
- package/dist/generated/prisma/commonInputTypes.d.ts +50 -50
- package/dist/generated/prisma/internal/class.d.ts +66 -0
- package/dist/generated/prisma/internal/class.js +4 -4
- package/dist/generated/prisma/internal/prismaNamespace.d.ts +530 -1
- package/dist/generated/prisma/internal/prismaNamespace.js +75 -2
- package/dist/generated/prisma/internal/prismaNamespaceBrowser.d.ts +79 -0
- package/dist/generated/prisma/internal/prismaNamespaceBrowser.js +75 -2
- package/dist/generated/prisma/models/Agent.d.ts +271 -0
- 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 +119 -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/Secret.d.ts +387 -0
- package/dist/generated/prisma/models/Tool.d.ts +223 -1
- package/dist/generated/prisma/models.d.ts +6 -0
- package/dist/help/search.js +12 -3
- package/dist/help-index.json +719 -47
- 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/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 +3 -0
- package/dist/mcp/index.js +11 -0
- package/dist/mcp/tools/agents.js +100 -15
- 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/repositories.js +50 -10
- package/dist/mcp/tools/runs.js +178 -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 +14 -0
- package/dist/native-worker/gateway-server.js +28 -7
- package/dist/native-worker/gateway.d.ts +2 -0
- package/dist/native-worker/gateway.js +5 -1
- package/dist/native-worker/http-server.d.ts +4 -0
- package/dist/native-worker/http-server.js +51 -0
- 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 +15 -0
- package/dist/providers/coding-proxy/prisma-ledger.d.ts +1 -0
- package/dist/providers/coding-proxy/prisma-ledger.js +30 -2
- package/dist/providers/coding-proxy/proxy.d.ts +7 -1
- package/dist/providers/coding-proxy/proxy.js +134 -34
- package/dist/providers/coding-proxy/runtime.d.ts +6 -0
- package/dist/providers/coding-proxy/runtime.js +32 -1
- 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 +13 -0
- package/dist/providers/executor/composition.js +5 -0
- package/dist/providers/executor/container.d.ts +33 -1
- package/dist/providers/executor/container.js +76 -7
- 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 +5 -0
- package/dist/providers/jobs/kubernetes.js +6 -1
- 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/viewer/graph.js +2 -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 +164 -0
- package/docs/code-review-agents.md +6 -0
- package/docs/coding-agent-setup.md +77 -1
- package/docs/coding-worker-byo-images.md +4 -2
- package/docs/coding-worker-isolation.md +78 -7
- package/docs/debugging-agent-runs.md +242 -0
- package/docs/getting-started-gke.md +5 -4
- 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 +254 -0
- package/docs/native-sandbox.md +25 -9
- package/docs/security-deployment.md +110 -31
- package/docs/tool-secrets.md +3 -0
- package/docs/viewer-api.md +6 -0
- package/docs/viewer-api.openapi.yml +1 -1
- package/help/admin-viewer.md +4 -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 +40 -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/mcp-servers.md +73 -0
- package/help/mcp.md +7 -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 +3 -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/schema.prisma +159 -14
- package/scripts/git-askpass.sh +1 -1
package/dist/import/create.js
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
* attachments, secrets, datastores, webhooks, budget groups) to the database
|
|
4
4
|
* from a validated bundle + reconciliation.
|
|
5
5
|
*/
|
|
6
|
+
import { toolTimeoutProblem } from "../sandbox/tool-timeout.js";
|
|
7
|
+
import { loadToolMaxTimeoutMs } from "../config/tool-timeout.js";
|
|
6
8
|
import { Prisma } from "#prisma";
|
|
7
9
|
import { scheduleStartFields } from "../core/schedule-start.js";
|
|
8
10
|
import { createSecret, attachSecret } from "../core/secrets.js";
|
|
@@ -12,7 +14,7 @@ import { SecretCheckUnavailableError, SecretInValueError, createDbSecretWriteGua
|
|
|
12
14
|
import { createWebhook } from "../core/webhooks.js";
|
|
13
15
|
import { deriveJsonSchema } from "../sandbox/zod-params.js";
|
|
14
16
|
import { toolSubject, zod3ToolWarnings } from "../core/zod3-window.js";
|
|
15
|
-
import { findSameNamedAttachedTool, reservedToolNameReason } from "../core/tool-names.js";
|
|
17
|
+
import { findSameNamedAttachedTool, findSameNamedMcpTool, reservedToolNameReason } from "../core/tool-names.js";
|
|
16
18
|
import { ToolCapabilitiesPatchSchema, FETCH_WILDCARD } from "../sandbox/tool-capabilities.js";
|
|
17
19
|
import { PostgresDatastore } from "../providers/datastore/postgres.js";
|
|
18
20
|
import { decryptTransferEnvelope } from "../providers/secrets/transfer-envelope.js";
|
|
@@ -70,6 +72,13 @@ export async function createFromBundle(bundle, recon, opts) {
|
|
|
70
72
|
warnings.push(`tool ${t.name}: failed to derive JSON schema, skipping`);
|
|
71
73
|
continue;
|
|
72
74
|
}
|
|
75
|
+
const timeoutProblem = t.timeoutMs === undefined || t.timeoutMs === null
|
|
76
|
+
? undefined
|
|
77
|
+
: toolTimeoutProblem(t.timeoutMs, loadToolMaxTimeoutMs());
|
|
78
|
+
if (timeoutProblem) {
|
|
79
|
+
warnings.push(`tool ${t.name}: ${timeoutProblem} Skipping`);
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
73
82
|
// Names are unique per owner: reuse the target owner's own same-named
|
|
74
83
|
// tool (what upsert-by-name used to do), never another owner's. A
|
|
75
84
|
// compound unique with a nullable column can't key an upsert, hence
|
|
@@ -83,6 +92,7 @@ export async function createFromBundle(bundle, recon, opts) {
|
|
|
83
92
|
code: t.code,
|
|
84
93
|
jsonSchema: schema.value,
|
|
85
94
|
ownerId,
|
|
95
|
+
...(t.timeoutMs !== undefined && t.timeoutMs !== null ? { timeoutMs: t.timeoutMs } : {}),
|
|
86
96
|
},
|
|
87
97
|
}));
|
|
88
98
|
toolIdMap.set(t.name, tool.id);
|
|
@@ -180,6 +190,11 @@ export async function createFromBundle(bundle, recon, opts) {
|
|
|
180
190
|
warnings.push(`agent-tool ${at.agentName}/${at.toolName}: skipped — the agent already has a different tool with the same name (${clash.id})`);
|
|
181
191
|
continue;
|
|
182
192
|
}
|
|
193
|
+
const mcpClash = await findSameNamedMcpTool(db, agentId, eff(at.toolName));
|
|
194
|
+
if (mcpClash) {
|
|
195
|
+
warnings.push(`agent-tool ${at.agentName}/${at.toolName}: skipped — the agent already has a tool with this name from MCP server "${mcpClash.serverName}"`);
|
|
196
|
+
continue;
|
|
197
|
+
}
|
|
183
198
|
const caps = capsPatch.data;
|
|
184
199
|
// The importing operator is trusted with the database, so on an agent
|
|
185
200
|
// this import owns it grants the bundle's capabilities in the owner's
|
|
@@ -68,6 +68,7 @@ export declare const NeutralToolSchema: z.ZodObject<{
|
|
|
68
68
|
jsonSchema: z.ZodDefault<z.ZodNullable<z.ZodUnknown>>;
|
|
69
69
|
code: z.ZodString;
|
|
70
70
|
secretSchema: z.ZodDefault<z.ZodString>;
|
|
71
|
+
timeoutMs: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
71
72
|
}, z.core.$strip>;
|
|
72
73
|
export type NeutralTool = z.infer<typeof NeutralToolSchema>;
|
|
73
74
|
export declare const NeutralAgentToolSchema: z.ZodObject<{
|
|
@@ -53,6 +53,8 @@ export const NeutralToolSchema = z.object({
|
|
|
53
53
|
jsonSchema: z.unknown().nullable().default(null), // ignored; we re-derive (ruling 2)
|
|
54
54
|
code: z.string(),
|
|
55
55
|
secretSchema: z.string().default(""),
|
|
56
|
+
/** The tool's own time limit (ms); absent or null keeps the default. Checked against WARDBY_TOOL_MAX_TIMEOUT_MS on import. */
|
|
57
|
+
timeoutMs: z.number().int().nullable().optional(),
|
|
56
58
|
});
|
|
57
59
|
export const NeutralAgentToolSchema = z.object({
|
|
58
60
|
agentName: z.string(),
|
|
@@ -77,16 +77,18 @@ export interface MigrationReport {
|
|
|
77
77
|
}
|
|
78
78
|
export declare function migrationReport(db: ReportDb): Promise<MigrationReport>;
|
|
79
79
|
export declare function formatMigrationReport(report: MigrationReport): string;
|
|
80
|
+
/** A binding that belongs to its resource's owner; for an MCP server, boundName is the server name. */
|
|
81
|
+
export type BindingKind = "secret" | "datastore" | "mcp_server";
|
|
80
82
|
export interface AdoptedAgent {
|
|
81
83
|
agentId: string;
|
|
82
84
|
name: string;
|
|
83
85
|
bindingsRemoved: {
|
|
84
|
-
kind:
|
|
86
|
+
kind: BindingKind;
|
|
85
87
|
boundName: string;
|
|
86
88
|
resourceId: string;
|
|
87
89
|
}[];
|
|
88
90
|
bindingsKept: {
|
|
89
|
-
kind:
|
|
91
|
+
kind: BindingKind;
|
|
90
92
|
boundName: string;
|
|
91
93
|
}[];
|
|
92
94
|
capabilitiesStamped: string[];
|
|
@@ -126,7 +128,7 @@ export declare function formatAdoptResult(result: AdoptResult): string;
|
|
|
126
128
|
export interface PruneResult {
|
|
127
129
|
dryRun: boolean;
|
|
128
130
|
removed: {
|
|
129
|
-
kind:
|
|
131
|
+
kind: BindingKind;
|
|
130
132
|
agentId: string;
|
|
131
133
|
agentName: string;
|
|
132
134
|
boundName: string;
|
|
@@ -134,7 +136,7 @@ export interface PruneResult {
|
|
|
134
136
|
resourceOwnerId: string | null;
|
|
135
137
|
}[];
|
|
136
138
|
}
|
|
137
|
-
/** Deletes cross-owner secret/datastore bindings on owned agents, in one transaction. */
|
|
139
|
+
/** Deletes cross-owner secret/datastore/MCP server bindings on owned agents, in one transaction. */
|
|
138
140
|
export declare function pruneBindings(db: PrismaClient, opts: {
|
|
139
141
|
dryRun: boolean;
|
|
140
142
|
}): Promise<PruneResult>;
|
|
@@ -14,13 +14,13 @@
|
|
|
14
14
|
* owner's own bindings, deleting the rest, and stamping the new owner's
|
|
15
15
|
* consent on attachments of its own tools. Other attachments stay inert
|
|
16
16
|
* and are printed with the attach_tool call that re-grants them.
|
|
17
|
-
* - prune-bindings deletes the secret/datastore bindings on owned agents
|
|
17
|
+
* - prune-bindings deletes the secret/datastore/MCP server bindings on owned agents
|
|
18
18
|
* whose resource another principal owns (inert at run time since the
|
|
19
19
|
* grants release) and prints each one: the "removed and listed" record.
|
|
20
20
|
*/
|
|
21
21
|
import { parseArgs } from "node:util";
|
|
22
22
|
import { Prisma } from "#prisma";
|
|
23
|
-
import { canDelegate } from "../../core/grants.js";
|
|
23
|
+
import { canDelegate, filterUnusableMcpServers } from "../../core/grants.js";
|
|
24
24
|
import { resolvePrincipal } from "./principal.js";
|
|
25
25
|
const USAGE = "Use grants migration-report [--json] | grants adopt-public --owner <subject> [--dry-run] [--keep-everyone-execute] | grants prune-bindings [--dry-run].";
|
|
26
26
|
/** The attach_tool call (MCP) that re-grants an attachment's capabilities, for its owner to review and run. */
|
|
@@ -276,8 +276,14 @@ export async function adoptPublic(db, opts) {
|
|
|
276
276
|
include: { datastore: { select: { ownerId: true } } },
|
|
277
277
|
orderBy: { boundName: "asc" },
|
|
278
278
|
});
|
|
279
|
+
const mcpServers = await tx.agentMcpServer.findMany({
|
|
280
|
+
where: { agentId: id },
|
|
281
|
+
include: { server: { select: { id: true, name: true, ownerId: true } } },
|
|
282
|
+
orderBy: { serverId: "asc" },
|
|
283
|
+
});
|
|
279
284
|
const foreignSecrets = secrets.filter((b) => b.secret.ownerId !== owner.id);
|
|
280
285
|
const foreignDatastores = datastores.filter((b) => b.datastore.ownerId !== owner.id);
|
|
286
|
+
const foreignMcpServers = await filterUnusableMcpServers(tx, () => owner.id, mcpServers);
|
|
281
287
|
for (const b of foreignSecrets) {
|
|
282
288
|
await tx.agentSecret.delete({ where: { agentId_secretId: { agentId: id, secretId: b.secretId } } });
|
|
283
289
|
}
|
|
@@ -286,6 +292,9 @@ export async function adoptPublic(db, opts) {
|
|
|
286
292
|
where: { agentId_datastoreId: { agentId: id, datastoreId: b.datastoreId } },
|
|
287
293
|
});
|
|
288
294
|
}
|
|
295
|
+
for (const b of foreignMcpServers) {
|
|
296
|
+
await tx.agentMcpServer.delete({ where: { agentId_serverId: { agentId: id, serverId: b.serverId } } });
|
|
297
|
+
}
|
|
289
298
|
// Only the tool's owner could attach its private tool to a public
|
|
290
299
|
// agent or change that attachment's capabilities, so those carry
|
|
291
300
|
// the new owner's consent. Everything else stays inert.
|
|
@@ -350,6 +359,11 @@ export async function adoptPublic(db, opts) {
|
|
|
350
359
|
boundName: b.boundName,
|
|
351
360
|
resourceId: b.datastoreId,
|
|
352
361
|
})),
|
|
362
|
+
...foreignMcpServers.map((b) => ({
|
|
363
|
+
kind: "mcp_server",
|
|
364
|
+
boundName: b.server.name,
|
|
365
|
+
resourceId: b.serverId,
|
|
366
|
+
})),
|
|
353
367
|
],
|
|
354
368
|
bindingsKept: [
|
|
355
369
|
...secrets
|
|
@@ -358,6 +372,9 @@ export async function adoptPublic(db, opts) {
|
|
|
358
372
|
...datastores
|
|
359
373
|
.filter((b) => b.datastore.ownerId === owner.id)
|
|
360
374
|
.map((b) => ({ kind: "datastore", boundName: b.boundName })),
|
|
375
|
+
...mcpServers
|
|
376
|
+
.filter((b) => !foreignMcpServers.includes(b))
|
|
377
|
+
.map((b) => ({ kind: "mcp_server", boundName: b.server.name })),
|
|
361
378
|
],
|
|
362
379
|
capabilitiesStamped: own.map((at) => at.tool.name),
|
|
363
380
|
foreignTools: attachments
|
|
@@ -411,7 +428,7 @@ export function formatAdoptResult(result) {
|
|
|
411
428
|
}
|
|
412
429
|
return lines.join("\n");
|
|
413
430
|
}
|
|
414
|
-
/** Deletes cross-owner secret/datastore bindings on owned agents, in one transaction. */
|
|
431
|
+
/** Deletes cross-owner secret/datastore/MCP server bindings on owned agents, in one transaction. */
|
|
415
432
|
export async function pruneBindings(db, opts) {
|
|
416
433
|
return db.$transaction(async (tx) => {
|
|
417
434
|
const secrets = await tx.agentSecret.findMany({
|
|
@@ -424,8 +441,17 @@ export async function pruneBindings(db, opts) {
|
|
|
424
441
|
include: { agent: { select: { name: true, ownerId: true } }, datastore: { select: { ownerId: true } } },
|
|
425
442
|
orderBy: [{ agentId: "asc" }, { boundName: "asc" }],
|
|
426
443
|
});
|
|
444
|
+
const mcpServers = await tx.agentMcpServer.findMany({
|
|
445
|
+
where: { agent: { ownerId: { not: null } } },
|
|
446
|
+
include: {
|
|
447
|
+
agent: { select: { name: true, ownerId: true } },
|
|
448
|
+
server: { select: { id: true, name: true, ownerId: true } },
|
|
449
|
+
},
|
|
450
|
+
orderBy: [{ agentId: "asc" }, { serverId: "asc" }],
|
|
451
|
+
});
|
|
427
452
|
const foreignSecrets = secrets.filter((b) => b.secret.ownerId !== b.agent.ownerId);
|
|
428
453
|
const foreignDatastores = datastores.filter((b) => b.datastore.ownerId !== b.agent.ownerId);
|
|
454
|
+
const foreignMcpServers = await filterUnusableMcpServers(tx, (b) => b.agent.ownerId, mcpServers);
|
|
429
455
|
if (!opts.dryRun) {
|
|
430
456
|
for (const b of foreignSecrets) {
|
|
431
457
|
await tx.agentSecret.delete({ where: { agentId_secretId: { agentId: b.agentId, secretId: b.secretId } } });
|
|
@@ -435,6 +461,11 @@ export async function pruneBindings(db, opts) {
|
|
|
435
461
|
where: { agentId_datastoreId: { agentId: b.agentId, datastoreId: b.datastoreId } },
|
|
436
462
|
});
|
|
437
463
|
}
|
|
464
|
+
for (const b of foreignMcpServers) {
|
|
465
|
+
await tx.agentMcpServer.delete({
|
|
466
|
+
where: { agentId_serverId: { agentId: b.agentId, serverId: b.serverId } },
|
|
467
|
+
});
|
|
468
|
+
}
|
|
438
469
|
}
|
|
439
470
|
return {
|
|
440
471
|
dryRun: opts.dryRun,
|
|
@@ -455,6 +486,14 @@ export async function pruneBindings(db, opts) {
|
|
|
455
486
|
resourceId: b.datastoreId,
|
|
456
487
|
resourceOwnerId: b.datastore.ownerId,
|
|
457
488
|
})),
|
|
489
|
+
...foreignMcpServers.map((b) => ({
|
|
490
|
+
kind: "mcp_server",
|
|
491
|
+
agentId: b.agentId,
|
|
492
|
+
agentName: b.agent.name,
|
|
493
|
+
boundName: b.server.name,
|
|
494
|
+
resourceId: b.serverId,
|
|
495
|
+
resourceOwnerId: b.server.ownerId,
|
|
496
|
+
})),
|
|
458
497
|
],
|
|
459
498
|
};
|
|
460
499
|
}, { isolationLevel: Prisma.TransactionIsolationLevel.Serializable });
|
|
@@ -19,7 +19,9 @@ export declare const SCOPES_SUPPORTED: string[];
|
|
|
19
19
|
* runs next to every coding run that names an entry; admin:view reads every
|
|
20
20
|
* owner's runs (the admin viewer API); models:admin changes the model catalog,
|
|
21
21
|
* which prices every principal's runs; power:manage scales the shared coding
|
|
22
|
-
* proxy and native gateway to zero (power_sleep) and reads their state
|
|
22
|
+
* proxy and native gateway to zero (power_sleep) and reads their state;
|
|
23
|
+
* hosts:admin registers host providers, whose approved tools run in the push
|
|
24
|
+
* and pull request flow.
|
|
23
25
|
* A token scope only DELEGATES — it never authorizes on its own:
|
|
24
26
|
* requireScope/requireAnyScope honour one only when one of the caller's
|
|
25
27
|
* roles (McpRequestContext.roles, resolved live per request) grants it.
|
|
@@ -30,6 +30,9 @@ export const SCOPES_SUPPORTED = [
|
|
|
30
30
|
// Scale the coding proxy and native gateway to zero now (power_sleep) and
|
|
31
31
|
// read their state (power_status); low-power mode only (docs/getting-started-gke.md).
|
|
32
32
|
"power:manage",
|
|
33
|
+
// Register host providers and approve their tools (docs/host-providers.md).
|
|
34
|
+
// Bound tools decide who has permission and who wrote a comment.
|
|
35
|
+
"hosts:admin",
|
|
33
36
|
];
|
|
34
37
|
/**
|
|
35
38
|
* Scopes whose operations reach beyond the caller's own (or public)
|
|
@@ -40,7 +43,9 @@ export const SCOPES_SUPPORTED = [
|
|
|
40
43
|
* runs next to every coding run that names an entry; admin:view reads every
|
|
41
44
|
* owner's runs (the admin viewer API); models:admin changes the model catalog,
|
|
42
45
|
* which prices every principal's runs; power:manage scales the shared coding
|
|
43
|
-
* proxy and native gateway to zero (power_sleep) and reads their state
|
|
46
|
+
* proxy and native gateway to zero (power_sleep) and reads their state;
|
|
47
|
+
* hosts:admin registers host providers, whose approved tools run in the push
|
|
48
|
+
* and pull request flow.
|
|
44
49
|
* A token scope only DELEGATES — it never authorizes on its own:
|
|
45
50
|
* requireScope/requireAnyScope honour one only when one of the caller's
|
|
46
51
|
* roles (McpRequestContext.roles, resolved live per request) grants it.
|
|
@@ -52,16 +57,26 @@ export const PRIVILEGED_SCOPES = [
|
|
|
52
57
|
"admin:view",
|
|
53
58
|
"models:admin",
|
|
54
59
|
"power:manage",
|
|
60
|
+
"hosts:admin",
|
|
55
61
|
];
|
|
56
62
|
/**
|
|
57
63
|
* The built-in roles and the permissions (privileged scope names) each
|
|
58
64
|
* grants. No roles = member: every non-privileged scope, nothing privileged.
|
|
59
65
|
*/
|
|
60
66
|
export const ROLE_PERMISSIONS = {
|
|
61
|
-
admin: [
|
|
67
|
+
admin: [
|
|
68
|
+
"agents:admin",
|
|
69
|
+
"packages:approve",
|
|
70
|
+
"services:manage",
|
|
71
|
+
"admin:view",
|
|
72
|
+
"models:admin",
|
|
73
|
+
"power:manage",
|
|
74
|
+
"hosts:admin",
|
|
75
|
+
],
|
|
62
76
|
"package-approver": ["packages:approve"],
|
|
63
77
|
"service-manager": ["services:manage"],
|
|
64
78
|
"model-manager": ["models:admin"],
|
|
79
|
+
"host-manager": ["hosts:admin"],
|
|
65
80
|
};
|
|
66
81
|
export const ROLE_NAMES = Object.keys(ROLE_PERMISSIONS);
|
|
67
82
|
export function isRoleName(name) {
|
package/dist/mcp/context.d.ts
CHANGED
|
@@ -15,6 +15,7 @@ import type { IssueTrackerRegistry } from "../providers/issue-tracker/types.js";
|
|
|
15
15
|
import type { HostUserAuthorizerRegistry, ReviewHostRegistry } from "../providers/review-host/types.js";
|
|
16
16
|
import type { Power } from "../core/power.js";
|
|
17
17
|
import type { ChatProviderRegistry } from "../providers/chat/types.js";
|
|
18
|
+
import type { safeFetch } from "../sandbox/safe-fetch.js";
|
|
18
19
|
export type McpProviders = Pick<ProviderRegistry, "llm" | "engine" | "datastore" | "secrets" | "executor" | "memory"> & {
|
|
19
20
|
reviewHosts?: ReviewHostRegistry;
|
|
20
21
|
/** Issue trackers (Jira); empty or absent = no issue-tracker integration. */
|
|
@@ -31,6 +32,8 @@ export type McpProviders = Pick<ProviderRegistry, "llm" | "engine" | "datastore"
|
|
|
31
32
|
repoAccess?: RepoAccessGate;
|
|
32
33
|
/** Low-power mode (scale idle proxy/gateway to zero); absent = DISABLED_POWER. */
|
|
33
34
|
power?: Power;
|
|
35
|
+
/** Overrides the outbound call to remote MCP servers (register/update/refresh_mcp_server) — tests only. */
|
|
36
|
+
sandboxFetch?: typeof safeFetch;
|
|
34
37
|
};
|
|
35
38
|
export interface McpRequestContext {
|
|
36
39
|
principal: Principal;
|
package/dist/mcp/index.js
CHANGED
|
@@ -13,6 +13,9 @@
|
|
|
13
13
|
* missing `AUTH_JWKS_URI`/`AUTH_SIGNING_KEY` a deployment running stdio-only
|
|
14
14
|
* never needed to set.
|
|
15
15
|
*/
|
|
16
|
+
import { loadToolMaxTimeoutMs } from "../config/tool-timeout.js";
|
|
17
|
+
import { loadMcpServersMode } from "../config/mcp-servers.js";
|
|
18
|
+
import { loadMcpMaxCallsPerRun } from "../config/mcp-call-limit.js";
|
|
16
19
|
import { startupTestSimulators } from "../config/test-simulators.js";
|
|
17
20
|
import { loadProviderConfig, loadMcpConfig, loadShutdownDrainSeconds, loadAuthConfig, loadGitHubEventConfig, loadJiraConfig, loadGitHubVcsConfig, loadBlobStoreConfig, } from "../config/providers.js";
|
|
18
21
|
import { prisma } from "../core/db.js";
|
|
@@ -62,6 +65,8 @@ import { registerCostReportTools } from "./tools/cost-report.js";
|
|
|
62
65
|
import { registerDatastoreTools } from "./tools/datastore.js";
|
|
63
66
|
import { registerSubAgentTools } from "./tools/subagents.js";
|
|
64
67
|
import { registerGrantTools } from "./tools/grants.js";
|
|
68
|
+
import { registerHostProviderTools } from "./tools/host-providers.js";
|
|
69
|
+
import { registerMcpServerTools } from "./tools/mcp-servers.js";
|
|
65
70
|
import { registerRepositoryTools } from "./tools/repositories.js";
|
|
66
71
|
import { registerIssueProjectTools } from "./tools/issue-projects.js";
|
|
67
72
|
import { registerNotificationChannelTools } from "./tools/notification-channels.js";
|
|
@@ -140,6 +145,8 @@ export function registerAllTools(mcp, opts) {
|
|
|
140
145
|
registerSubAgentTools(mcp);
|
|
141
146
|
registerGrantTools(mcp);
|
|
142
147
|
registerRepositoryTools(mcp);
|
|
148
|
+
registerHostProviderTools(mcp);
|
|
149
|
+
registerMcpServerTools(mcp);
|
|
143
150
|
registerIssueProjectTools(mcp);
|
|
144
151
|
registerNotificationChannelTools(mcp);
|
|
145
152
|
registerHostAccountTools(mcp);
|
|
@@ -193,6 +200,10 @@ export function buildMcpProviders() {
|
|
|
193
200
|
issueTrackers,
|
|
194
201
|
repoAccess,
|
|
195
202
|
};
|
|
203
|
+
// Read again at each tool call; checked here so a bad value stops start-up, not every run.
|
|
204
|
+
loadToolMaxTimeoutMs();
|
|
205
|
+
loadMcpServersMode();
|
|
206
|
+
loadMcpMaxCallsPerRun();
|
|
196
207
|
const nativeExecutor = buildExecutor(providerConfig, nativeProviders, prisma);
|
|
197
208
|
// One Power for the process, shared by both executors (wake) and the scheduler leader (sleep).
|
|
198
209
|
const power = buildPower({ env: process.env, db: prisma });
|
package/dist/mcp/tools/agents.js
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import { Prisma } from "#prisma";
|
|
9
9
|
import { z } from "zod";
|
|
10
|
+
import { codeProviderOf } from "../../coding/code-host.js";
|
|
10
11
|
import { CodingProfilePatchSchema, CodingProfileSchema } from "../../coding/profile.js";
|
|
11
12
|
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
13
|
import { loadBlobStoreConfig, loadContainerExecutorConfig } from "../../config/providers.js";
|
|
@@ -18,12 +19,15 @@ import { ModelUnavailableError } from "../../providers/llm/catalog-types.js";
|
|
|
18
19
|
import { currentModelCatalog } from "../../providers/llm/catalog-store.js";
|
|
19
20
|
import { validateCronExpression } from "../../core/cron.js";
|
|
20
21
|
import { scheduleStartFields } from "../../core/schedule-start.js";
|
|
22
|
+
import { preferIndexScans, withSerializableRetry } from "../../core/serializable-retry.js";
|
|
21
23
|
import { modelSupportedEfforts } from "../../providers/llm/routing.js";
|
|
24
|
+
import { agentMcpServerViews, codexUnavailableMcpTools, codexWriteToolsWarning } from "./mcp-servers.js";
|
|
25
|
+
import { isCodexCoding } from "../../core/mcp-tools.js";
|
|
22
26
|
import { PROJECT_KEY } from "../../providers/issue-tracker/types.js";
|
|
23
27
|
import { LLM_EFFORT_LEVELS, isLlmEffort } from "../../providers/llm/types.js";
|
|
24
28
|
import { requireReadableBudgetGroup } from "../auth/ownership.js";
|
|
25
29
|
import { agentAccessResolver, agentNotFound, assertAgentAccess, readableAgentsWhere, requireAgentAccess, } from "../auth/access.js";
|
|
26
|
-
import { EVERYONE_KEY, atLeast, canDelegate, deleteGrantsFor, effectiveAccess, isPublicWritable, } from "../../core/grants.js";
|
|
30
|
+
import { EVERYONE_KEY, atLeast, canDelegate, deleteGrantsFor, effectiveAccess, isPublicWritable, filterUnusableMcpServers, } from "../../core/grants.js";
|
|
27
31
|
import { projectTool } from "./tools.js";
|
|
28
32
|
import { withZod3ToolWarnings } from "../../core/zod3-window.js";
|
|
29
33
|
import { requireAnyScope, requireScope } from "../auth/resource-server.js";
|
|
@@ -491,7 +495,7 @@ export function registerAgentTools(mcp) {
|
|
|
491
495
|
type: "integer",
|
|
492
496
|
minimum: 1,
|
|
493
497
|
maximum: MAX_DELEGATIONS_PER_RUN,
|
|
494
|
-
description: "Native agents with sub-agents: how many delegate_to_* calls one run may make,
|
|
498
|
+
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
499
|
"(one after another, or together with parallelDelegations). Default 1: route to exactly one.",
|
|
496
500
|
},
|
|
497
501
|
parallelDelegations: { type: "boolean", description: PARALLEL_DELEGATIONS_DESCRIPTION },
|
|
@@ -550,7 +554,7 @@ export function registerAgentTools(mcp) {
|
|
|
550
554
|
const authorization = codingProfile
|
|
551
555
|
? await authorizeRepositoryForSet(ctx, {
|
|
552
556
|
ownerId: ctx.principal.id,
|
|
553
|
-
provider:
|
|
557
|
+
provider: codeProviderOf(codingProfile.repository),
|
|
554
558
|
repository: codingProfile.repository,
|
|
555
559
|
kind: "coding",
|
|
556
560
|
adminOverride: repositoryAdminOverride === true,
|
|
@@ -597,7 +601,7 @@ export function registerAgentTools(mcp) {
|
|
|
597
601
|
type: "integer",
|
|
598
602
|
minimum: 1,
|
|
599
603
|
maximum: MAX_DELEGATIONS_PER_RUN,
|
|
600
|
-
description: "Native agents with sub-agents: how many delegate_to_* calls one run may make,
|
|
604
|
+
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
605
|
"(one after another, or together with parallelDelegations). Default 1: route to exactly one.",
|
|
602
606
|
},
|
|
603
607
|
parallelDelegations: { type: "boolean", description: PARALLEL_DELEGATIONS_DESCRIPTION },
|
|
@@ -738,7 +742,7 @@ export function registerAgentTools(mcp) {
|
|
|
738
742
|
const authorization = plannedRepository !== null && repositoryChanges(before)
|
|
739
743
|
? await authorizeRepositoryForSet(ctx, {
|
|
740
744
|
ownerId: before.ownerId,
|
|
741
|
-
provider:
|
|
745
|
+
provider: codeProviderOf(plannedRepository),
|
|
742
746
|
repository: plannedRepository,
|
|
743
747
|
kind: "coding",
|
|
744
748
|
adminOverride: args.repositoryAdminOverride === true,
|
|
@@ -774,6 +778,17 @@ export function registerAgentTools(mcp) {
|
|
|
774
778
|
await requireReadableBudgetGroup(tx, args.budgetGroupId, ctx.principal.id);
|
|
775
779
|
}
|
|
776
780
|
const nextKind = args.kind ?? existing.kind;
|
|
781
|
+
if (nextKind !== existing.kind) {
|
|
782
|
+
// A write link without triggers is a coding agent's extra repository
|
|
783
|
+
// and a native agent's plain write access; changing kind would
|
|
784
|
+
// silently reinterpret it.
|
|
785
|
+
const reinterpreted = await tx.agentRepository.count({
|
|
786
|
+
where: { agentId: args.id, access: "write", triggers: { isEmpty: true } },
|
|
787
|
+
});
|
|
788
|
+
if (reinterpreted > 0) {
|
|
789
|
+
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.`);
|
|
790
|
+
}
|
|
791
|
+
}
|
|
777
792
|
// A coding repository is an owner binding (agentOwnerBindings): an
|
|
778
793
|
// agent everyone can write never gets one, as attach_* refuse theirs.
|
|
779
794
|
if (((nextKind === "coding" && existing.kind !== "coding") || repositoryChanges(existing)) &&
|
|
@@ -892,16 +907,58 @@ export function registerAgentTools(mcp) {
|
|
|
892
907
|
by: ctx.principal.id,
|
|
893
908
|
}, "coding package audit mode set");
|
|
894
909
|
}
|
|
895
|
-
|
|
910
|
+
const shown = withProfileWarnings(withOperatorMode(agent));
|
|
911
|
+
// Becoming Codex (a provider switch, or a native agent becoming coding) leaves out every attached
|
|
912
|
+
// MCP tool the server doesn't mark read-only: say which, so the owner isn't surprised by a run.
|
|
913
|
+
const wasCodex = before.kind === "coding" && isCodexCoding(before.codingProfile?.provider);
|
|
914
|
+
if (agent.kind === "coding" && isCodexCoding(agent.codingProfile?.provider) && !wasCodex) {
|
|
915
|
+
const writers = await codexUnavailableMcpTools(ctx.db, agent.id);
|
|
916
|
+
if (writers.length > 0) {
|
|
917
|
+
return textResult({ ...shown, warnings: [...(shown.warnings ?? []), codexWriteToolsWarning(writers)] });
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
return textResult(shown);
|
|
896
921
|
},
|
|
897
922
|
});
|
|
898
923
|
mcp.registerTool({
|
|
899
924
|
name: "list_agents",
|
|
900
925
|
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
|
-
|
|
926
|
+
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. " +
|
|
927
|
+
"summary: true returns only id, name, kind, model, codingProvider, budgetUsd, ownerId and access per agent, for listings that don't need the full configuration. " +
|
|
928
|
+
"all: true lists every agent in the deployment (admins only, admin:view); an agent you have no grant on shows access `none`.",
|
|
929
|
+
inputSchema: {
|
|
930
|
+
type: "object",
|
|
931
|
+
properties: { summary: { type: "boolean" }, all: { type: "boolean" } },
|
|
932
|
+
additionalProperties: false,
|
|
933
|
+
},
|
|
934
|
+
handler: async (args, ctx) => {
|
|
935
|
+
if (args.all === true && !ctx.operator)
|
|
936
|
+
requireScope(ctx, ctx.canonicalUri, "admin:view");
|
|
937
|
+
const [where, accessOf] = await Promise.all([
|
|
938
|
+
args.all === true ? Promise.resolve({}) : readableAgentsWhere(ctx),
|
|
939
|
+
agentAccessResolver(ctx),
|
|
940
|
+
]);
|
|
941
|
+
if (args.summary === true) {
|
|
942
|
+
const agents = await ctx.db.agent.findMany({
|
|
943
|
+
where,
|
|
944
|
+
select: {
|
|
945
|
+
id: true,
|
|
946
|
+
name: true,
|
|
947
|
+
kind: true,
|
|
948
|
+
model: true,
|
|
949
|
+
budgetUsd: true,
|
|
950
|
+
ownerId: true,
|
|
951
|
+
codingProfile: { select: { provider: true } },
|
|
952
|
+
},
|
|
953
|
+
orderBy: { name: "asc" },
|
|
954
|
+
});
|
|
955
|
+
return textResult(agents.map(({ codingProfile, budgetUsd, ...agent }) => ({
|
|
956
|
+
...agent,
|
|
957
|
+
codingProvider: codingProfile?.provider ?? null,
|
|
958
|
+
budgetUsd: Number(budgetUsd),
|
|
959
|
+
access: accessOf(agent),
|
|
960
|
+
})));
|
|
961
|
+
}
|
|
905
962
|
const agents = await ctx.db.agent.findMany({ where, include: { codingProfile: true } });
|
|
906
963
|
return textResult(agents.map((agent) => ({ ...withOperatorMode(agent), access: accessOf(agent) })));
|
|
907
964
|
},
|
|
@@ -925,6 +982,7 @@ export function registerAgentTools(mcp) {
|
|
|
925
982
|
return textResult({
|
|
926
983
|
...withProfileWarnings(withOperatorMode(agent)),
|
|
927
984
|
access,
|
|
985
|
+
mcpServers: await agentMcpServerViews(ctx.db, agent.id),
|
|
928
986
|
// Zod 3 window: the caller's own tools carry their warnings (see core/zod3-window.ts).
|
|
929
987
|
tools: await Promise.all(agent.tools.map(async (attachment) => ({
|
|
930
988
|
...attachment,
|
|
@@ -974,7 +1032,7 @@ export function registerAgentTools(mcp) {
|
|
|
974
1032
|
mcp.registerTool({
|
|
975
1033
|
name: "make_owner",
|
|
976
1034
|
scope: "agents:admin",
|
|
977
|
-
description: "Admins: gives an agent a new owner (an existing principal id). Secret and
|
|
1035
|
+
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
1036
|
inputSchema: {
|
|
979
1037
|
type: "object",
|
|
980
1038
|
additionalProperties: false,
|
|
@@ -996,7 +1054,9 @@ export function registerAgentTools(mcp) {
|
|
|
996
1054
|
const principal = await ctx.db.principal.findUnique({ where: { id: newOwner } });
|
|
997
1055
|
if (!principal)
|
|
998
1056
|
throw new McpError(400, `Principal "${newOwner}" not found.`);
|
|
999
|
-
|
|
1057
|
+
// Bounded retry: a concurrent grant or attach on the same rows aborts one side (Serializable).
|
|
1058
|
+
const result = await withSerializableRetry(() => ctx.db.$transaction(async (tx) => {
|
|
1059
|
+
await preferIndexScans(tx);
|
|
1000
1060
|
const agent = await tx.agent.findUnique({ where: { id: args.agentId }, include: { codingProfile: true } });
|
|
1001
1061
|
if (!agent)
|
|
1002
1062
|
throw agentNotFound(args.agentId);
|
|
@@ -1015,7 +1075,10 @@ export function registerAgentTools(mcp) {
|
|
|
1015
1075
|
where: { agentId: agent.id, authorizedVia: approved },
|
|
1016
1076
|
orderBy: { repository: "asc" },
|
|
1017
1077
|
});
|
|
1018
|
-
await tx.agentRepository.updateMany({
|
|
1078
|
+
await tx.agentRepository.updateMany({
|
|
1079
|
+
where: { agentId: agent.id, authorizedVia: approved },
|
|
1080
|
+
data: reset,
|
|
1081
|
+
});
|
|
1019
1082
|
revoked.push(...links.map((l) => l.repository));
|
|
1020
1083
|
const profile = await tx.codingAgentProfile.updateMany({
|
|
1021
1084
|
where: { agentId: agent.id, repositoryAuthorizedVia: approved },
|
|
@@ -1072,9 +1135,31 @@ export function registerAgentTools(mcp) {
|
|
|
1072
1135
|
where: { agentId: agent.id, datastoreId: { in: foreignDatastores.map((b) => b.datastoreId) } },
|
|
1073
1136
|
});
|
|
1074
1137
|
}
|
|
1138
|
+
// MCP server attachments: the server's brokered secrets act for its
|
|
1139
|
+
// owner, so a server the new owner neither owns nor holds use on is detached.
|
|
1140
|
+
const mcpServers = await tx.agentMcpServer.findMany({
|
|
1141
|
+
where: { agentId: agent.id },
|
|
1142
|
+
include: { server: { select: { id: true, name: true, ownerId: true } } },
|
|
1143
|
+
orderBy: { serverId: "asc" },
|
|
1144
|
+
});
|
|
1145
|
+
const foreignMcpServers = await filterUnusableMcpServers(tx, () => newOwner, mcpServers);
|
|
1146
|
+
if (foreignMcpServers.length > 0) {
|
|
1147
|
+
await tx.agentMcpServer.deleteMany({
|
|
1148
|
+
where: { agentId: agent.id, serverId: { in: foreignMcpServers.map((b) => b.serverId) } },
|
|
1149
|
+
});
|
|
1150
|
+
}
|
|
1075
1151
|
const bindingsRemoved = [
|
|
1076
1152
|
...foreignSecrets.map((b) => ({ kind: "secret", boundName: b.boundName, resourceId: b.secretId })),
|
|
1077
|
-
...foreignDatastores.map((b) => ({
|
|
1153
|
+
...foreignDatastores.map((b) => ({
|
|
1154
|
+
kind: "datastore",
|
|
1155
|
+
boundName: b.boundName,
|
|
1156
|
+
resourceId: b.datastoreId,
|
|
1157
|
+
})),
|
|
1158
|
+
...foreignMcpServers.map((b) => ({
|
|
1159
|
+
kind: "mcp_server",
|
|
1160
|
+
boundName: b.server.name,
|
|
1161
|
+
resourceId: b.serverId,
|
|
1162
|
+
})),
|
|
1078
1163
|
];
|
|
1079
1164
|
// Tool capabilities: rows aren't rewritten; the runner ignores any
|
|
1080
1165
|
// the new owner never stamped (capabilitiesGrantedById).
|
|
@@ -1132,7 +1217,7 @@ export function registerAgentTools(mcp) {
|
|
|
1132
1217
|
everyoneGrant,
|
|
1133
1218
|
webhooksInactive,
|
|
1134
1219
|
};
|
|
1135
|
-
}, { isolationLevel: Prisma.TransactionIsolationLevel.Serializable });
|
|
1220
|
+
}, { isolationLevel: Prisma.TransactionIsolationLevel.Serializable }));
|
|
1136
1221
|
return textResult(result);
|
|
1137
1222
|
},
|
|
1138
1223
|
});
|