import { createBuiltinAgents } from "../agents"; import { createSisyphusJuniorAgentWithOverrides } from "../agents/sisyphus-junior"; import type { OhMyOpenCodeConfig } from "../config"; import { log, migrateAgentConfig } from "../shared"; import { AGENT_NAME_MAP } from "../shared/migration"; import { getAgentDisplayName } from "../shared/agent-display-names"; import { registerAgentName } from "../features/claude-code-session-state"; import { discoverConfigSourceSkills, discoverGlobalAgentsSkills, discoverOpencodeGlobalSkills, discoverOpencodeProjectSkills, discoverProjectAgentsSkills, discoverProjectClaudeSkills, discoverUserClaudeSkills, } from "../features/opencode-skill-loader"; import { loadProjectAgents, loadUserAgents } from "../features/claude-code-agent-loader"; import type { PluginComponents } from "./plugin-components-loader"; import { reorderAgentsByPriority } from "./agent-priority-order"; import { remapAgentKeysToDisplayNames } from "./agent-key-remapper"; import { createProtectedAgentNameSet, filterProtectedAgentOverrides, } from "./agent-override-protection"; import { buildPrometheusAgentConfig } from "./prometheus-agent-config-builder"; import { buildPlanDemoteConfig } from "./plan-model-inheritance"; type AgentConfigRecord = Record | undefined> & { build?: Record; plan?: Record; }; function getConfiguredDefaultAgent(config: Record): string | undefined { const defaultAgent = config.default_agent; if (typeof defaultAgent !== "string") return undefined; const trimmedDefaultAgent = defaultAgent.trim(); return trimmedDefaultAgent.length > 0 ? trimmedDefaultAgent : undefined; } export async function applyAgentConfig(params: { config: Record; pluginConfig: OhMyOpenCodeConfig; ctx: { directory: string; client?: any }; pluginComponents: PluginComponents; }): Promise> { const migratedDisabledAgents = (params.pluginConfig.disabled_agents ?? []).map( (agent) => { return AGENT_NAME_MAP[agent.toLowerCase()] ?? AGENT_NAME_MAP[agent] ?? agent; }, ) as typeof params.pluginConfig.disabled_agents; const includeClaudeSkillsForAwareness = params.pluginConfig.claude_code?.skills ?? true; const [ discoveredConfigSourceSkills, discoveredUserSkills, discoveredProjectSkills, discoveredProjectAgentsSkills, discoveredOpencodeGlobalSkills, discoveredOpencodeProjectSkills, discoveredGlobalAgentsSkills, ] = await Promise.all([ discoverConfigSourceSkills({ config: params.pluginConfig.skills, configDir: params.ctx.directory, }), includeClaudeSkillsForAwareness ? discoverUserClaudeSkills() : Promise.resolve([]), includeClaudeSkillsForAwareness ? discoverProjectClaudeSkills(params.ctx.directory) : Promise.resolve([]), includeClaudeSkillsForAwareness ? discoverProjectAgentsSkills(params.ctx.directory) : Promise.resolve([]), discoverOpencodeGlobalSkills(), discoverOpencodeProjectSkills(params.ctx.directory), includeClaudeSkillsForAwareness ? discoverGlobalAgentsSkills() : Promise.resolve([]), ]); const allDiscoveredSkills = [ ...discoveredConfigSourceSkills, ...discoveredOpencodeProjectSkills, ...discoveredProjectSkills, ...discoveredProjectAgentsSkills, ...discoveredOpencodeGlobalSkills, ...discoveredUserSkills, ...discoveredGlobalAgentsSkills, ]; const browserProvider = params.pluginConfig.browser_automation_engine?.provider ?? "playwright"; const currentModel = params.config.model as string | undefined; const disabledSkills = new Set(params.pluginConfig.disabled_skills ?? []); const useTaskSystem = params.pluginConfig.experimental?.task_system ?? true; const disableOmoEnv = params.pluginConfig.experimental?.disable_omo_env ?? false; const includeClaudeAgents = params.pluginConfig.claude_code?.agents ?? true; const userAgents = includeClaudeAgents ? loadUserAgents() : {}; const projectAgents = includeClaudeAgents ? loadProjectAgents(params.ctx.directory) : {}; const rawPluginAgents = params.pluginComponents.agents; const pluginAgents = Object.fromEntries( Object.entries(rawPluginAgents).map(([key, value]) => [ key, value ? migrateAgentConfig(value as Record) : value, ]), ); const configAgent = params.config.agent as AgentConfigRecord | undefined; const customAgentSummaries = [ ...Object.entries(configAgent ?? {}), ...Object.entries(userAgents), ...Object.entries(projectAgents), ...Object.entries(pluginAgents).filter(([, config]) => config !== undefined), ] .filter(([, config]) => config != null) .map(([name, config]) => ({ name, description: typeof (config as Record)?.description === "string" ? ((config as Record).description as string) : "", })); const builtinAgents = await createBuiltinAgents( migratedDisabledAgents, params.pluginConfig.agents, params.ctx.directory, currentModel, params.pluginConfig.categories, params.pluginConfig.git_master, allDiscoveredSkills, customAgentSummaries, browserProvider, currentModel, disabledSkills, useTaskSystem, disableOmoEnv, ); const disabledAgentNames = new Set( (migratedDisabledAgents ?? []).map(a => a.toLowerCase()) ); const filterDisabledAgents = (agents: Record) => Object.fromEntries( Object.entries(agents).filter(([name]) => !disabledAgentNames.has(name.toLowerCase())) ); const isSisyphusEnabled = params.pluginConfig.sisyphus_agent?.disabled !== true; const builderEnabled = params.pluginConfig.sisyphus_agent?.default_builder_enabled ?? false; const plannerEnabled = params.pluginConfig.sisyphus_agent?.planner_enabled ?? true; const replacePlan = params.pluginConfig.sisyphus_agent?.replace_plan ?? true; const shouldDemotePlan = plannerEnabled && replacePlan; const configuredDefaultAgent = getConfiguredDefaultAgent(params.config); if (isSisyphusEnabled && builtinAgents.sisyphus) { if (configuredDefaultAgent) { (params.config as { default_agent?: string }).default_agent = getAgentDisplayName(configuredDefaultAgent); } else { (params.config as { default_agent?: string }).default_agent = getAgentDisplayName("sisyphus"); } const agentConfig: Record = { sisyphus: builtinAgents.sisyphus, }; agentConfig["sisyphus-junior"] = createSisyphusJuniorAgentWithOverrides( params.pluginConfig.agents?.["sisyphus-junior"], (builtinAgents.atlas as { model?: string } | undefined)?.model, useTaskSystem, ); if (builderEnabled) { const { name: _buildName, ...buildConfigWithoutName } = configAgent?.build ?? {}; const migratedBuildConfig = migrateAgentConfig( buildConfigWithoutName as Record, ); const override = params.pluginConfig.agents?.["OpenCode-Builder"]; const base = { ...migratedBuildConfig, description: `${(configAgent?.build?.description as string) ?? "Build agent"} (OpenCode default)`, }; agentConfig["OpenCode-Builder"] = override ? { ...base, ...override } : base; } if (plannerEnabled) { const prometheusOverride = params.pluginConfig.agents?.["prometheus"] as | (Record & { prompt_append?: string }) | undefined; agentConfig["prometheus"] = await buildPrometheusAgentConfig({ configAgentPlan: configAgent?.plan, pluginPrometheusOverride: prometheusOverride, userCategories: params.pluginConfig.categories, currentModel, disabledTools: params.pluginConfig.disabled_tools, }); } const filteredConfigAgents = configAgent ? Object.fromEntries( Object.entries(configAgent) .filter(([key]) => { if (key === "build") return false; if (key === "plan" && shouldDemotePlan) return false; if (key in builtinAgents) return false; return true; }) .map(([key, value]) => [ key, value ? migrateAgentConfig(value as Record) : value, ]), ) : {}; const migratedBuild = configAgent?.build ? migrateAgentConfig(configAgent.build as Record) : {}; const planDemoteConfig = shouldDemotePlan ? buildPlanDemoteConfig( agentConfig["prometheus"] as Record | undefined, params.pluginConfig.agents?.plan as Record | undefined, ) : undefined; const protectedBuiltinAgentNames = createProtectedAgentNameSet([ ...Object.keys(agentConfig), ...Object.keys(builtinAgents), ]); const filteredUserAgents = filterProtectedAgentOverrides( userAgents, protectedBuiltinAgentNames, ); const filteredProjectAgents = filterProtectedAgentOverrides( projectAgents, protectedBuiltinAgentNames, ); const filteredPluginAgents = filterProtectedAgentOverrides( pluginAgents, protectedBuiltinAgentNames, ); params.config.agent = { ...agentConfig, ...Object.fromEntries( Object.entries(builtinAgents).filter(([key]) => key !== "sisyphus"), ), ...filterDisabledAgents(filteredUserAgents), ...filterDisabledAgents(filteredProjectAgents), ...filterDisabledAgents(filteredPluginAgents), ...filteredConfigAgents, build: { ...migratedBuild, mode: "subagent", hidden: true }, ...(planDemoteConfig ? { plan: planDemoteConfig } : {}), }; } else { const protectedBuiltinAgentNames = createProtectedAgentNameSet( Object.keys(builtinAgents), ); const filteredUserAgents = filterProtectedAgentOverrides( userAgents, protectedBuiltinAgentNames, ); const filteredProjectAgents = filterProtectedAgentOverrides( projectAgents, protectedBuiltinAgentNames, ); const filteredPluginAgents = filterProtectedAgentOverrides( pluginAgents, protectedBuiltinAgentNames, ); params.config.agent = { ...builtinAgents, ...filterDisabledAgents(filteredUserAgents), ...filterDisabledAgents(filteredProjectAgents), ...filterDisabledAgents(filteredPluginAgents), ...configAgent, }; } if (params.config.agent) { params.config.agent = remapAgentKeysToDisplayNames( params.config.agent as Record, ); params.config.agent = reorderAgentsByPriority( params.config.agent as Record, ); } const agentResult = params.config.agent as Record; for (const name of Object.keys(agentResult)) { registerAgentName(name); } log("[config-handler] agents loaded", { agentKeys: Object.keys(agentResult) }); return agentResult; }