Skip to content

Commit dcc72a9

Browse files
committed
fix(proxy): preserve opencode CLI client identity to upstream
opencode CLI sends Originator: opencode and a non-codex User-Agent. The previous logic at executor.go:514 only honored codex_* prefixes, so opencode's identity was rewritten to codex_cli_rs. The Codex upstream then silently downgraded reasoning_effort=xhigh, which is the symptom reported in issue #65 (xhigh shown in admin panel but no thinking output). Add "opencode" to the official originator/UA prefix lists so opencode's real identity flows through unchanged, matching CLIProxyAPI's behavior. Closes #65
1 parent 44c83e7 commit dcc72a9

3 files changed

Lines changed: 34 additions & 2 deletions

File tree

proxy/executor_test.go

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,7 @@ func TestApplyCodexRequestHeadersFallsBackForNonOfficialClient(t *testing.T) {
290290
acc := &auth.Account{DBID: 42}
291291
downstreamHeaders := http.Header{
292292
"User-Agent": []string{"curl/8.0"},
293-
"Originator": []string{"opencode"},
293+
"Originator": []string{"random-client"},
294294
}
295295

296296
applyCodexRequestHeaders(req, acc, "token-123", "", "api-key-1", nil, downstreamHeaders)
@@ -306,6 +306,31 @@ func TestApplyCodexRequestHeadersFallsBackForNonOfficialClient(t *testing.T) {
306306
}
307307
}
308308

309+
func TestApplyCodexRequestHeadersPreservesOpenCodeClient(t *testing.T) {
310+
prev := CurrentRuntimeSettings()
311+
ApplyRuntimeSettings(RuntimeSettings{ClientCompatMode: ClientCompatModePreserve})
312+
t.Cleanup(func() { ApplyRuntimeSettings(prev) })
313+
314+
req, err := http.NewRequest(http.MethodPost, "https://example.com/v1/responses", nil)
315+
if err != nil {
316+
t.Fatalf("http.NewRequest() error = %v", err)
317+
}
318+
acc := &auth.Account{DBID: 42, AccountID: "acct-42"}
319+
downstreamHeaders := http.Header{
320+
"User-Agent": []string{"opencode/0.5.0"},
321+
"Originator": []string{"opencode"},
322+
}
323+
324+
applyCodexRequestHeaders(req, acc, "token-123", "", "api-key-1", nil, downstreamHeaders)
325+
326+
if got := req.Header.Get("User-Agent"); got != "opencode/0.5.0" {
327+
t.Fatalf("User-Agent = %q, want %q", got, "opencode/0.5.0")
328+
}
329+
if got := req.Header.Get("Originator"); got != "opencode" {
330+
t.Fatalf("Originator = %q, want %q", got, "opencode")
331+
}
332+
}
333+
309334
func TestCodexTransportModeDefaultsToStandard(t *testing.T) {
310335
t.Setenv("CODEX_TRANSPORT_MODE", "")
311336
if _, ok := newCodexTransport("").(*http.Transport); !ok {

proxy/useragent.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,16 @@ var codexOfficialClientUserAgentPrefixes = []string{
3636
"codex_exec/",
3737
"codex_sdk_ts/",
3838
"codex ",
39+
"opencode/",
3940
}
4041

42+
// Third-party CLIs that ChatGPT-backend Codex accepts as first-party originators.
43+
// opencode advertises itself via Originator: "opencode" and reaching upstream with
44+
// that identity is required for features like reasoning_effort=xhigh to take effect.
4145
var codexOfficialClientOriginatorPrefixes = []string{
4246
"codex_",
4347
"codex ",
48+
"opencode",
4449
}
4550

4651
func IsCodexOfficialClientByHeaders(userAgent, originator string) bool {

proxy/useragent_test.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,9 @@ func TestIsCodexOfficialClientByHeaders(t *testing.T) {
3838
{name: "cli ua", userAgent: "codex_cli_rs/0.128.0", want: true},
3939
{name: "vscode ua", userAgent: "codex_vscode/1.2.3", want: true},
4040
{name: "desktop originator", originator: "codex_chatgpt_desktop", want: true},
41-
{name: "non official", userAgent: "curl/8.0", originator: "opencode", want: false},
41+
{name: "opencode ua", userAgent: "opencode/0.5.0", want: true},
42+
{name: "opencode originator", originator: "opencode", want: true},
43+
{name: "non official", userAgent: "curl/8.0", originator: "random-client", want: false},
4244
}
4345
for _, tc := range tests {
4446
t.Run(tc.name, func(t *testing.T) {

0 commit comments

Comments
 (0)