From 3a83babcdcedc130e47a086f67b5db13c70c0d0e Mon Sep 17 00:00:00 2001 From: ump45nose <52391318+ump45nose@users.noreply.github.com> Date: Fri, 16 Oct 2026 03:20:29 +0800 Subject: [PATCH] fix(opencode): read v2's native default variant as the Default option (#528) --- packages/adapters/opencode/src/v2/state.ts | 15 +++++++- packages/adapters/opencode/test/v2.test.ts | 40 +++++++++++++++++++++- 2 files changed, 53 insertions(+), 2 deletions(-) diff --git a/packages/adapters/opencode/src/v2/state.ts b/packages/adapters/opencode/src/v2/state.ts index ba8b80666..2372d600a 100644 --- a/packages/adapters/opencode/src/v2/state.ts +++ b/packages/adapters/opencode/src/v2/state.ts @@ -140,12 +140,25 @@ export function v2State( availableThinkingOptions: catalog.thinkingOptions.filter((v) => entry.supportedThinkingOptionIds?.includes(v.id), ), - effectiveThinkingOptionId: encodeOpenCodeVariant(session.model?.variant), + effectiveThinkingOptionId: effectiveVariant(session.model?.variant, entry), } : {}), effectivePermissionModeId: decodeMode(mode?.effect), }; } +/** + * OpenCode v2 reads its default variant back as the string "default"; it is + * the catalog's Default option unless the model names a variant "default". + */ +function effectiveVariant( + variant: string | undefined, + entry: HarnessModelCatalog["models"][number], +) { + const id = encodeOpenCodeVariant(variant); + return variant === "default" && !entry.supportedThinkingOptionIds?.includes(id) + ? encodeOpenCodeVariant(undefined) + : id; +} function decodeMode(effect?: string) { return effect === "allow" || effect === "ask" ? harnessPermissionModeIdSchema.parse(effect) diff --git a/packages/adapters/opencode/test/v2.test.ts b/packages/adapters/opencode/test/v2.test.ts index c9eb72cf5..63813a761 100644 --- a/packages/adapters/opencode/test/v2.test.ts +++ b/packages/adapters/opencode/test/v2.test.ts @@ -1,5 +1,6 @@ import { afterEach, describe, expect, it, vi } from "vitest"; import type { + ModelInfo, OpenCodeClient, OpenCodeEvent, SessionInfo, @@ -14,9 +15,10 @@ import { } from "@codexhost/harness-adapter"; import { CodexTurnProjector } from "@codexhost/protocol-core"; import { harnessModelCatalogSchema, hostTurnIdSchema } from "@codexhost/shared-contracts"; +import { encodeOpenCodeVariant } from "../src/model-catalog.js"; import { openCodeMajor } from "../src/version.js"; import { assistantItems, projectHistory, readMessages } from "../src/v2/history.js"; -import { v2Locator, v2Permissions, v2Ref } from "../src/v2/state.js"; +import { v2Catalog, v2Locator, v2Permissions, v2Ref, v2State } from "../src/v2/state.js"; import { V2Session } from "../src/v2/session.js"; import { v2UsageRequest } from "../src/v2/usage.js"; import { formInteraction, replyInteraction } from "../src/v2/interactions.js"; @@ -160,6 +162,42 @@ describe("OpenCode protocol versions", () => { }); }); +describe("OpenCode v2 Thinking state", () => { + const model = (id: string, variants: string[]) => + ({ + id, + providerID: "test", + name: id, + enabled: true, + variants: variants.map((variant) => ({ id: variant })), + }) as unknown as ModelInfo; + const stateFor = (variants: string[], variant: string | undefined) => + v2State( + { ...info, model: { providerID: "test", id: "model", ...(variant ? { variant } : {}) } }, + v2Catalog([model("model", variants)], null), + "default", + ); + + it("reads the native default variant as the catalog's Default option", () => { + const state = stateFor(["high", "low"], "default"); + expect(state.effectiveThinkingOptionId).toBe(encodeOpenCodeVariant(undefined)); + expect(state.availableThinkingOptions?.map((option) => option.id)).toContain( + state.effectiveThinkingOptionId, + ); + }); + + it("keeps a variant the model itself names default", () => { + const state = stateFor(["default", "high"], "default"); + expect(state.effectiveThinkingOptionId).toBe(encodeOpenCodeVariant("default")); + }); + + it("keeps an explicit variant's own option", () => { + expect(stateFor(["high", "low"], "high").effectiveThinkingOptionId).toBe( + encodeOpenCodeVariant("high"), + ); + }); +}); + describe("OpenCode v2 native history", () => { it("groups steered prompts by durable idle and never guesses a terminal", () => { const snapshot = projectHistory(