diff --git a/aibridge/session.go b/aibridge/session.go index 759a2e69f1..9882029ea3 100644 --- a/aibridge/session.go +++ b/aibridge/session.go @@ -76,7 +76,12 @@ func GuessSessionID(client Client, r *http.Request) *string { case ClientCoderAgents: return cleanRef(r.Header.Get("X-Coder-Chat-Id")) case ClientOpenCode: - return cleanRef(r.Header.Get("X-OpenCode-Session")) + // Prefer X-OpenCode-Session (set by the OpenCode "Zen" provider). + if sid := cleanRef(r.Header.Get("X-OpenCode-Session")); sid != nil { + return sid + } + // Fall back to x-session-affinity (set by other providers). + return cleanRef(r.Header.Get("x-session-affinity")) case ClientCrush: return nil // Crush does not send a session ID header. case ClientRoo: diff --git a/aibridge/session_test.go b/aibridge/session_test.go index f1fb7614f4..24d72d57a3 100644 --- a/aibridge/session_test.go +++ b/aibridge/session_test.go @@ -195,6 +195,18 @@ func TestGuessSessionID(t *testing.T) { headers: map[string]string{"X-OpenCode-Session": " ses_15a48edefffe7oY0YcIHRv29dD "}, sessionID: utils.PtrTo("ses_15a48edefffe7oY0YcIHRv29dD"), }, + { + name: "opencode_zen_header_takes_precedence_over_session_affinity", + client: aibridge.ClientOpenCode, + headers: map[string]string{"X-OpenCode-Session": "zen-session", "x-session-affinity": "other-session"}, + sessionID: utils.PtrTo("zen-session"), + }, + { + name: "opencode_session_affinity_fallback", + client: aibridge.ClientOpenCode, + headers: map[string]string{"x-session-affinity": "affinity-session-123"}, + sessionID: utils.PtrTo("affinity-session-123"), + }, { name: "opencode_without_session_header", client: aibridge.ClientOpenCode,