antigravity-claude-proxy 2.8.6 → 2.8.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "antigravity-claude-proxy",
3
- "version": "2.8.6",
3
+ "version": "2.8.7",
4
4
  "description": "Proxy server to use Antigravity's Claude models with Claude Code CLI",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
@@ -37,7 +37,8 @@
37
37
  "test:emptyretry": "node tests/test-empty-response-retry.cjs",
38
38
  "test:sanitizer": "node tests/test-schema-sanitizer.cjs",
39
39
  "test:strategies": "node tests/test-strategies.cjs",
40
- "test:cache-control": "node tests/test-cache-control.cjs"
40
+ "test:cache-control": "node tests/test-cache-control.cjs",
41
+ "test:billing-header": "node tests/test-billing-header.cjs"
41
42
  },
42
43
  "keywords": [
43
44
  "claude",
@@ -24,6 +24,17 @@ import {
24
24
  } from './thinking-utils.js';
25
25
  import { logger } from '../utils/logger.js';
26
26
 
27
+ // Claude Code injects a billing pseudo-header as a system prompt block, e.g.
28
+ // "x-anthropic-billing-header: cc_version=2.1.274.834; cc_entrypoint=sdk-cli;".
29
+ // Cloud Code API answers any request containing it with a 429 RESOURCE_EXHAUSTED
30
+ // even when quota remains (Issue #375).
31
+ const BILLING_HEADER_LINE = /^x-anthropic-billing-header:[^\n]*\n?/gim;
32
+
33
+ function stripBillingHeader(text) {
34
+ if (typeof text !== 'string' || !/x-anthropic-billing-header:/i.test(text)) return text;
35
+ return text.replace(BILLING_HEADER_LINE, '').trim();
36
+ }
37
+
27
38
  /**
28
39
  * Convert Anthropic Messages API request to the format expected by Cloud Code
29
40
  *
@@ -56,15 +67,18 @@ export function convertAnthropicToGoogle(anthropicRequest) {
56
67
  if (system) {
57
68
  let systemParts = [];
58
69
  if (typeof system === 'string') {
59
- systemParts = [{ text: system }];
70
+ systemParts = [{ text: stripBillingHeader(system) }];
60
71
  } else if (Array.isArray(system)) {
61
72
  // Filter for text blocks as system prompts are usually text
62
73
  // Anthropic supports text blocks in system prompts
63
74
  systemParts = system
64
75
  .filter(block => block.type === 'text')
65
- .map(block => ({ text: block.text }));
76
+ .map(block => ({ text: stripBillingHeader(block.text) }));
66
77
  }
67
78
 
79
+ // Drop parts left empty once the billing header is stripped
80
+ systemParts = systemParts.filter(part => part.text);
81
+
68
82
  if (systemParts.length > 0) {
69
83
  googleRequest.systemInstruction = {
70
84
  parts: systemParts