From 6b0628dde5b93af505dd8c227796d11f6e0d7505 Mon Sep 17 00:00:00 2001 From: MoerAI Date: Fri, 22 May 2026 16:01:13 +0900 Subject: [PATCH] fix(agents): log warning when a builtin agent is silently skipped due to model availability (fixes #4150) When AGENT_MODEL_REQUIREMENTS gates an agent (sisyphus / hephaestus / atlas / general agents) and resolution returns no model -- or the requiresModel / requiresAnyModel / requiresProvider constraint is not satisfied by any connected provider -- the agent was being dropped from the registered set with no log, no warning, no doctor signal. Users upgrading from v3 to v4 saw their configured agents disappear from 'opencode agent list' with zero indication of why, and the only debugging path was reading the source.\n\nAdd a log() call at every silent-drop point in the four wrapper files so the user-facing 'oh-my-opencode.log' shows exactly which agent was skipped and which model / provider requirement failed. Follows the existing precedent in src/tools/delegate-task/categories.ts:44 ([resolveCategoryConfig] Category X requires Y but not available) and the in-file precedent in general-agents.ts:86 ([agent-registration] User-configured model not resolved). Zero behavior change -- agents that were skipped before are still skipped now, the only difference is they are no longer silent.\n\nVerification: bun test src/agents/builtin-agents/ -> 27/27 pass. bun test src/agents/ -> 405/405 pass. bun run typecheck -> exit 0. LSP diagnostics clean on all four touched files. --- src/agents/builtin-agents/atlas-agent.ts | 9 ++++++++- src/agents/builtin-agents/general-agents.ts | 12 +++++++++++- src/agents/builtin-agents/hephaestus-agent.ts | 17 +++++++++++++++-- src/agents/builtin-agents/sisyphus-agent.ts | 14 +++++++++++++- 4 files changed, 47 insertions(+), 5 deletions(-) diff --git a/src/agents/builtin-agents/atlas-agent.ts b/src/agents/builtin-agents/atlas-agent.ts index bea50480b..c5120eefc 100644 --- a/src/agents/builtin-agents/atlas-agent.ts +++ b/src/agents/builtin-agents/atlas-agent.ts @@ -3,6 +3,7 @@ import type { AgentOverrides } from "../types" import type { CategoriesConfig, CategoryConfig } from "../../config/schema" import type { AvailableAgent, AvailableSkill } from "../dynamic-agent-prompt-builder" import { AGENT_MODEL_REQUIREMENTS } from "../../shared" +import { log } from "../../shared/logger" import { applyOverrides } from "./agent-overrides" import { applyModelResolution } from "./model-resolution" import { createAtlasAgent } from "../atlas" @@ -46,7 +47,13 @@ export function maybeCreateAtlasConfig(input: { systemDefaultModel, }) - if (!atlasResolution) return undefined + if (!atlasResolution) { + log("[agent-registration] Agent skipped: model resolution returned no result", { + agent: "atlas", + configuredModel: orchestratorOverride?.model, + }) + return undefined + } const { model: atlasModel, variant: atlasResolvedVariant } = atlasResolution let orchestratorConfig = createAtlasAgent({ diff --git a/src/agents/builtin-agents/general-agents.ts b/src/agents/builtin-agents/general-agents.ts index 065e26831..aefcb0f87 100644 --- a/src/agents/builtin-agents/general-agents.ts +++ b/src/agents/builtin-agents/general-agents.ts @@ -66,6 +66,10 @@ export function collectPendingBuiltinAgents(input: { // Check if agent requires a specific model if (requirement?.requiresModel && availableModels) { if (!isModelAvailable(requirement.requiresModel, availableModels)) { + log("[agent-registration] Agent skipped: required model not available", { + agent: agentName, + requiredModel: requirement.requiresModel, + }) continue } } @@ -92,7 +96,13 @@ export function collectPendingBuiltinAgents(input: { resolution = getFirstFallbackModel(requirement) } } - if (!resolution) continue + if (!resolution) { + log("[agent-registration] Agent skipped: model resolution returned no result", { + agent: agentName, + configuredModel: override?.model, + }) + continue + } const { model, variant: resolvedVariant } = resolution let config = buildAgent(source, model, mergedCategories) diff --git a/src/agents/builtin-agents/hephaestus-agent.ts b/src/agents/builtin-agents/hephaestus-agent.ts index c05b1fa71..0b42913f2 100644 --- a/src/agents/builtin-agents/hephaestus-agent.ts +++ b/src/agents/builtin-agents/hephaestus-agent.ts @@ -3,6 +3,7 @@ import type { AgentOverrides } from "../types" import type { CategoryConfig } from "../../config/schema" import type { AvailableAgent, AvailableCategory, AvailableSkill } from "../dynamic-agent-prompt-builder" import { AGENT_MODEL_REQUIREMENTS, isAnyProviderConnected } from "../../shared" +import { log } from "../../shared/logger" import { createHephaestusAgent } from "../hephaestus" import { applyEnvironmentContext } from "./environment-context" import { applyCategoryOverride, mergeAgentConfig } from "./agent-overrides" @@ -51,7 +52,13 @@ export function maybeCreateHephaestusConfig(input: { isFirstRunNoCache || isAnyProviderConnected(hephaestusRequirement.requiresProvider, availableModels) - if (!hasRequiredProvider) return undefined + if (!hasRequiredProvider) { + log("[agent-registration] Agent skipped: required provider not connected", { + agent: "hephaestus", + requiredProvider: hephaestusRequirement?.requiresProvider, + }) + return undefined + } let hephaestusResolution = applyModelResolution({ userModel: hephaestusOverride?.model, @@ -64,7 +71,13 @@ export function maybeCreateHephaestusConfig(input: { hephaestusResolution = getFirstFallbackModel(hephaestusRequirement) } - if (!hephaestusResolution) return undefined + if (!hephaestusResolution) { + log("[agent-registration] Agent skipped: model resolution returned no result", { + agent: "hephaestus", + configuredModel: hephaestusOverride?.model, + }) + return undefined + } const { model: hephaestusModel, variant: hephaestusResolvedVariant } = hephaestusResolution let hephaestusConfig = createHephaestusAgent( diff --git a/src/agents/builtin-agents/sisyphus-agent.ts b/src/agents/builtin-agents/sisyphus-agent.ts index 6cb91370f..6a8d0dc1c 100644 --- a/src/agents/builtin-agents/sisyphus-agent.ts +++ b/src/agents/builtin-agents/sisyphus-agent.ts @@ -3,6 +3,7 @@ import type { AgentOverrides } from "../types" import type { CategoriesConfig, CategoryConfig } from "../../config/schema" import type { AvailableAgent, AvailableCategory, AvailableSkill } from "../dynamic-agent-prompt-builder" import { AGENT_MODEL_REQUIREMENTS, isAnyFallbackModelAvailable } from "../../shared" +import { log } from "../../shared/logger" import { applyEnvironmentContext } from "./environment-context" import { applyOverrides } from "./agent-overrides" import { applyModelResolution, getFirstFallbackModel } from "./model-resolution" @@ -51,6 +52,11 @@ export function maybeCreateSisyphusConfig(input: { isFirstRunNoCache || isAnyFallbackModelAvailable(sisyphusRequirement.fallbackChain, availableModels) + if (!disabledAgents.includes("sisyphus") && !meetsSisyphusAnyModelRequirement) { + log("[agent-registration] Agent skipped: no model in fallback chain is available", { + agent: "sisyphus", + }) + } if (disabledAgents.includes("sisyphus") || !meetsSisyphusAnyModelRequirement) return undefined let sisyphusResolution = applyModelResolution({ @@ -65,7 +71,13 @@ export function maybeCreateSisyphusConfig(input: { sisyphusResolution = getFirstFallbackModel(sisyphusRequirement) } - if (!sisyphusResolution) return undefined + if (!sisyphusResolution) { + log("[agent-registration] Agent skipped: model resolution returned no result", { + agent: "sisyphus", + configuredModel: sisyphusOverride?.model, + }) + return undefined + } const { model: sisyphusModel, variant: sisyphusResolvedVariant } = sisyphusResolution let sisyphusConfig = createSisyphusAgent(