From 9c9207c11041d2c9ae1aeab45821ce7890e65d3e Mon Sep 17 00:00:00 2001 From: Ingwannu Date: Wed, 9 Sep 2026 11:51:23 +0000 Subject: [PATCH] fix(codex): report operator-owned root routing truthfully --- src/codex/inject.ts | 5 ++-- structure/02_config-and-codex-home.md | 6 ++++ .../codex-inject-integration.test.ts | 28 ++++++++++++++++++- 3 files changed, 36 insertions(+), 3 deletions(-) diff --git a/src/codex/inject.ts b/src/codex/inject.ts index b43e8076de..cbcb3c671c 100644 --- a/src/codex/inject.ts +++ b/src/codex/inject.ts @@ -1430,7 +1430,7 @@ export async function injectCodexConfig( const historyMessage = keepRootOverrideAlongsideTable ? (keptUserBaseUrl - ? ` Codex resume history: left unchanged; threads already tagged openai follow your own root openai_base_url, not the proxy.\n` + ? ` Codex resume history: left unchanged; threads already tagged openai follow your configured root openai_base_url.\n` : ` Codex resume history: left unchanged; existing threads keep reaching the proxy through the retained openai_base_url override.\n`) : config?.syncResumeHistory === false ? ` Codex resume history: left unchanged (syncResumeHistory=false).\n` @@ -1447,6 +1447,7 @@ export async function injectCodexConfig( // The client-compaction form writes a provider table as well, so "nothing was injected" would // misdescribe the file it just produced: new threads do use the injected table. Report that // mixed result on its own terms, and never tell the operator to delete a setting of theirs. + // Ownership alone says nothing about destination: their line may already target this proxy. if (keptUserBaseUrl && keepRootOverrideAlongsideTable) { return { success: true, @@ -1459,7 +1460,7 @@ export async function injectCodexConfig( managedDefaultsMessage + ` New threads use the injected opencodex provider and route through the proxy.\n` + ` Threads already tagged openai resolve through Codex's built-in provider, which your root openai_base_url points at.\n` + - ` Remove that line and rerun 'ocx start' only if you want those threads on the proxy too.\n` + + ` No root URL change is required to enable client-side compaction for new threads.\n` + ` Fallback: codex --profile opencodex (same behavior)`, }; } diff --git a/structure/02_config-and-codex-home.md b/structure/02_config-and-codex-home.md index 9ab5cf2bba..d7aa214a43 100644 --- a/structure/02_config-and-codex-home.md +++ b/structure/02_config-and-codex-home.md @@ -360,6 +360,12 @@ explicitly runs legacy OpenAI recovery. A user-owned root `openai_base_url` is p overwritten, and that case also blocks managed sub-agent defaults rather than fighting the user for ownership. +Client-compaction mode can retain that user-owned root URL alongside an injected provider table. +Its status must distinguish ownership from destination: an unmarked user-owned line may already +point to this proxy. Report that existing `openai` threads follow the configured root URL and new +threads use the injected table, without inferring a foreign endpoint or prescribing URL removal. +This diagnostic distinction does not change URL ownership, journal entries, or session history. + **API auth header (non-loopback).** The built-in `openai` provider cannot carry the `x-opencodex-api-key` env header, so this form re-tags the root provider and appends the table: diff --git a/tests/codex-integration/codex-inject-integration.test.ts b/tests/codex-integration/codex-inject-integration.test.ts index 827251129e..57e83650f1 100644 --- a/tests/codex-integration/codex-inject-integration.test.ts +++ b/tests/codex-integration/codex-inject-integration.test.ts @@ -934,7 +934,33 @@ describe("injectCodexConfig integration (Design B)", () => { expect(message).not.toContain("Codex routing NOT injected"); expect(message).not.toContain("remove your openai_base_url line"); expect(message).toContain("left exactly as you set it"); - expect(message).toContain("follow your own root openai_base_url, not the proxy"); + expect(message).toContain("follow your configured root openai_base_url"); + expect(message).not.toContain("not the proxy"); + expect(message).not.toContain("Remove that line"); + }); + + test("client compaction does not mistake a user-owned proxy URL for a foreign destination (#4110)", () => { + // The URL equals the target but lacks our marker: keep ownership separate from destination. + const rootLine = 'openai_base_url = "http://127.0.0.1:10100/v1"'; + writeFileSync(join(codexHome, "config.toml"), `${rootLine}\nmodel = "gpt-5.5"\n`, "utf8"); + + const enabled = runInject(codexHome, ocxHome, JSON.stringify({ codexClientCompaction: true })); + expect(enabled.status).toBe(0); + const config = readFileSync(join(codexHome, "config.toml"), "utf8"); + expect(config).toContain(rootLine); + expect(config.match(/openai_base_url/g)?.length).toBe(1); + expect(config).toContain('model_provider = "opencodex"'); + expect(config).toContain("[model_providers.opencodex]"); + const journal = JSON.parse(readFileSync(join(codexHome, "opencodex-journal.json"), "utf8")); + expect(journal.injectedOpenaiBaseUrl).toBeNull(); + + const message = String(JSON.parse(enabled.stdout).message); + expect(message).toContain("Injected opencodex as default provider"); + expect(message).toContain("left exactly as you set it"); + expect(message).toContain("follow your configured root openai_base_url"); + expect(message).not.toContain("not the proxy"); + expect(message).not.toContain("Remove that line"); + expect(message).not.toContain("Codex routing NOT injected"); }); test("the managed override keeps reporting proxy routing for existing threads", () => {