diff --git a/src/adapters/identity.ts b/src/adapters/identity.ts index b156afe8e..655b6a28b 100644 --- a/src/adapters/identity.ts +++ b/src/adapters/identity.ts @@ -14,9 +14,19 @@ * to be a specific first-party client. */ -/** The exact identity line Codex injects for every model. */ +/** Historical exact identity line Codex injected for every model. */ export const CODEX_GPT5_IDENTITY_LINE = "You are Codex, a coding agent based on GPT-5."; +/** Codex CLI 0.145.0+ wording (#622) — still GPT-5 identity, slightly different phrasing. */ +export const CODEX_GPT5_IDENTITY_LINE_AGENT = "You are Codex, an agent based on GPT-5."; + +/** + * Known Codex GPT-5 identity sentences. Narrow: only "coding agent" / "an agent" + GPT-5(.x)? + * Avoid a broad `You are Codex.*` rewrite that could touch unrelated content. + */ +const CODEX_GPT5_IDENTITY_RE = + /You are Codex, (?:a coding agent|an agent) based on GPT-5(?:\.[0-9]+)*\./g; + /** Proxy-neutral replacement: no "opencodex proxy" mention, just the GPT-5/OpenAI disclaimer. */ export const NEUTRAL_IDENTITY_LINE = "You are a coding agent. Do not claim to be GPT-5 or to be made by OpenAI."; @@ -27,7 +37,7 @@ export const NEUTRAL_IDENTITY_LINE = "You are a coding agent. Do not claim to be * the leak can't reappear in one adapter while being fixed in another. */ export function neutralizeIdentity(systemText: string): string { - return systemText.replace(CODEX_GPT5_IDENTITY_LINE, NEUTRAL_IDENTITY_LINE); + return systemText.replace(CODEX_GPT5_IDENTITY_RE, NEUTRAL_IDENTITY_LINE); } /** The catalog (static, on-disk) replacement for `base_instructions`. Same neutral wording. */ diff --git a/tests/identity-neutralize.test.ts b/tests/identity-neutralize.test.ts index e0c09867d..2ea559022 100644 --- a/tests/identity-neutralize.test.ts +++ b/tests/identity-neutralize.test.ts @@ -2,7 +2,12 @@ import { afterEach, beforeEach, describe, expect, test } from "bun:test"; import { mkdtempSync, rmSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; -import { CODEX_GPT5_IDENTITY_LINE, NEUTRAL_IDENTITY_LINE, neutralizeIdentity } from "../src/adapters/identity"; +import { + CODEX_GPT5_IDENTITY_LINE, + CODEX_GPT5_IDENTITY_LINE_AGENT, + NEUTRAL_IDENTITY_LINE, + neutralizeIdentity, +} from "../src/adapters/identity"; import { createGoogleAdapter } from "../src/adapters/google"; import { createKiroAdapter } from "../src/adapters/kiro"; import { createOpenAIChatAdapter } from "../src/adapters/openai-chat"; @@ -23,6 +28,12 @@ describe("identity neutralization — central helper", () => { expect(neutralizeIdentity(SYS)).toBe(NEUTRAL_IDENTITY_LINE); }); + test("replaces the Codex CLI 0.145 'an agent' identity variant (#622)", () => { + expect(neutralizeIdentity(CODEX_GPT5_IDENTITY_LINE_AGENT)).toBe(NEUTRAL_IDENTITY_LINE); + expect(neutralizeIdentity("You are Codex, an agent based on GPT-5.4.")).toBe(NEUTRAL_IDENTITY_LINE); + expect(neutralizeIdentity("You are Codex, an agent based on GPT-5.4.1.")).toBe(NEUTRAL_IDENTITY_LINE); + }); + test("never emits the opencodex proxy identity", () => { const out = neutralizeIdentity(`${SYS}\n\nmore context`); expect(out).not.toMatch(/opencodex proxy/i); @@ -32,6 +43,9 @@ describe("identity neutralization — central helper", () => { test("leaves text without the GPT-5 line unchanged", () => { expect(neutralizeIdentity("plain system text")).toBe("plain system text"); + expect(neutralizeIdentity("You are Codex, helpful for reviewing PRs.")).toBe( + "You are Codex, helpful for reviewing PRs.", + ); }); test("neutral line still forbids GPT-5 / OpenAI self-reporting", () => {