feat(providers): add native options and exact MCP grants (#3025)

* feat(providers): add native options and exact MCP grants

Keep provider policy in provider-owned schemas so unattended Hub runs can grant only requested tools from injected MCP servers. Preserve the native resolved sandbox policy when a workflow override is present, including configured writable roots.

* test(hub): verify real provider tool policy

* test(app): avoid pinning transient restart status
This commit is contained in:
Mohamed Boudra
2026-08-08 16:49:29 +02:00
committed by GitHub
parent b669dad221
commit f0eb7cea4b
48 changed files with 2497 additions and 280 deletions
+47 -12
View File
@@ -362,6 +362,22 @@ const McpServerConfigSchema = z.discriminatedUnion("type", [
McpSseServerConfigSchema,
]);
const ProviderOptionsSchema = z.record(z.string(), z.json());
const McpToolRefSchema = z
.object({
kind: z.literal("mcp"),
server: z.string().trim().min(1),
tool: z.string().trim().min(1),
})
.strict();
const ToolPolicySchema = z
.object({
preapproved: z.array(McpToolRefSchema),
})
.strict();
const AgentSessionConfigSchema = z.object({
provider: AgentProviderSchema,
cwd: z.string(),
@@ -370,17 +386,8 @@ const AgentSessionConfigSchema = z.object({
thinkingOptionId: z.string().optional(),
featureValues: z.record(z.string(), z.unknown()).optional(),
title: z.string().trim().min(1).max(MAX_EXPLICIT_AGENT_TITLE_CHARS).optional().nullable(),
approvalPolicy: z.string().optional(),
sandboxMode: z.string().optional(),
networkAccess: z.boolean().optional(),
webSearch: z.boolean().optional(),
extra: z
.object({
codex: z.record(z.string(), z.unknown()).optional(),
claude: z.record(z.string(), z.unknown()).optional(),
})
.partial()
.optional(),
providerOptions: ProviderOptionsSchema.optional(),
toolPolicy: ToolPolicySchema.optional(),
systemPrompt: z.string().optional(),
mcpServers: z.record(z.string(), McpServerConfigSchema).optional(),
});
@@ -2513,6 +2520,8 @@ export const HubExecutionAgentCreateRequestSchema = z.object({
modeId: z.string().optional(),
thinkingOptionId: z.string().optional(),
featureValues: z.record(z.string(), z.unknown()).optional(),
providerOptions: ProviderOptionsSchema.optional(),
toolPolicy: ToolPolicySchema.optional(),
env: z.record(z.string(), z.string()).optional(),
mcpServers: z.record(z.string(), McpServerConfigSchema).optional(),
worktree: CreateAgentWorktreeTargetSchema.optional(),
@@ -2520,6 +2529,31 @@ export const HubExecutionAgentCreateRequestSchema = z.object({
export type HubExecutionAgentCreateRequest = z.infer<typeof HubExecutionAgentCreateRequestSchema>;
const HubExecutionAgentCreateErrorSchema = z.discriminatedUnion("code", [
z.object({
code: z.literal("provider_options_invalid"),
provider: z.string(),
issues: z.array(
z.object({
path: z.array(z.union([z.string(), z.number()])),
message: z.string(),
}),
),
message: z.string(),
}),
z.object({
code: z.literal("tool_policy_unsupported"),
provider: z.string(),
message: z.string(),
}),
z.object({
code: z.literal("create_failed"),
message: z.string(),
}),
]);
export type HubExecutionAgentCreateError = z.infer<typeof HubExecutionAgentCreateErrorSchema>;
export const HubExecutionControlActionSchema = z.enum(["interrupt", "archive"]);
export type HubExecutionControlAction = z.infer<typeof HubExecutionControlActionSchema>;
@@ -5300,7 +5334,8 @@ export const HubExecutionAgentCreateResponseSchema = z.object({
agentId: z.string().nullable(),
agent: AgentSnapshotPayloadSchema.nullable(),
success: z.boolean(),
error: z.string().nullable(),
toolPolicyApplied: z.literal(true).optional(),
error: HubExecutionAgentCreateErrorSchema.nullable(),
}),
});