diff --git a/packages/adapters/opencode-local/src/server/models.test.ts b/packages/adapters/opencode-local/src/server/models.test.ts index 153096d29a..0de89de8dd 100644 --- a/packages/adapters/opencode-local/src/server/models.test.ts +++ b/packages/adapters/opencode-local/src/server/models.test.ts @@ -148,12 +148,21 @@ describe("openCode models", () => { pid: 1, startedAt: new Date().toISOString(), }) + .mockResolvedValueOnce({ + exitCode: 0, + signal: null, + timedOut: false, + stdout: "Models cache refreshed\n", + stderr: "", + pid: 1, + startedAt: new Date().toISOString(), + }) .mockResolvedValueOnce({ exitCode: 0, signal: null, timedOut: false, stdout: - "openrouter/example/stale-model\nopenrouter/deepseek/deepseek-v4-flash-0731\n", + "openrouter/example/current-model\nopenrouter/deepseek/deepseek-v4-flash-0731\n", stderr: "", pid: 1, startedAt: new Date().toISOString(), @@ -167,9 +176,10 @@ describe("openCode models", () => { id: "openrouter/deepseek/deepseek-v4-flash-0731", label: "openrouter/deepseek/deepseek-v4-flash-0731", }); - expect(spy).toHaveBeenCalledTimes(2); + expect(spy).toHaveBeenCalledTimes(3); expect(spy.mock.calls[0]?.[2]).toEqual(["models"]); expect(spy.mock.calls[1]?.[2]).toEqual(["models", "--refresh"]); + expect(spy.mock.calls[2]?.[2]).toEqual(["models"]); }); it("still rejects when a refreshed non-empty catalog omits the configured model", async () => { @@ -184,6 +194,15 @@ describe("openCode models", () => { pid: 1, startedAt: new Date().toISOString(), }) + .mockResolvedValueOnce({ + exitCode: 0, + signal: null, + timedOut: false, + stdout: "Models cache refreshed\n", + stderr: "", + pid: 1, + startedAt: new Date().toISOString(), + }) .mockResolvedValueOnce({ exitCode: 0, signal: null, @@ -201,11 +220,12 @@ describe("openCode models", () => { ).rejects.toThrow( "Configured OpenCode model is unavailable: openrouter/deepseek/deepseek-v4-flash-0731", ); - expect(spy).toHaveBeenCalledTimes(2); + expect(spy).toHaveBeenCalledTimes(3); expect(spy.mock.calls[1]?.[2]).toEqual(["models", "--refresh"]); + expect(spy.mock.calls[2]?.[2]).toEqual(["models"]); }); - it("still rejects from the original catalog when refresh returns no models", async () => { + it("still rejects from the original catalog when post-refresh enumeration returns no models", async () => { const spy = vi .spyOn(serverUtils, "runChildProcess") .mockResolvedValueOnce({ @@ -217,6 +237,15 @@ describe("openCode models", () => { pid: 1, startedAt: new Date().toISOString(), }) + .mockResolvedValueOnce({ + exitCode: 0, + signal: null, + timedOut: false, + stdout: "Models cache refreshed\n", + stderr: "", + pid: 1, + startedAt: new Date().toISOString(), + }) .mockResolvedValueOnce({ exitCode: 0, signal: null, @@ -232,8 +261,9 @@ describe("openCode models", () => { model: "openrouter/deepseek/deepseek-v4-flash-0731", }), ).rejects.toThrow("Available models: openrouter/example/stale-model"); - expect(spy).toHaveBeenCalledTimes(2); + expect(spy).toHaveBeenCalledTimes(3); expect(spy.mock.calls[1]?.[2]).toEqual(["models", "--refresh"]); + expect(spy.mock.calls[2]?.[2]).toEqual(["models"]); }); it("still rejects from the original catalog when refresh fails", async () => { diff --git a/packages/adapters/opencode-local/src/server/models.ts b/packages/adapters/opencode-local/src/server/models.ts index 92199830f6..3e576eaa20 100644 --- a/packages/adapters/opencode-local/src/server/models.ts +++ b/packages/adapters/opencode-local/src/server/models.ts @@ -238,12 +238,17 @@ async function refreshOpenCodeModelsCached(input: { const command = resolveOpenCodeCommand(input.command); const cwd = asString(input.cwd, process.cwd()); const env = normalizeEnv(input.env); - const models = await discoverOpenCodeModels({ + // OpenCode 1.18.17 uses `models --refresh` only to update its on-disk + // models.dev cache. Its stdout is a confirmation message, not the refreshed + // catalog, so enumerate once more after the refresh under the exact same + // command/cwd/env before deciding whether the configured model exists. + await discoverOpenCodeModels({ command, cwd, env, refresh: true, }); + const models = await discoverOpenCodeModels({ command, cwd, env }); if (models.length > 0) { discoveryCache.set(discoveryCacheKey(command, cwd, env), { expiresAt: Date.now() + MODELS_CACHE_TTL_MS,