import { z } from "zod" import { FallbackModelsSchema } from "./fallback-models" import { OverridableAgentNameSchema } from "./agent-names" import { AgentPermissionSchema } from "./internal/permission" export const AgentOverrideConfigSchema = z.object({ /** @deprecated Use `category` instead. Model is inherited from category defaults. */ model: z.string().optional(), fallback_models: FallbackModelsSchema.optional(), variant: z.string().optional(), /** Category name to inherit model and other settings from CategoryConfig */ category: z.string().optional(), /** Skill names to inject into agent prompt */ skills: z.array(z.string()).optional(), temperature: z.number().min(0).max(2).optional(), top_p: z.number().min(0).max(1).optional(), prompt: z.string().optional(), /** Text to append to agent prompt. Supports file:// URIs (file:///abs, file://./rel, file://~/home) */ prompt_append: z.string().optional(), tools: z.record(z.string(), z.boolean()).optional(), disable: z.boolean().optional(), description: z.string().optional(), mode: z.enum(["subagent", "primary", "all"]).optional(), color: z .string() .regex(/^#[0-9A-Fa-f]{6}$/) .optional(), permission: AgentPermissionSchema.optional(), /** Maximum tokens for response. Passed directly to OpenCode SDK. */ maxTokens: z.number().optional(), /** Extended thinking configuration (Anthropic). Overrides category and default settings. */ thinking: z .object({ type: z.enum(["enabled", "disabled"]), budgetTokens: z.number().optional(), }) .optional(), /** Reasoning effort level (OpenAI). Overrides category and default settings. */ reasoningEffort: z.enum(["low", "medium", "high", "xhigh"]).optional(), /** Text verbosity level. */ textVerbosity: z.enum(["low", "medium", "high"]).optional(), /** Provider-specific options. Passed directly to OpenCode SDK. */ providerOptions: z.record(z.string(), z.unknown()).optional(), /** Per-message ultrawork override model/variant when ultrawork keyword is detected. */ ultrawork: z .object({ model: z.string().optional(), variant: z.string().optional(), }) .optional(), compaction: z .object({ model: z.string().optional(), variant: z.string().optional(), }) .optional(), }) const BuiltinAgentOverridesSchema = z.object({ build: AgentOverrideConfigSchema.optional(), plan: AgentOverrideConfigSchema.optional(), sisyphus: AgentOverrideConfigSchema.optional(), hephaestus: AgentOverrideConfigSchema.optional(), "sisyphus-junior": AgentOverrideConfigSchema.optional(), "OpenCode-Builder": AgentOverrideConfigSchema.optional(), prometheus: AgentOverrideConfigSchema.optional(), metis: AgentOverrideConfigSchema.optional(), momus: AgentOverrideConfigSchema.optional(), oracle: AgentOverrideConfigSchema.optional(), librarian: AgentOverrideConfigSchema.optional(), explore: AgentOverrideConfigSchema.optional(), "multimodal-looker": AgentOverrideConfigSchema.optional(), atlas: AgentOverrideConfigSchema.optional(), }).strict() export const AgentOverridesSchema = BuiltinAgentOverridesSchema const RESERVED_CUSTOM_AGENT_NAMES = OverridableAgentNameSchema.options const RESERVED_CUSTOM_AGENT_NAME_SET = new Set( RESERVED_CUSTOM_AGENT_NAMES.map((name) => name.toLowerCase()), ) const RESERVED_CUSTOM_AGENT_NAME_PATTERN = new RegExp( `^(?!(?:${RESERVED_CUSTOM_AGENT_NAMES.map((name) => name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|")})$).+`, ) export const CustomAgentOverridesSchema = z .record( z.string().regex( RESERVED_CUSTOM_AGENT_NAME_PATTERN, "custom_agents key cannot reuse built-in agent override name", ), AgentOverrideConfigSchema, ) .superRefine((value, ctx) => { for (const key of Object.keys(value)) { if (RESERVED_CUSTOM_AGENT_NAME_SET.has(key.toLowerCase())) { ctx.addIssue({ code: z.ZodIssueCode.custom, path: [key], message: "custom_agents key cannot reuse built-in agent override name", }) } } }) export type AgentOverrideConfig = z.infer export type AgentOverrides = z.infer export type CustomAgentOverrides = z.infer