Merge pull request #1951 from edxeth/feat/custom-agents

feat(config): make custom agents first-class for planning and delegation
This commit is contained in:
YeonGyu-Kim
2026-03-02 23:28:57 +09:00
committed by GitHub
19 changed files with 1511 additions and 47 deletions
+53 -2
View File
@@ -1,5 +1,6 @@
import { z } from "zod"
import { FallbackModelsSchema } from "./fallback-models"
import { OverridableAgentNameSchema } from "./agent-names"
import { AgentPermissionSchema } from "./internal/permission"
export const AgentOverrideConfigSchema = z.object({
@@ -55,7 +56,7 @@ export const AgentOverrideConfigSchema = z.object({
.optional(),
})
export const AgentOverridesSchema = z.object({
const BuiltinAgentOverridesSchema = z.object({
build: AgentOverrideConfigSchema.optional(),
plan: AgentOverrideConfigSchema.optional(),
sisyphus: AgentOverrideConfigSchema.optional(),
@@ -72,7 +73,57 @@ export const AgentOverridesSchema = z.object({
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()),
)
function escapeRegexLiteral(value: string): string {
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")
}
function toCaseInsensitiveLiteralPattern(value: string): string {
return value
.split("")
.map((char) => {
if (/^[A-Za-z]$/.test(char)) {
const lower = char.toLowerCase()
const upper = char.toUpperCase()
return `[${lower}${upper}]`
}
return escapeRegexLiteral(char)
})
.join("")
}
const RESERVED_CUSTOM_AGENT_NAME_PATTERN = new RegExp(
`^(?!(?:${RESERVED_CUSTOM_AGENT_NAMES.map(toCaseInsensitiveLiteralPattern).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<typeof AgentOverrideConfigSchema>
export type AgentOverrides = z.infer<typeof AgentOverridesSchema>
export type CustomAgentOverrides = z.infer<typeof CustomAgentOverridesSchema>
+2 -1
View File
@@ -1,7 +1,7 @@
import { z } from "zod"
import { AnyMcpNameSchema } from "../../mcp/types"
import { BuiltinAgentNameSchema, BuiltinSkillNameSchema } from "./agent-names"
import { AgentOverridesSchema } from "./agent-overrides"
import { AgentOverridesSchema, CustomAgentOverridesSchema } from "./agent-overrides"
import { BabysittingConfigSchema } from "./babysitting"
import { BackgroundTaskConfigSchema } from "./background-task"
import { BrowserAutomationConfigSchema } from "./browser-automation"
@@ -39,6 +39,7 @@ export const OhMyOpenCodeConfigSchema = z.object({
/** Enable model fallback on API errors (default: false). Set to true to enable automatic model switching when model errors occur. */
model_fallback: z.boolean().optional(),
agents: AgentOverridesSchema.optional(),
custom_agents: CustomAgentOverridesSchema.optional(),
categories: CategoriesConfigSchema.optional(),
claude_code: ClaudeCodeConfigSchema.optional(),
sisyphus_agent: SisyphusAgentConfigSchema.optional(),