From e299c09ee860db4fc785218857fee1eca604eb1c Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Tue, 17 Feb 2026 01:51:31 +0900 Subject: [PATCH] fix: include provider-models cache for Hephaestus availability --- src/agents/builtin-agents.ts | 16 +++++++++++++--- src/agents/utils.test.ts | 30 +++++++++++++++++++++++++++++- 2 files changed, 42 insertions(+), 4 deletions(-) diff --git a/src/agents/builtin-agents.ts b/src/agents/builtin-agents.ts index dd91e019..2a4d651b 100644 --- a/src/agents/builtin-agents.ts +++ b/src/agents/builtin-agents.ts @@ -13,7 +13,11 @@ import { createAtlasAgent, atlasPromptMetadata } from "./atlas" import { createMomusAgent, momusPromptMetadata } from "./momus" import { createHephaestusAgent } from "./hephaestus" import type { AvailableCategory } from "./dynamic-agent-prompt-builder" -import { fetchAvailableModels, readConnectedProvidersCache } from "../shared" +import { + fetchAvailableModels, + readConnectedProvidersCache, + readProviderModelsCache, +} from "../shared" import { CATEGORY_DESCRIPTIONS } from "../tools/delegate-task/constants" import { mergeCategories } from "../shared/merge-categories" import { buildAvailableSkills } from "./builtin-agents/available-skills" @@ -68,14 +72,20 @@ export async function createBuiltinAgents( useTaskSystem = false ): Promise> { const connectedProviders = readConnectedProvidersCache() + const providerModelsConnected = connectedProviders + ? (readProviderModelsCache()?.connected ?? []) + : [] + const mergedConnectedProviders = Array.from( + new Set([...(connectedProviders ?? []), ...providerModelsConnected]) + ) // IMPORTANT: Do NOT call OpenCode client APIs during plugin initialization. // This function is called from config handler, and calling client API causes deadlock. // See: https://github.com/code-yeongyu/oh-my-opencode/issues/1301 const availableModels = await fetchAvailableModels(undefined, { - connectedProviders: connectedProviders ?? undefined, + connectedProviders: mergedConnectedProviders.length > 0 ? mergedConnectedProviders : undefined, }) const isFirstRunNoCache = - availableModels.size === 0 && (!connectedProviders || connectedProviders.length === 0) + availableModels.size === 0 && mergedConnectedProviders.length === 0 const result: Record = {} diff --git a/src/agents/utils.test.ts b/src/agents/utils.test.ts index 6dc84368..a0c16925 100644 --- a/src/agents/utils.test.ts +++ b/src/agents/utils.test.ts @@ -428,7 +428,7 @@ describe("createBuiltinAgents with model overrides", () => { ) // #then - const matches = agents.sisyphus.prompt.match(/Custom agent: researcher/gi) ?? [] + const matches = (agents.sisyphus?.prompt ?? "").match(/Custom agent: researcher/gi) ?? [] expect(matches.length).toBe(1) } finally { fetchSpy.mockRestore() @@ -525,6 +525,34 @@ describe("createBuiltinAgents without systemDefaultModel", () => { }) describe("createBuiltinAgents with requiresProvider gating (hephaestus)", () => { + test("hephaestus is created when provider-models cache connected list includes required provider", async () => { + // #given + const connectedCacheSpy = spyOn(connectedProvidersCache, "readConnectedProvidersCache").mockReturnValue(["anthropic"]) + const providerModelsSpy = spyOn(connectedProvidersCache, "readProviderModelsCache").mockReturnValue({ + connected: ["openai"], + models: {}, + updatedAt: new Date().toISOString(), + }) + const fetchSpy = spyOn(shared, "fetchAvailableModels").mockImplementation(async (_, options) => { + const providers = options?.connectedProviders ?? [] + return providers.includes("openai") + ? new Set(["openai/gpt-5.3-codex"]) + : new Set(["anthropic/claude-opus-4-6"]) + }) + + try { + // #when + const agents = await createBuiltinAgents([], {}, undefined, TEST_DEFAULT_MODEL, undefined, undefined, [], {}) + + // #then + expect(agents.hephaestus).toBeDefined() + } finally { + connectedCacheSpy.mockRestore() + providerModelsSpy.mockRestore() + fetchSpy.mockRestore() + } + }) + test("hephaestus is not created when no required provider is connected", async () => { // #given - only anthropic models available, not in hephaestus requiresProvider const fetchSpy = spyOn(shared, "fetchAvailableModels").mockResolvedValue(