fix(session): read Claude Code session id from its request header

Claude Code carries the session in metadata.user_id, which the Responses
API translation drops before the executor resolves a cache session. The
request then fell through to the assistant-text hash and the per-connection
fallback, so one conversation was split across several prompt_cache_key
values and the upstream prefix cache kept missing.

Fall back to the x-claude-code-session-id header, which survives every
translation. The body stays authoritative when both are present.
This commit is contained in:
decolua
2026-08-28 18:17:55 +07:00
parent df85e16d7a
commit 2fd99eae5d

View File

@@ -94,6 +94,7 @@ const MAX_CONTINUATION_SESSIONS = 5000;
// Client headers/body fields that carry an upstream session id (priority order)
const SESSION_HEADER_KEYS = ["x-session-id", "session-id", "session_id", "x-amp-thread-id"];
const CLAUDE_CODE_SESSION_RE = /_session_([a-f0-9-]+)$/;
const CLAUDE_CODE_SESSION_HEADER = "x-claude-code-session-id";
function sha16(text) {
return crypto.createHash("sha256").update(text).digest("hex").slice(0, 16);
@@ -135,7 +136,10 @@ function extractAntigravitySession(body) {
}
function extractClientSessionId(headers, body, scope = "") {
const claude = extractClaudeCodeSession(body?.metadata?.user_id);
// Claude Code sends the session in a header AND in metadata.user_id; the header
// survives translation to formats that drop metadata (e.g. Responses API).
const claude = extractClaudeCodeSession(body?.metadata?.user_id)
|| headerValue(headers, CLAUDE_CODE_SESSION_HEADER);
if (claude) return `claude:${claude}`;
const antigravity = extractAntigravitySession(body);
if (antigravity) return `antigravity:${antigravity}`;