From 603ae2ffa69d6e8a7c0df979c3c880f61c719381 Mon Sep 17 00:00:00 2001 From: Ryan Loney Date: Sun, 10 May 2026 14:17:38 -0700 Subject: [PATCH 1/5] fix(opencode): replay Cerebras reasoning in assistant content --- packages/opencode/src/provider/transform.ts | 21 +++ .../opencode/test/provider/transform.test.ts | 124 ++++++++++++++++++ 2 files changed, 145 insertions(+) diff --git a/packages/opencode/src/provider/transform.ts b/packages/opencode/src/provider/transform.ts index bd778dacc53e..3502f20b6534 100644 --- a/packages/opencode/src/provider/transform.ts +++ b/packages/opencode/src/provider/transform.ts @@ -284,6 +284,27 @@ function normalizeMessages( return result } + if (model.api.npm === "@ai-sdk/cerebras") { + // @ai-sdk/openai-compatible replays reasoning parts as assistant.reasoning_content, + // but Cerebras expects prior reasoning to be folded back into assistant.content. + msgs = msgs.map((msg) => { + if (msg.role !== "assistant" || !Array.isArray(msg.content)) return msg + return { + ...msg, + content: msg.content.flatMap((part) => { + if (part.type !== "reasoning") return [part] + if (part.text.length === 0) return [] + return [ + { + type: "text" as const, + text: model.api.id.toLowerCase().includes("gpt-oss") ? part.text : `${part.text}`, + }, + ] + }), + } + }) + } + // Deepseek requires all assistant messages to have reasoning on them if (model.api.id.toLowerCase().includes("deepseek")) { msgs = msgs.map((msg) => { diff --git a/packages/opencode/test/provider/transform.test.ts b/packages/opencode/test/provider/transform.test.ts index df21922b097a..758636c7efa4 100644 --- a/packages/opencode/test/provider/transform.test.ts +++ b/packages/opencode/test/provider/transform.test.ts @@ -1123,6 +1123,130 @@ describe("ProviderTransform.message - DeepSeek reasoning content", () => { }) }) +describe("ProviderTransform.message - Cerebras reasoning replay", () => { + test("Cerebras GLM rewrites reasoning blocks into text", () => { + const msgs = [ + { + role: "assistant", + content: [ + { type: "reasoning", text: "Let me think about this..." }, + { + type: "tool-call", + toolCallId: "test", + toolName: "bash", + input: { command: "echo hello" }, + }, + { type: "text", text: "Done" }, + ], + }, + ] as any[] + + const result = ProviderTransform.message( + msgs, + { + id: ModelID.make("cerebras/zai-glm-4.7"), + providerID: ProviderID.make("cerebras"), + api: { + id: "zai-glm-4.7", + url: "https://api.cerebras.ai/v1", + npm: "@ai-sdk/cerebras", + }, + name: "Z.AI GLM-4.7", + capabilities: { + temperature: true, + reasoning: true, + attachment: false, + toolcall: true, + input: { text: true, audio: false, image: false, video: false, pdf: false }, + output: { text: true, audio: false, image: false, video: false, pdf: false }, + interleaved: false, + }, + cost: { + input: 0.001, + output: 0.002, + cache: { read: 0.0001, write: 0.0002 }, + }, + limit: { + context: 128000, + output: 4096, + }, + status: "active", + options: {}, + headers: {}, + release_date: "2026-01-10", + }, + {}, + ) + + expect(result[0].content).toEqual([ + { type: "text", text: "Let me think about this..." }, + { + type: "tool-call", + toolCallId: "test", + toolName: "bash", + input: { command: "echo hello" }, + }, + { type: "text", text: "Done" }, + ]) + expect(result[0].providerOptions?.openaiCompatible?.reasoning_content).toBeUndefined() + }) + + test("Cerebras gpt-oss replays reasoning as plain assistant text", () => { + const msgs = [ + { + role: "assistant", + content: [ + { type: "reasoning", text: "First, I should think it through." }, + { type: "text", text: "Answer" }, + ], + }, + ] as any[] + + const result = ProviderTransform.message( + msgs, + { + id: ModelID.make("cerebras/gpt-oss-120b"), + providerID: ProviderID.make("cerebras"), + api: { + id: "gpt-oss-120b", + url: "https://api.cerebras.ai/v1", + npm: "@ai-sdk/cerebras", + }, + name: "GPT OSS 120B", + capabilities: { + temperature: true, + reasoning: true, + attachment: false, + toolcall: true, + input: { text: true, audio: false, image: false, video: false, pdf: false }, + output: { text: true, audio: false, image: false, video: false, pdf: false }, + interleaved: false, + }, + cost: { + input: 0.001, + output: 0.002, + cache: { read: 0.0001, write: 0.0002 }, + }, + limit: { + context: 128000, + output: 4096, + }, + status: "active", + options: {}, + headers: {}, + release_date: "2025-08-05", + }, + {}, + ) + + expect(result[0].content).toEqual([ + { type: "text", text: "First, I should think it through." }, + { type: "text", text: "Answer" }, + ]) + expect(result[0].providerOptions?.openaiCompatible?.reasoning_content).toBeUndefined() + }) +}) + describe("ProviderTransform.message - surrogate sanitization", () => { const model = { id: "test/test-model", From ee2fa6b64661c69f232dc9006d834f94e96e881e Mon Sep 17 00:00:00 2001 From: Ryan Loney Date: Mon, 11 May 2026 16:51:06 -0700 Subject: [PATCH 2/5] test(opencode): cover kimi and glm reasoning replay --- .../opencode/test/provider/transform.test.ts | 81 +++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/packages/opencode/test/provider/transform.test.ts b/packages/opencode/test/provider/transform.test.ts index 758636c7efa4..10b2ba82cf0f 100644 --- a/packages/opencode/test/provider/transform.test.ts +++ b/packages/opencode/test/provider/transform.test.ts @@ -1247,6 +1247,87 @@ describe("ProviderTransform.message - Cerebras reasoning replay", () => { }) }) +describe("ProviderTransform.message - OpenAI-compatible reasoning replay", () => { + test.each([ + { + id: "opencode/kimi-k2.6", + apiId: "kimi-k2.6", + name: "Kimi K2.6", + }, + { + id: "opencode/glm-4.7", + apiId: "glm-4.7", + name: "GLM-4.7", + }, + ])("$name with tool calls includes reasoning_content in providerOptions", ({ id, apiId }) => { + const msgs = [ + { + role: "assistant", + content: [ + { type: "reasoning", text: "Let me think about this..." }, + { + type: "tool-call", + toolCallId: "test", + toolName: "bash", + input: { command: "echo hello" }, + }, + { type: "text", text: "Done" }, + ], + }, + ] as any[] + + const result = ProviderTransform.message( + msgs, + { + id: ModelID.make(id), + providerID: ProviderID.make("opencode"), + api: { + id: apiId, + url: "https://opencode.ai/zen/v1", + npm: "@ai-sdk/openai-compatible", + }, + name: apiId, + capabilities: { + temperature: true, + reasoning: true, + attachment: false, + toolcall: true, + input: { text: true, audio: false, image: false, video: false, pdf: false }, + output: { text: true, audio: false, image: false, video: false, pdf: false }, + interleaved: { + field: "reasoning_content", + }, + }, + cost: { + input: 0.001, + output: 0.002, + cache: { read: 0.0001, write: 0.0002 }, + }, + limit: { + context: 128000, + output: 4096, + }, + status: "active", + options: {}, + headers: {}, + release_date: "2026-04-21", + }, + {}, + ) + + expect(result[0].content).toEqual([ + { + type: "tool-call", + toolCallId: "test", + toolName: "bash", + input: { command: "echo hello" }, + }, + { type: "text", text: "Done" }, + ]) + expect(result[0].providerOptions?.openaiCompatible?.reasoning_content).toBe("Let me think about this...") + }) +}) + describe("ProviderTransform.message - surrogate sanitization", () => { const model = { id: "test/test-model", From da4f7e820d7ea9a45661731c64fdd5018c502949 Mon Sep 17 00:00:00 2001 From: Ryan Loney Date: Tue, 12 May 2026 06:48:24 -0700 Subject: [PATCH 3/5] fix(provider): default reasoning replay for kimi and glm --- packages/opencode/src/provider/provider.ts | 32 ++++++++++++---- .../opencode/test/provider/provider.test.ts | 38 ++++++++++++++++++- 2 files changed, 62 insertions(+), 8 deletions(-) diff --git a/packages/opencode/src/provider/provider.ts b/packages/opencode/src/provider/provider.ts index c27b69b6a208..114ec4fbf1f2 100644 --- a/packages/opencode/src/provider/provider.ts +++ b/packages/opencode/src/provider/provider.ts @@ -38,6 +38,22 @@ function shouldUseCopilotResponsesApi(modelID: string): boolean { return Number(match[1]) >= 5 && !modelID.startsWith("gpt-5-mini") } +function defaultOpenAICompatibleInterleaved( + apiNpm: string, + apiID: string, + reasoning: boolean, +): false | { field: "reasoning_content" } { + if (apiNpm !== "@ai-sdk/openai-compatible" || !reasoning) return false + + const id = apiID.toLowerCase() + const usesReasoningContent = + id.includes("deepseek") || + id.includes("kimi") || + /(^|[/:])glm-(4\.7|5(?:\.1)?|5v)(?:[^a-z0-9]|$)/.test(id) + + return usesReasoningContent ? { field: "reasoning_content" } : false +} + function wrapSSE(res: Response, ms: number, ctl: AbortController) { if (typeof ms !== "number" || ms <= 0) return res if (!res.body) return res @@ -1203,6 +1219,13 @@ const layer: Layer.Layer< if (model.id && model.id !== modelID) return modelID return existingModel?.name ?? modelID }) + const reasoning = model.reasoning ?? existingModel?.capabilities.reasoning ?? false + const defaultInterleaved = defaultOpenAICompatibleInterleaved(apiNpm, apiID, reasoning) + const interleaved = + model.interleaved ?? + (existingModel?.capabilities.interleaved && existingModel.capabilities.interleaved !== false + ? existingModel.capabilities.interleaved + : defaultInterleaved) const parsedModel: Model = { id: ModelID.make(modelID), api: { @@ -1215,7 +1238,7 @@ const layer: Layer.Layer< providerID: ProviderID.make(providerID), capabilities: { temperature: model.temperature ?? existingModel?.capabilities.temperature ?? false, - reasoning: model.reasoning ?? existingModel?.capabilities.reasoning ?? false, + reasoning, attachment: model.attachment ?? existingModel?.capabilities.attachment ?? false, toolcall: model.tool_call ?? existingModel?.capabilities.toolcall ?? true, input: { @@ -1235,12 +1258,7 @@ const layer: Layer.Layer< model.modalities?.output?.includes("video") ?? existingModel?.capabilities.output.video ?? false, pdf: model.modalities?.output?.includes("pdf") ?? existingModel?.capabilities.output.pdf ?? false, }, - interleaved: - model.interleaved ?? - existingModel?.capabilities.interleaved ?? - (!existingModel && apiNpm === "@ai-sdk/openai-compatible" && apiID.includes("deepseek") - ? { field: "reasoning_content" } - : false), + interleaved, }, cost: { input: model?.cost?.input ?? existingModel?.cost?.input ?? 0, diff --git a/packages/opencode/test/provider/provider.test.ts b/packages/opencode/test/provider/provider.test.ts index cdb9d2057245..bdfa34285a22 100644 --- a/packages/opencode/test/provider/provider.test.ts +++ b/packages/opencode/test/provider/provider.test.ts @@ -301,7 +301,7 @@ test("custom provider with npm package", async () => { }) }) -test("custom DeepSeek openai-compatible model defaults interleaved reasoning field", async () => { +test("custom OpenAI-compatible reasoning_content models default interleaved reasoning field", async () => { await using tmp = await tmpdir({ init: async (dir) => { await Bun.write( @@ -316,14 +316,43 @@ test("custom DeepSeek openai-compatible model defaults interleaved reasoning fie models: { "deepseek-r1": { name: "DeepSeek R1", + reasoning: true, }, "deepseek-details": { name: "DeepSeek Details", + reasoning: true, interleaved: { field: "reasoning_details" }, }, + "kimi-k2.5": { + name: "Kimi K2.5", + reasoning: true, + }, + "kimi-k2.6": { + name: "Kimi K2.6", + reasoning: true, + }, + "kimi-k2-thinking": { + name: "Kimi K2 Thinking", + reasoning: true, + }, + "glm-5": { + name: "GLM 5", + reasoning: true, + }, + "glm-5.1": { + name: "GLM 5.1", + reasoning: true, + }, + "glm-5v-turbo": { + name: "GLM 5V Turbo", + reasoning: true, + }, "custom-model": { name: "Custom Model", }, + "kimi-k2-turbo-preview": { + name: "Kimi K2 Turbo Preview", + }, }, options: { apiKey: "custom-key", @@ -354,7 +383,14 @@ test("custom DeepSeek openai-compatible model defaults interleaved reasoning fie const provider = providers[ProviderID.make("custom-provider")] expect(provider.models["deepseek-r1"].capabilities.interleaved).toEqual({ field: "reasoning_content" }) expect(provider.models["deepseek-details"].capabilities.interleaved).toEqual({ field: "reasoning_details" }) + expect(provider.models["kimi-k2.5"].capabilities.interleaved).toEqual({ field: "reasoning_content" }) + expect(provider.models["kimi-k2.6"].capabilities.interleaved).toEqual({ field: "reasoning_content" }) + expect(provider.models["kimi-k2-thinking"].capabilities.interleaved).toEqual({ field: "reasoning_content" }) + expect(provider.models["glm-5"].capabilities.interleaved).toEqual({ field: "reasoning_content" }) + expect(provider.models["glm-5.1"].capabilities.interleaved).toEqual({ field: "reasoning_content" }) + expect(provider.models["glm-5v-turbo"].capabilities.interleaved).toEqual({ field: "reasoning_content" }) expect(provider.models["custom-model"].capabilities.interleaved).toBe(false) + expect(provider.models["kimi-k2-turbo-preview"].capabilities.interleaved).toBe(false) expect( providers[ProviderID.make("custom-anthropic-provider")].models["deepseek-r1"].capabilities.interleaved, ).toBe(false) From e82536eb533cf9d196fbd4fe284dcbd32a28740c Mon Sep 17 00:00:00 2001 From: nubscarson Date: Thu, 14 May 2026 19:20:42 +0000 Subject: [PATCH 4/5] fix(provider): strip Cerebras reasoning replay fields --- packages/opencode/src/provider/transform.ts | 116 +++++++++++-- packages/opencode/src/session/llm.ts | 11 +- .../opencode/test/provider/transform.test.ts | 161 +++++++++++++++++- 3 files changed, 264 insertions(+), 24 deletions(-) diff --git a/packages/opencode/src/provider/transform.ts b/packages/opencode/src/provider/transform.ts index 57192d516d75..58df27230c23 100644 --- a/packages/opencode/src/provider/transform.ts +++ b/packages/opencode/src/provider/transform.ts @@ -22,6 +22,76 @@ export function sanitizeSurrogates(content: string) { return content.replace(/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?${text}` +} + +function reasoningReplayText(value: unknown): string { + if (typeof value === "string") return value + if (Array.isArray(value)) return value.map(reasoningReplayText).join("") + if (!value || typeof value !== "object") return "" + const entry = value as Record + if (typeof entry.text === "string") return entry.text + if (typeof entry.content === "string") return entry.content + if (typeof entry.reasoning === "string") return entry.reasoning + return "" +} + +function topLevelReasoningReplayText(value: unknown): string { + if (!value || typeof value !== "object" || Array.isArray(value)) return "" + const entry = value as Record + return REASONING_REPLAY_FIELDS.map((field) => reasoningReplayText(entry[field])).join("") +} + +function stripReasoningReplayFields(value: T, model: Provider.Model): T { + if (!value || typeof value !== "object" || Array.isArray(value)) return value + let changed = false + const result: Record = { ...(value as Record) } + + for (const field of REASONING_REPLAY_FIELDS) { + if (field in result) { + delete result[field] + changed = true + } + } + + const providerOptions = result.providerOptions + if (!providerOptions) return changed ? (result as T) : value + + const optionKeys = unique(["openaiCompatible", "openai-compatible", model.providerID]) + const nextProviderOptions: Record = { ...providerOptions } + + for (const optionKey of optionKeys) { + const options = nextProviderOptions[optionKey] + if (!options || typeof options !== "object" || Array.isArray(options)) continue + + const nextOptions: Record = { ...options } + for (const field of REASONING_REPLAY_FIELDS) { + if (field in nextOptions) { + delete nextOptions[field] + changed = true + } + } + + if (Object.keys(nextOptions).length === 0) delete nextProviderOptions[optionKey] + else nextProviderOptions[optionKey] = nextOptions + } + + if (!changed) return value + if (Object.keys(nextProviderOptions).length === 0) delete result.providerOptions + else result.providerOptions = nextProviderOptions + return result as T +} + // Maps npm package to the key the AI SDK expects for providerOptions function sdkKey(npm: string): string | undefined { switch (npm) { @@ -283,24 +353,35 @@ function normalizeMessages( return result } - if (model.api.npm === "@ai-sdk/cerebras") { - // @ai-sdk/openai-compatible replays reasoning parts as assistant.reasoning_content, - // but Cerebras expects prior reasoning to be folded back into assistant.content. + if (isCerebrasCompatibleEndpoint(model)) { + // Cerebras-compatible endpoints reject OpenAI-compatible reasoning replay + // fields on prior assistant messages. Fold useful reasoning back into + // content and strip replay providerOptions before the SDK serializes them. msgs = msgs.map((msg) => { - if (msg.role !== "assistant" || !Array.isArray(msg.content)) return msg - return { - ...msg, - content: msg.content.flatMap((part) => { - if (part.type !== "reasoning") return [part] - if (part.text.length === 0) return [] - return [ - { - type: "text" as const, - text: model.api.id.toLowerCase().includes("gpt-oss") ? part.text : `${part.text}`, - }, - ] - }), + if (msg.role !== "assistant") return msg + const replayText = topLevelReasoningReplayText(msg) + const stripped = stripReasoningReplayFields(msg, model) + if (!Array.isArray(stripped.content)) { + if (!replayText) return stripped + const folded = cerebrasReasoningText(replayText, model) + const existing = typeof stripped.content === "string" ? stripped.content : "" + return { + ...stripped, + content: existing.trim() ? `${folded}\n${existing}` : folded, + } as ModelMessage + } + const content = stripped.content.flatMap((part: any) => { + if (part.type !== "reasoning") return [stripReasoningReplayFields(part, model)] + if (part.text.length === 0) return [] + return [{ type: "text" as const, text: cerebrasReasoningText(part.text, model) }] + }) + if (replayText) { + content.unshift({ type: "text" as const, text: cerebrasReasoningText(replayText, model) }) } + return { + ...stripped, + content, + } as ModelMessage }) } @@ -325,7 +406,8 @@ function normalizeMessages( if ( typeof model.capabilities.interleaved === "object" && model.capabilities.interleaved.field && - model.api.npm !== "@openrouter/ai-sdk-provider" + model.api.npm !== "@openrouter/ai-sdk-provider" && + !isCerebrasCompatibleEndpoint(model) ) { const field = model.capabilities.interleaved.field return msgs.map((msg) => { diff --git a/packages/opencode/src/session/llm.ts b/packages/opencode/src/session/llm.ts index 116254a81e75..ce9976465b97 100644 --- a/packages/opencode/src/session/llm.ts +++ b/packages/opencode/src/session/llm.ts @@ -395,11 +395,14 @@ const live: Layer.Layer< { specificationVersion: "v3" as const, async transformParams(args) { - if (args.type === "stream") { - // @ts-expect-error - args.params.prompt = ProviderTransform.message(args.params.prompt, input.model, options) + const params = args.params as any + if (Array.isArray(params.prompt)) { + params.prompt = ProviderTransform.message(params.prompt, input.model, options) } - return args.params + if (Array.isArray(params.messages)) { + params.messages = ProviderTransform.message(params.messages, input.model, options) + } + return params }, }, ], diff --git a/packages/opencode/test/provider/transform.test.ts b/packages/opencode/test/provider/transform.test.ts index 070bf8d3ec92..90e03fbe8bc8 100644 --- a/packages/opencode/test/provider/transform.test.ts +++ b/packages/opencode/test/provider/transform.test.ts @@ -1264,9 +1264,24 @@ describe("ProviderTransform.message - Cerebras reasoning replay", () => { const msgs = [ { role: "assistant", + providerOptions: { + openaiCompatible: { + reasoning_content: "stale SDK replay", + keep: "safe", + }, + }, content: [ { type: "reasoning", text: "First, I should think it through." }, - { type: "text", text: "Answer" }, + { + type: "text", + text: "Answer", + providerOptions: { + openaiCompatible: { + reasoning_details: "stale part replay", + keepPart: "safe", + }, + }, + }, ], }, ] as any[] @@ -1289,7 +1304,9 @@ describe("ProviderTransform.message - Cerebras reasoning replay", () => { toolcall: true, input: { text: true, audio: false, image: false, video: false, pdf: false }, output: { text: true, audio: false, image: false, video: false, pdf: false }, - interleaved: false, + interleaved: { + field: "reasoning_content", + }, }, cost: { input: 0.001, @@ -1310,9 +1327,147 @@ describe("ProviderTransform.message - Cerebras reasoning replay", () => { expect(result[0].content).toEqual([ { type: "text", text: "First, I should think it through." }, - { type: "text", text: "Answer" }, + { + type: "text", + text: "Answer", + providerOptions: { + openaiCompatible: { + keepPart: "safe", + }, + }, + }, + ]) + expect(result[0].providerOptions?.openaiCompatible?.reasoning_content).toBeUndefined() + expect(result[0].providerOptions?.openaiCompatible?.keep).toBe("safe") + }) + + test("custom OpenAI-compatible Cerebras endpoints strip reasoning replay fields", () => { + const msgs = [ + { + role: "assistant", + providerOptions: { + openaiCompatible: { + reasoning_content: "old reasoning", + keep: "safe", + }, + }, + content: [ + { type: "reasoning", text: "Use the routed app directory." }, + { + type: "tool-call", + toolCallId: "test", + toolName: "bash", + input: { command: "pwd" }, + }, + { type: "text", text: "Continuing." }, + ], + }, + ] as any[] + + const result = ProviderTransform.message( + msgs, + { + id: ModelID.make("eliza-cerebras/gpt-oss-120b"), + providerID: ProviderID.make("eliza-cerebras"), + api: { + id: "gpt-oss-120b", + url: "https://api.cerebras.ai/v1", + npm: "@ai-sdk/openai-compatible", + }, + name: "GPT OSS 120B", + capabilities: { + temperature: true, + reasoning: true, + attachment: false, + toolcall: true, + input: { text: true, audio: false, image: false, video: false, pdf: false }, + output: { text: true, audio: false, image: false, video: false, pdf: false }, + interleaved: { + field: "reasoning_content", + }, + }, + cost: { + input: 0.001, + output: 0.002, + cache: { read: 0.0001, write: 0.0002 }, + }, + limit: { + context: 128000, + output: 4096, + }, + status: "active", + options: {}, + headers: {}, + release_date: "2025-08-05", + }, + {}, + ) + + expect(result[0].content).toEqual([ + { type: "text", text: "Use the routed app directory." }, + { + type: "tool-call", + toolCallId: "test", + toolName: "bash", + input: { command: "pwd" }, + }, + { type: "text", text: "Continuing." }, ]) expect(result[0].providerOptions?.openaiCompatible?.reasoning_content).toBeUndefined() + expect(result[0].providerOptions?.openaiCompatible?.reasoning_details).toBeUndefined() + expect(result[0].providerOptions?.openaiCompatible?.keep).toBe("safe") + }) + + test("custom OpenAI-compatible Cerebras endpoints fold serialized top-level reasoning replay", () => { + const msgs = [ + { + role: "assistant", + reasoning_content: "Create the remaining files before verifying.", + content: "", + }, + ] as any[] + + const result = ProviderTransform.message( + msgs, + { + id: ModelID.make("eliza-cerebras/gpt-oss-120b"), + providerID: ProviderID.make("eliza-cerebras"), + api: { + id: "gpt-oss-120b", + url: "https://api.cerebras.ai/v1", + npm: "@ai-sdk/openai-compatible", + }, + name: "GPT OSS 120B", + capabilities: { + temperature: true, + reasoning: true, + attachment: false, + toolcall: true, + input: { text: true, audio: false, image: false, video: false, pdf: false }, + output: { text: true, audio: false, image: false, video: false, pdf: false }, + interleaved: { + field: "reasoning_content", + }, + }, + cost: { + input: 0.001, + output: 0.002, + cache: { read: 0.0001, write: 0.0002 }, + }, + limit: { + context: 128000, + output: 4096, + }, + status: "active", + options: {}, + headers: {}, + release_date: "2025-08-05", + }, + {}, + ) + + expect(result[0].content).toBe("Create the remaining files before verifying.") + expect((result[0] as any).reasoning_content).toBeUndefined() }) }) From 0fe7cee1301acbccbadcd2dfc2f18d44e9eb720e Mon Sep 17 00:00:00 2001 From: nubscarson Date: Thu, 14 May 2026 19:22:48 +0000 Subject: [PATCH 5/5] fix(core): satisfy current typecheck --- packages/opencode/src/bus/global.ts | 11 +++++++---- packages/opencode/src/provider/provider.ts | 5 ++--- packages/opencode/src/pty/pty.node.ts | 1 - 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/packages/opencode/src/bus/global.ts b/packages/opencode/src/bus/global.ts index 3cfd453624c1..80333295c191 100644 --- a/packages/opencode/src/bus/global.ts +++ b/packages/opencode/src/bus/global.ts @@ -11,11 +11,14 @@ export type GlobalEvent = { class GlobalBusEmitter extends EventEmitter<{ event: [GlobalEvent] }> { - override emit(eventName: "event", event: GlobalEvent): boolean { - if (event.payload && typeof event.payload === "object" && !("id" in event.payload)) { - event.payload.id = event.payload.syncEvent?.id ?? Identifier.create("evt", "ascending") + override emit(eventName: string | symbol, ...args: any[]): boolean { + if (eventName === "event") { + const event = args[0] as GlobalEvent | undefined + if (event?.payload && typeof event.payload === "object" && !("id" in event.payload)) { + event.payload.id = event.payload.syncEvent?.id ?? Identifier.create("evt", "ascending") + } } - return super.emit(eventName, event) + return super.emit(eventName, ...args) } } diff --git a/packages/opencode/src/provider/provider.ts b/packages/opencode/src/provider/provider.ts index 58bb6b7473ef..0347922532c6 100644 --- a/packages/opencode/src/provider/provider.ts +++ b/packages/opencode/src/provider/provider.ts @@ -1289,11 +1289,10 @@ const layer = Layer.effect( }) const reasoning = model.reasoning ?? existingModel?.capabilities.reasoning ?? false const defaultInterleaved = defaultOpenAICompatibleInterleaved(apiNpm, apiID, reasoning) + const existingInterleaved = existingModel?.capabilities.interleaved const interleaved = model.interleaved ?? - (existingModel?.capabilities.interleaved && existingModel.capabilities.interleaved !== false - ? existingModel.capabilities.interleaved - : defaultInterleaved) + (existingInterleaved ? existingInterleaved : defaultInterleaved) const parsedModel: Model = { id: ModelID.make(modelID), api: { diff --git a/packages/opencode/src/pty/pty.node.ts b/packages/opencode/src/pty/pty.node.ts index b45c5bf50985..76f415f4cdd7 100644 --- a/packages/opencode/src/pty/pty.node.ts +++ b/packages/opencode/src/pty/pty.node.ts @@ -1,4 +1,3 @@ -/** @ts-expect-error */ import * as pty from "@lydell/node-pty" import type { Opts, Proc } from "./pty"