diff --git a/open-sse/config/appConstants.js b/open-sse/config/appConstants.js index 106cba50..62ddfb6b 100644 --- a/open-sse/config/appConstants.js +++ b/open-sse/config/appConstants.js @@ -179,6 +179,10 @@ export const CLAUDE_SYSTEM_PROMPT = "You are Claude Code, Anthropic's official C export const ANTIGRAVITY_PROMPT_REWRITES = [ { from: "You are a Claude agent, built on Anthropic's Claude Agent SDK.", to: "" }, { from: /You are Hermes Agent,\s*(an intelligent AI assistant)(?: created by Nous Research)?\./gi, to: "You are Hermes Agent. You are $1." }, + // Claude Code prepends this line to its system prompt. The Claude-format translator strips it, + // but OpenAI-format clients (e.g. proxies that convert Claude Code to /v1/chat/completions) + // pass it through, and any system text containing it gets a fake 429 RESOURCE_EXHAUSTED. + { from: /^x-anthropic-billing-header:[^\n]*(?:\r?\n)*/gim, to: "" }, { from: /opencode/gi, to: (m) => (m === "OpenCode" ? "Antigravity" : m === "OPENCODE" ? "ANTIGRAVITY" : "antigravity") } ]; diff --git a/tests/unit/antigravity-billing-header-rewrite.test.js b/tests/unit/antigravity-billing-header-rewrite.test.js new file mode 100644 index 00000000..e324294f --- /dev/null +++ b/tests/unit/antigravity-billing-header-rewrite.test.js @@ -0,0 +1,38 @@ +import { describe, expect, it } from "vitest"; + +import { AntigravityExecutor } from "../../open-sse/executors/antigravity.js"; +import { openaiToAntigravityRequest } from "../../open-sse/translator/request/openai-to-gemini.js"; + +const HEADER = "x-anthropic-billing-header: cc_version=2.1.275.f15; cc_entrypoint=cli;"; + +function systemTextSentToAntigravity(systemContent) { + // OpenAI-format client (e.g. a proxy converting Claude Code to /v1/chat/completions). + const body = openaiToAntigravityRequest("gemini-3.8-flash-tiered", { + messages: [ + { role: "system", content: systemContent }, + { role: "user", content: "hi" }, + ], + }, true); + const finalBody = new AntigravityExecutor().transformRequest("gemini-3.8-flash-tiered", body, true, {}); + return finalBody.request.systemInstruction.parts.map((p) => p.text).join("\n"); +} + +describe("Antigravity strips the Claude Code billing header from system prompts", () => { + it("removes the header line prepended by Claude Code", () => { + const text = systemTextSentToAntigravity(`${HEADER}\n\nYou are Claude Code, Anthropic's official CLI for Claude.`); + expect(text).not.toContain("x-anthropic-billing-header"); + expect(text).toContain("You are Claude Code, Anthropic's official CLI for Claude."); + }); + + it("removes the header when it is not the first line", () => { + const text = systemTextSentToAntigravity(`Some preamble\n${HEADER}\nRest of prompt`); + expect(text).not.toContain("x-anthropic-billing-header"); + expect(text).toContain("Some preamble"); + expect(text).toContain("Rest of prompt"); + }); + + it("leaves prompts without the header untouched", () => { + const text = systemTextSentToAntigravity("You are a helpful assistant."); + expect(text).toContain("You are a helpful assistant."); + }); +});