From 013c6046c96afebbc9fe275171a3ec09b1136f3d Mon Sep 17 00:00:00 2001 From: Zhou Rui Date: Thu, 27 Nov 2025 14:04:41 +0800 Subject: [PATCH 1/2] fix: avoid orphan function_call outputs when no tools --- lib/request/request-transformer.ts | 8 ++++++++ test/request-transformer.test.ts | 18 ++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/lib/request/request-transformer.ts b/lib/request/request-transformer.ts index 0b3b7a8..12b3a9e 100644 --- a/lib/request/request-transformer.ts +++ b/lib/request/request-transformer.ts @@ -440,6 +440,14 @@ export async function transformRequestBody( // DEFAULT MODE: Keep original behavior with tool remap message body.input = addToolRemapMessage(body.input, !!body.tools); } + + if (!body.tools && body.input) { + body.input = body.input.filter( + (item) => + item.type !== "function_call" && + item.type !== "function_call_output", + ); + } } // Configure reasoning (use normalized model family + model-specific config) diff --git a/test/request-transformer.test.ts b/test/request-transformer.test.ts index bccdbce..642d835 100644 --- a/test/request-transformer.test.ts +++ b/test/request-transformer.test.ts @@ -847,6 +847,24 @@ describe('Request Transformer Module', () => { expect(result.reasoning?.effort).toBe('medium'); }); + it('should drop function_call items when no tools present', async () => { + const body: RequestBody = { + model: 'gpt-5-codex', + input: [ + { type: 'message', role: 'user', content: 'hello' }, + { type: 'function_call', role: 'assistant', name: 'write', arguments: '{}' } as any, + { type: 'function_call_output', role: 'assistant', call_id: 'call_1', output: '{}' } as any, + ], + }; + + const result = await transformRequestBody(body, codexInstructions); + + expect(result.tools).toBeUndefined(); + expect(result.input).toHaveLength(1); + expect(result.input![0].type).toBe('message'); + expect(result.input![0].role).toBe('user'); + }); + describe('CODEX_MODE parameter', () => { it('should use bridge message when codexMode=true and tools present (default)', async () => { const body: RequestBody = { From 1f6ecccb435b70a5b1c73f5c69aff489ababd422 Mon Sep 17 00:00:00 2001 From: Zhou Rui Date: Thu, 27 Nov 2025 14:05:03 +0800 Subject: [PATCH 2/2] feat: fallback GitHub latest tag resolution without api.github.com --- lib/prompts/codex.ts | 42 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 36 insertions(+), 6 deletions(-) diff --git a/lib/prompts/codex.ts b/lib/prompts/codex.ts index e7c4f54..0004c42 100644 --- a/lib/prompts/codex.ts +++ b/lib/prompts/codex.ts @@ -4,9 +4,10 @@ import { dirname, join } from "node:path"; import { fileURLToPath } from "node:url"; import type { CacheMetadata, GitHubRelease } from "../types.js"; -// Codex instructions constants const GITHUB_API_RELEASES = "https://api.github.com/repos/openai/codex/releases/latest"; +const GITHUB_HTML_RELEASES = + "https://github.com/openai/codex/releases/latest"; const CACHE_DIR = join(homedir(), ".opencode", "cache"); const __filename = fileURLToPath(import.meta.url); @@ -61,11 +62,40 @@ export function getModelFamily(normalizedModel: string): ModelFamily { * @returns Release tag name (e.g., "rust-v0.43.0") */ async function getLatestReleaseTag(): Promise { - const response = await fetch(GITHUB_API_RELEASES); - if (!response.ok) - throw new Error(`Failed to fetch latest release: ${response.status}`); - const data = (await response.json()) as GitHubRelease; - return data.tag_name; + try { + const response = await fetch(GITHUB_API_RELEASES); + if (response.ok) { + const data = (await response.json()) as GitHubRelease; + if (data.tag_name) { + return data.tag_name; + } + } + } catch { + } + + const htmlResponse = await fetch(GITHUB_HTML_RELEASES); + if (!htmlResponse.ok) { + throw new Error( + `Failed to fetch latest release: ${htmlResponse.status}`, + ); + } + + const finalUrl = htmlResponse.url; + if (finalUrl) { + const parts = finalUrl.split("/tag/"); + const last = parts[parts.length - 1]; + if (last && !last.includes("/")) { + return last; + } + } + + const html = await htmlResponse.text(); + const match = html.match(/\/openai\/codex\/releases\/tag\/([^"]+)/); + if (match && match[1]) { + return match[1]; + } + + throw new Error("Failed to determine latest release tag from GitHub"); } /**