@@ -827,11 +827,9 @@ export function prepareAntigravityRequest(
827827 // Use stable session ID for signature caching across multi-turn conversations
828828 ( req as any ) . sessionId = signatureSessionKey ;
829829 stripInjectedDebugFromRequestPayload ( req as Record < string , unknown > ) ;
830+ sanitizeCrossModelPayloadInPlace ( req , { targetModel : effectiveModel } ) ;
830831
831832 if ( isClaude ) {
832- // Step 0: Sanitize cross-model metadata (strips Gemini signatures when sending to Claude)
833- sanitizeCrossModelPayloadInPlace ( req , { targetModel : effectiveModel } ) ;
834-
835833 // Step 1: Strip corrupted/unsigned thinking blocks FIRST
836834 deepFilterThinkingBlocks ( req , signatureSessionKey , getCachedSignature , true ) ;
837835
@@ -1270,10 +1268,8 @@ export function prepareAntigravityRequest(
12701268 // For Claude models, filter out unsigned thinking blocks (required by Claude API)
12711269 // Attempts to restore signatures from cache for multi-turn conversations
12721270 // Handle both Gemini-style contents[] and Anthropic-style messages[] payloads.
1271+ sanitizeCrossModelPayloadInPlace ( requestPayload , { targetModel : effectiveModel } ) ;
12731272 if ( isClaude ) {
1274- // Step 0: Sanitize cross-model metadata (strips Gemini signatures when sending to Claude)
1275- sanitizeCrossModelPayloadInPlace ( requestPayload , { targetModel : effectiveModel } ) ;
1276-
12771273 // Step 1: Strip corrupted/unsigned thinking blocks FIRST
12781274 deepFilterThinkingBlocks ( requestPayload , signatureSessionKey , getCachedSignature , true ) ;
12791275
@@ -1588,6 +1584,49 @@ export async function transformAntigravityResponse(
15881584 toolDebugPayload ?: string ,
15891585 debugLines ?: string [ ] ,
15901586) : Promise < Response > {
1587+ const extractBestErrorMessage = ( errorValue : unknown ) : string => {
1588+ if ( ! errorValue || typeof errorValue !== "object" ) {
1589+ return "" ;
1590+ }
1591+
1592+ const errorObj = errorValue as Record < string , unknown > ;
1593+ if ( typeof errorObj . message === "string" && errorObj . message . trim ( ) ) {
1594+ return errorObj . message ;
1595+ }
1596+ if ( typeof errorObj . status === "string" && errorObj . status . trim ( ) ) {
1597+ return errorObj . status ;
1598+ }
1599+
1600+ if ( Array . isArray ( errorObj . details ) && errorObj . details . length > 0 ) {
1601+ const firstDetail = errorObj . details [ 0 ] ;
1602+ if ( firstDetail && typeof firstDetail === "object" ) {
1603+ const detailRecord = firstDetail as Record < string , unknown > ;
1604+ if ( typeof detailRecord . reason === "string" && detailRecord . reason . trim ( ) ) {
1605+ return detailRecord . reason ;
1606+ }
1607+ }
1608+ }
1609+
1610+ if ( Array . isArray ( errorObj . errors ) && errorObj . errors . length > 0 ) {
1611+ const firstBatchError = errorObj . errors [ 0 ] ;
1612+ if ( firstBatchError && typeof firstBatchError === "object" ) {
1613+ const batchRecord = firstBatchError as Record < string , unknown > ;
1614+ if ( typeof batchRecord . message === "string" && batchRecord . message . trim ( ) ) {
1615+ return batchRecord . message ;
1616+ }
1617+ if ( typeof batchRecord . status === "string" && batchRecord . status . trim ( ) ) {
1618+ return batchRecord . status ;
1619+ }
1620+ }
1621+ }
1622+
1623+ try {
1624+ return JSON . stringify ( errorObj ) ;
1625+ } catch {
1626+ return "" ;
1627+ }
1628+ } ;
1629+
15911630 const contentType = response . headers . get ( "content-type" ) ?? "" ;
15921631 const isJsonResponse = contentType . includes ( "application/json" ) ;
15931632 const isEventStreamResponse = contentType . includes ( "text/event-stream" ) ;
@@ -1651,31 +1690,67 @@ export async function transformAntigravityResponse(
16511690 const text = await response . text ( ) ;
16521691
16531692 if ( ! response . ok ) {
1654- let errorBody ;
1693+ let errorBody : any ;
16551694 try {
16561695 errorBody = JSON . parse ( text ) ;
16571696 } catch {
16581697 errorBody = { error : { message : text } } ;
16591698 }
16601699
1700+ if ( ! errorBody || typeof errorBody !== "object" ) {
1701+ errorBody = { error : { message : String ( errorBody ?? text ) } } ;
1702+ }
1703+
1704+ if ( ! errorBody . error || typeof errorBody . error !== "object" ) {
1705+ errorBody . error = {
1706+ message : typeof errorBody . message === "string" ? errorBody . message : undefined ,
1707+ status : typeof errorBody . status === "string" ? errorBody . status : undefined ,
1708+ details : Array . isArray ( errorBody . details ) ? errorBody . details : undefined ,
1709+ errors : Array . isArray ( errorBody . errors ) ? errorBody . errors : undefined ,
1710+ } ;
1711+ }
1712+
1713+ // Extract retry headers from google.rpc.RetryInfo when present.
1714+ if ( Array . isArray ( errorBody ?. error ?. details ) ) {
1715+ const retryInfo = errorBody . error . details . find (
1716+ ( detail : any ) => detail ?. [ "@type" ] === "type.googleapis.com/google.rpc.RetryInfo" ,
1717+ ) ;
1718+
1719+ if ( retryInfo ?. retryDelay ) {
1720+ const match = retryInfo . retryDelay . match ( / ^ ( [ \d . ] + ) s $ / ) ;
1721+ if ( match && match [ 1 ] ) {
1722+ const retrySeconds = parseFloat ( match [ 1 ] ) ;
1723+ if ( ! isNaN ( retrySeconds ) && retrySeconds > 0 ) {
1724+ const retryAfterSec = Math . ceil ( retrySeconds ) . toString ( ) ;
1725+ const retryAfterMs = Math . ceil ( retrySeconds * 1000 ) . toString ( ) ;
1726+ headers . set ( "Retry-After" , retryAfterSec ) ;
1727+ headers . set ( "retry-after-ms" , retryAfterMs ) ;
1728+ }
1729+ }
1730+ }
1731+ }
1732+
16611733 // Inject Debug Info
16621734 if ( errorBody ?. error ) {
1663- const rawErrorMessage =
1664- typeof errorBody . error . message === "string" && errorBody . error . message . length > 0
1665- ? errorBody . error . message
1666- : "Unknown error" ;
1667- const errorType = detectErrorType ( rawErrorMessage ) ;
1735+ const extractedMessage = extractBestErrorMessage ( errorBody . error ) || "Unknown error" ;
16681736 const debugInfo = `\n\n[Debug Info]\nRequested Model: ${ requestedModel || "Unknown" } \nEffective Model: ${ effectiveModel || "Unknown" } \nProject: ${ projectId || "Unknown" } \nEndpoint: ${ endpoint || "Unknown" } \nStatus: ${ response . status } \nRequest ID: ${ headers . get ( "x-request-id" ) || "N/A" } ${ toolDebugMissing !== undefined ? `\nTool Debug Missing: ${ toolDebugMissing } ` : "" } ${ toolDebugSummary ? `\nTool Debug Summary: ${ toolDebugSummary } ` : "" } ${ toolDebugPayload ? `\nTool Debug Payload: ${ toolDebugPayload } ` : "" } ` ;
16691737 const injectedDebug = debugText ? `\n\n${ debugText } ` : "" ;
1670- errorBody . error . message = rawErrorMessage + debugInfo + injectedDebug ;
1671-
1672- // Check if this is a recoverable thinking error - throw to trigger retry
1738+ errorBody . error . message = extractedMessage + debugInfo + injectedDebug ;
1739+
1740+ // Signal recoverable thinking errors via headers so caller can retry without throwing.
1741+ const firstDetailReason =
1742+ Array . isArray ( errorBody ?. error ?. details ) &&
1743+ errorBody . error . details [ 0 ] &&
1744+ typeof errorBody . error . details [ 0 ] === "object"
1745+ ? ( ( errorBody . error . details [ 0 ] as Record < string , unknown > ) . reason as string | undefined )
1746+ : undefined ;
1747+ const errorType = detectErrorType (
1748+ [ extractedMessage , typeof firstDetailReason === "string" ? firstDetailReason : "" ]
1749+ . filter ( Boolean )
1750+ . join ( " " ) ,
1751+ ) ;
16731752 if ( errorType === "thinking_block_order" ) {
1674- const recoveryError = new Error ( "THINKING_RECOVERY_NEEDED" ) ;
1675- ( recoveryError as any ) . recoveryType = errorType ;
1676- ( recoveryError as any ) . originalError = errorBody ;
1677- ( recoveryError as any ) . debugInfo = debugInfo ;
1678- throw recoveryError ;
1753+ headers . set ( "x-antigravity-recovery-needed" , errorType ) ;
16791754 }
16801755
16811756 // Detect context length / prompt too long errors - signal to caller for toast
@@ -1704,25 +1779,6 @@ export async function transformAntigravityResponse(
17041779 headers
17051780 } ) ;
17061781 }
1707-
1708- if ( errorBody ?. error ?. details && Array . isArray ( errorBody . error . details ) ) {
1709- const retryInfo = errorBody . error . details . find (
1710- ( detail : any ) => detail [ '@type' ] === 'type.googleapis.com/google.rpc.RetryInfo'
1711- ) ;
1712-
1713- if ( retryInfo ?. retryDelay ) {
1714- const match = retryInfo . retryDelay . match ( / ^ ( [ \d . ] + ) s $ / ) ;
1715- if ( match && match [ 1 ] ) {
1716- const retrySeconds = parseFloat ( match [ 1 ] ) ;
1717- if ( ! isNaN ( retrySeconds ) && retrySeconds > 0 ) {
1718- const retryAfterSec = Math . ceil ( retrySeconds ) . toString ( ) ;
1719- const retryAfterMs = Math . ceil ( retrySeconds * 1000 ) . toString ( ) ;
1720- headers . set ( 'Retry-After' , retryAfterSec ) ;
1721- headers . set ( 'retry-after-ms' , retryAfterMs ) ;
1722- }
1723- }
1724- }
1725- }
17261782 }
17271783
17281784 const init = {
0 commit comments