diff --git a/internal/runtime/executor/antigravity_executor.go b/internal/runtime/executor/antigravity_executor.go index 4430f27d4..074dc9fad 100644 --- a/internal/runtime/executor/antigravity_executor.go +++ b/internal/runtime/executor/antigravity_executor.go @@ -184,22 +184,24 @@ func newAntigravityHTTPClient(ctx context.Context, cfg *config.Config, auth *cli return client } -func validateAntigravityRequestSignatures(from sdktranslator.Format, rawJSON []byte) error { +func validateAntigravityRequestSignatures(from sdktranslator.Format, rawJSON []byte) ([]byte, error) { if from.String() != "claude" { - return nil + return rawJSON, nil } + // Always strip thinking blocks with empty signatures (proxy-generated). + rawJSON = antigravityclaude.StripEmptySignatureThinkingBlocks(rawJSON) if cache.SignatureCacheEnabled() { - return nil + return rawJSON, nil } if !cache.SignatureBypassStrictMode() { // Non-strict bypass: let the translator handle invalid signatures // by dropping unsigned thinking blocks silently (no 400). - return nil + return rawJSON, nil } if err := antigravityclaude.ValidateClaudeBypassSignatures(rawJSON); err != nil { - return statusErr{code: http.StatusBadRequest, msg: err.Error()} + return rawJSON, statusErr{code: http.StatusBadRequest, msg: err.Error()} } - return nil + return rawJSON, nil } // Identifier returns the executor identifier. @@ -695,9 +697,11 @@ func (e *AntigravityExecutor) Execute(ctx context.Context, auth *cliproxyauth.Au originalPayloadSource = opts.OriginalRequest } originalPayload := originalPayloadSource - if errValidate := validateAntigravityRequestSignatures(from, originalPayload); errValidate != nil { + originalPayload, errValidate := validateAntigravityRequestSignatures(from, originalPayload) + if errValidate != nil { return resp, errValidate } + req.Payload = originalPayload token, updatedAuth, errToken := e.ensureAccessToken(ctx, auth) if errToken != nil { return resp, errToken @@ -907,9 +911,11 @@ func (e *AntigravityExecutor) executeClaudeNonStream(ctx context.Context, auth * originalPayloadSource = opts.OriginalRequest } originalPayload := originalPayloadSource - if errValidate := validateAntigravityRequestSignatures(from, originalPayload); errValidate != nil { + originalPayload, errValidate := validateAntigravityRequestSignatures(from, originalPayload) + if errValidate != nil { return resp, errValidate } + req.Payload = originalPayload token, updatedAuth, errToken := e.ensureAccessToken(ctx, auth) if errToken != nil { return resp, errToken @@ -1370,9 +1376,11 @@ func (e *AntigravityExecutor) ExecuteStream(ctx context.Context, auth *cliproxya originalPayloadSource = opts.OriginalRequest } originalPayload := originalPayloadSource - if errValidate := validateAntigravityRequestSignatures(from, originalPayload); errValidate != nil { + originalPayload, errValidate := validateAntigravityRequestSignatures(from, originalPayload) + if errValidate != nil { return nil, errValidate } + req.Payload = originalPayload token, updatedAuth, errToken := e.ensureAccessToken(ctx, auth) if errToken != nil { return nil, errToken @@ -1626,9 +1634,11 @@ func (e *AntigravityExecutor) CountTokens(ctx context.Context, auth *cliproxyaut if len(opts.OriginalRequest) > 0 { originalPayloadSource = opts.OriginalRequest } - if errValidate := validateAntigravityRequestSignatures(from, originalPayloadSource); errValidate != nil { + originalPayloadSource, errValidate := validateAntigravityRequestSignatures(from, originalPayloadSource) + if errValidate != nil { return cliproxyexecutor.Response{}, errValidate } + req.Payload = originalPayloadSource token, updatedAuth, errToken := e.ensureAccessToken(ctx, auth) if errToken != nil { return cliproxyexecutor.Response{}, errToken diff --git a/internal/runtime/executor/antigravity_executor_signature_test.go b/internal/runtime/executor/antigravity_executor_signature_test.go index ad4ea4439..31955d35a 100644 --- a/internal/runtime/executor/antigravity_executor_signature_test.go +++ b/internal/runtime/executor/antigravity_executor_signature_test.go @@ -134,7 +134,7 @@ func TestAntigravityExecutor_NonStrictBypassSkipsPrecheck(t *testing.T) { payload := invalidClaudeThinkingPayload() from := sdktranslator.FromString("claude") - err := validateAntigravityRequestSignatures(from, payload) + _, err := validateAntigravityRequestSignatures(from, payload) if err != nil { t.Fatalf("non-strict bypass should skip precheck, got: %v", err) } @@ -150,7 +150,7 @@ func TestAntigravityExecutor_CacheModeSkipsPrecheck(t *testing.T) { payload := invalidClaudeThinkingPayload() from := sdktranslator.FromString("claude") - err := validateAntigravityRequestSignatures(from, payload) + _, err := validateAntigravityRequestSignatures(from, payload) if err != nil { t.Fatalf("cache mode should skip precheck, got: %v", err) } diff --git a/internal/translator/antigravity/claude/signature_validation.go b/internal/translator/antigravity/claude/signature_validation.go index 6ac75a151..f4fef08c6 100644 --- a/internal/translator/antigravity/claude/signature_validation.go +++ b/internal/translator/antigravity/claude/signature_validation.go @@ -55,6 +55,7 @@ import ( "github.com/router-for-me/CLIProxyAPI/v6/internal/cache" "github.com/tidwall/gjson" + "github.com/tidwall/sjson" "google.golang.org/protobuf/encoding/protowire" ) @@ -72,6 +73,44 @@ type claudeSignatureTree struct { HasField7 bool } +// StripEmptySignatureThinkingBlocks removes thinking blocks with empty signatures +// from messages[].content[]. These come from proxy-generated responses (Antigravity/Gemini) +// where no real Claude signature exists. +func StripEmptySignatureThinkingBlocks(payload []byte) []byte { + messages := gjson.GetBytes(payload, "messages") + if !messages.IsArray() { + return payload + } + modified := false + for i, msg := range messages.Array() { + content := msg.Get("content") + if !content.IsArray() { + continue + } + var kept []string + stripped := false + for _, part := range content.Array() { + if part.Get("type").String() == "thinking" && strings.TrimSpace(part.Get("signature").String()) == "" { + stripped = true + continue + } + kept = append(kept, part.Raw) + } + if stripped { + modified = true + if len(kept) == 0 { + payload, _ = sjson.SetRawBytes(payload, fmt.Sprintf("messages.%d.content", i), []byte("[]")) + } else { + payload, _ = sjson.SetRawBytes(payload, fmt.Sprintf("messages.%d.content", i), []byte("["+strings.Join(kept, ",")+"]")) + } + } + } + if !modified { + return payload + } + return payload +} + func ValidateClaudeBypassSignatures(inputRawJSON []byte) error { messages := gjson.GetBytes(inputRawJSON, "messages") if !messages.IsArray() {