diff --git a/open-sse/config/codexInstructions.ts b/open-sse/config/codexInstructions.ts index 7a5b4a5aa2..9a736211e1 100644 --- a/open-sse/config/codexInstructions.ts +++ b/open-sse/config/codexInstructions.ts @@ -1,6 +1,8 @@ // Default instructions for Codex models // Source: CLIProxyAPI internal/misc/codex_instructions/ +export const CODEX_CHAT_DEFAULT_INSTRUCTIONS = "You are a ChatGPT agent."; + export const CODEX_DEFAULT_INSTRUCTIONS = `You are Codex, based on GPT-5. You are running as a coding agent in the Codex CLI on a user's computer. ## General diff --git a/open-sse/executors/codex.ts b/open-sse/executors/codex.ts index 7930888432..720930e4d1 100644 --- a/open-sse/executors/codex.ts +++ b/open-sse/executors/codex.ts @@ -5,7 +5,10 @@ import { setUserAgentHeader, type ExecuteInput, } from "./base.ts"; -import { CODEX_DEFAULT_INSTRUCTIONS } from "../config/codexInstructions.ts"; +import { + CODEX_CHAT_DEFAULT_INSTRUCTIONS, + CODEX_DEFAULT_INSTRUCTIONS, +} from "../config/codexInstructions.ts"; import { PROVIDERS } from "../config/constants.ts"; import { getCodexClientVersion, getCodexUserAgent } from "../config/codexClient.ts"; import { getAccessToken } from "../services/tokenRefresh.ts"; @@ -1037,9 +1040,9 @@ export class CodexExecutor extends BaseExecutor { body.instructions = "Follow the developer instructions in the conversation."; } } else { - // Translated: use CODEX_DEFAULT_INSTRUCTIONS as fallback when no system - // prompt was provided by the client, BUT only if tools are requested. - // Injecting tool instructions on bare requests causes Harmony leaks (#1686). + // Translated: keep the full Codex tool instructions only for tool-capable + // requests. Bare chat requests still need a neutral instructions value + // because the Codex Responses backend rejects requests without it. const hasTools = Array.isArray(body.tools) && body.tools.length > 0; if ( !body.instructions || @@ -1048,7 +1051,7 @@ export class CodexExecutor extends BaseExecutor { if (hasTools) { body.instructions = CODEX_DEFAULT_INSTRUCTIONS; } else { - delete body.instructions; + body.instructions = CODEX_CHAT_DEFAULT_INSTRUCTIONS; } } } diff --git a/tests/unit/executor-codex.test.ts b/tests/unit/executor-codex.test.ts index 68b5ed6997..05c67da79b 100644 --- a/tests/unit/executor-codex.test.ts +++ b/tests/unit/executor-codex.test.ts @@ -17,6 +17,7 @@ import { setThinkingBudgetConfig, ThinkingMode, } from "../../open-sse/services/thinkingBudget.ts"; +import { CODEX_CHAT_DEFAULT_INSTRUCTIONS } from "../../open-sse/config/codexInstructions.ts"; test.afterEach(() => { setThinkingBudgetConfig(DEFAULT_THINKING_CONFIG); @@ -200,6 +201,37 @@ test("CodexExecutor.transformRequest injects default instructions, clamps reason assert.equal(result.stream_options, undefined); }); +test("CodexExecutor.transformRequest sends neutral instructions for bare chat requests", () => { + const executor = new CodexExecutor(); + const body = { + model: "gpt-5.5-medium", + input: [ + { + type: "message", + role: "user", + content: [ + { + type: "input_text", + text: "Calculate 79530+41475, and reply with the result only.", + }, + ], + }, + ], + instructions: "", + stream: false, + }; + + const result = executor.transformRequest("gpt-5.5-medium", body, false, { + requestEndpointPath: "/responses", + }); + + assert.equal(result.instructions, CODEX_CHAT_DEFAULT_INSTRUCTIONS); + assert.equal(result.stream, true); + assert.equal(result.model, "gpt-5.5"); + assert.equal(result.input.length, 1); + assert.equal(result.tools, undefined); +}); + test("CodexExecutor.transformRequest preserves compact requests and native passthrough semantics", () => { const executor = new CodexExecutor(); const body = {