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.
This commit is contained in:
MoerAI
2026-05-22 16:01:13 +09:00
parent cb205e1479
commit 6b0628dde5
4 changed files with 47 additions and 5 deletions
+8 -1
View File
@@ -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({
+11 -1
View File
@@ -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)
+15 -2
View File
@@ -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(
+13 -1
View File
@@ -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(