From 079ad3427d621d6f7cc87fc274f13c4dc7881911 Mon Sep 17 00:00:00 2001 From: Project516 <138796702+Project516@users.noreply.github.com> Date: Mon, 9 Nov 2026 18:30:25 -0600 Subject: [PATCH] fix(server): OpenCode keeps its cached models when a cold probe lists none --- .../src/provider/ProviderRegistry.test.ts | 17 ++++++++++++++++- apps/server/src/provider/ProviderRegistry.ts | 6 ++++-- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/apps/server/src/provider/ProviderRegistry.test.ts b/apps/server/src/provider/ProviderRegistry.test.ts index b5501943ca04f..478192cde40da 100644 --- a/apps/server/src/provider/ProviderRegistry.test.ts +++ b/apps/server/src/provider/ProviderRegistry.test.ts @@ -1050,7 +1050,7 @@ it.layer( ); }); - it("retains stale OpenCode models when a refresh fails", () => { + it("retains stale OpenCode models when a refresh fails or lists none", () => { const previousProvider = { instanceId: ProviderInstanceId.make("opencode"), driver: ProviderDriverKind.make("opencode"), @@ -1100,6 +1100,21 @@ it.layer( ProviderRegistry.mergeProviderSnapshot(previousProvider, refreshedProvider).skills, previousProvider.skills, ); + + const emptyCatalogProvider = { + ...refreshedProvider, + status: "warning", + message: "OpenCode 2 is running, but it did not list any models yet.", + slashCommands: [], + skills: [], + } satisfies ServerProvider; + const mergedEmptyCatalog = ProviderRegistry.mergeProviderSnapshot( + previousProvider, + emptyCatalogProvider, + ); + assert.deepStrictEqual(mergedEmptyCatalog.models, [...previousProvider.models]); + assert.deepStrictEqual(mergedEmptyCatalog.slashCommands, previousProvider.slashCommands); + assert.deepStrictEqual(mergedEmptyCatalog.skills, previousProvider.skills); }); describe("Pi model inventories", () => { diff --git a/apps/server/src/provider/ProviderRegistry.ts b/apps/server/src/provider/ProviderRegistry.ts index 18456b2397e7e..b711e1cc7c47c 100644 --- a/apps/server/src/provider/ProviderRegistry.ts +++ b/apps/server/src/provider/ProviderRegistry.ts @@ -212,7 +212,8 @@ const shouldRetainMissingProviderModels = (provider: ServerProvider): boolean => const isAntigravity = provider.driver === ProviderDriverKind.make("antigravity"); const isCodex = provider.driver === ProviderDriverKind.make("codex"); - if (!isAntigravity && !isCodex && provider.driver !== ProviderDriverKind.make("opencode")) { + const isOpenCode = provider.driver === ProviderDriverKind.make("opencode"); + if (!isAntigravity && !isCodex && !isOpenCode) { return true; } @@ -228,9 +229,10 @@ const shouldRetainMissingProviderModels = (provider: ServerProvider): boolean => isAntigravity && provider.status === "warning" && provider.auth.status === "unknown"; const isPendingInitialProbe = provider.enabled && !provider.installed && provider.status === "warning"; + // A cold OpenCode server can finish its probe as a warning before it lists any models. const isIncompleteInstalledProbe = provider.installed && - (provider.status === "error" || (isCodex && provider.status === "warning")); + (provider.status === "error" || ((isCodex || isOpenCode) && provider.status === "warning")); return isPendingAntigravityAuthentication || isPendingInitialProbe || isIncompleteInstalledProbe; };