@@ -195,12 +195,55 @@ export function prepareAntigravityRequest(
195195 const functionDeclarations : any [ ] = [ ] ;
196196 const passthroughTools : any [ ] = [ ] ;
197197
198- const normalizeSchema = ( schema : any ) => {
199- if ( schema && typeof schema === "object" ) {
198+ // Sanitize schema - remove features not supported by JSON Schema draft 2020-12
199+ // Recursively strips anyOf/allOf/oneOf and converts to permissive types
200+ const sanitizeSchema = ( schema : any ) : any => {
201+ if ( ! schema || typeof schema !== "object" ) {
200202 return schema ;
201203 }
202- toolDebugMissing += 1 ;
203- return { type : "object" , properties : { } } ;
204+
205+ const sanitized : any = { } ;
206+
207+ for ( const key of Object . keys ( schema ) ) {
208+ // Skip anyOf/allOf/oneOf - not well supported
209+ if ( key === "anyOf" || key === "allOf" || key === "oneOf" ) {
210+ continue ;
211+ }
212+
213+ const value = schema [ key ] ;
214+
215+ if ( key === "items" && value && typeof value === "object" ) {
216+ // Handle array items - if it has anyOf, replace with permissive type
217+ if ( value . anyOf || value . allOf || value . oneOf ) {
218+ sanitized . items = { } ;
219+ } else {
220+ sanitized . items = sanitizeSchema ( value ) ;
221+ }
222+ } else if ( key === "properties" && value && typeof value === "object" ) {
223+ // Recursively sanitize properties
224+ sanitized . properties = { } ;
225+ for ( const propKey of Object . keys ( value ) ) {
226+ sanitized . properties [ propKey ] = sanitizeSchema ( value [ propKey ] ) ;
227+ }
228+ } else if ( key === "additionalProperties" && value && typeof value === "object" ) {
229+ sanitized . additionalProperties = sanitizeSchema ( value ) ;
230+ } else {
231+ sanitized [ key ] = value ;
232+ }
233+ }
234+
235+ return sanitized ;
236+ } ;
237+
238+ const normalizeSchema = ( schema : any ) => {
239+ if ( ! schema || typeof schema !== "object" ) {
240+ toolDebugMissing += 1 ;
241+ // Minimal fallback for tools without schemas
242+ return { type : "object" } ;
243+ }
244+
245+ // Sanitize and pass through
246+ return sanitizeSchema ( schema ) ;
204247 } ;
205248
206249 requestPayload . tools . forEach ( ( tool : any , idx : number ) => {
@@ -218,13 +261,16 @@ export function prepareAntigravityRequest(
218261 tool . custom ?. parameters ||
219262 tool . custom ?. input_schema ;
220263
221- const name =
264+ let name =
222265 decl ?. name ||
223266 tool . name ||
224267 tool . function ?. name ||
225268 tool . custom ?. name ||
226269 `tool-${ functionDeclarations . length } ` ;
227270
271+ // Sanitize tool name: must be alphanumeric with underscores, no special chars
272+ name = String ( name ) . replace ( / [ ^ a - z A - Z 0 - 9 _ - ] / g, "_" ) . slice ( 0 , 64 ) ;
273+
228274 const description =
229275 decl ?. description ||
230276 tool . description ||
@@ -234,7 +280,7 @@ export function prepareAntigravityRequest(
234280
235281 functionDeclarations . push ( {
236282 name,
237- description,
283+ description : String ( description || "" ) ,
238284 parameters : normalizeSchema ( schema ) ,
239285 } ) ;
240286
@@ -302,18 +348,18 @@ export function prepareAntigravityRequest(
302348 newTool . custom = {
303349 name : newTool . function . name || nameCandidate ,
304350 description : newTool . function . description ,
305- input_schema : schema ?? { type : "object" , properties : { } } ,
351+ input_schema : schema ?? { type : "object" , properties : { } , additionalProperties : false } ,
306352 } ;
307353 }
308354 if ( ! newTool . custom && ! newTool . function ) {
309355 newTool . custom = {
310356 name : nameCandidate ,
311357 description : newTool . description ,
312- input_schema : schema ?? { type : "object" , properties : { } } ,
358+ input_schema : schema ?? { type : "object" , properties : { } , additionalProperties : false } ,
313359 } ;
314360 }
315361 if ( newTool . custom && ! newTool . custom . input_schema ) {
316- newTool . custom . input_schema = { type : "object" , properties : { } } ;
362+ newTool . custom . input_schema = { type : "object" , properties : { } , additionalProperties : false } ;
317363 toolDebugMissing += 1 ;
318364 }
319365
@@ -487,6 +533,58 @@ export async function transformAntigravityResponse(
487533 return response ;
488534 }
489535
536+ // For successful streaming responses, use TransformStream to transform SSE events
537+ // while maintaining real-time streaming (no buffering of entire response)
538+ if ( streaming && response . ok && isEventStreamResponse && response . body ) {
539+ const headers = new Headers ( response . headers ) ;
540+
541+ // Buffer for partial SSE events that span chunks
542+ let buffer = "" ;
543+ const decoder = new TextDecoder ( ) ;
544+ const encoder = new TextEncoder ( ) ;
545+
546+ const transformStream = new TransformStream < Uint8Array , Uint8Array > ( {
547+ transform ( chunk , controller ) {
548+ // Decode chunk with stream: true to handle multi-byte characters
549+ buffer += decoder . decode ( chunk , { stream : true } ) ;
550+
551+ // Split on double newline (SSE event delimiter)
552+ const events = buffer . split ( "\n\n" ) ;
553+
554+ // Keep last part in buffer (may be incomplete)
555+ buffer = events . pop ( ) || "" ;
556+
557+ // Process and forward complete events immediately
558+ for ( const event of events ) {
559+ if ( event . trim ( ) ) {
560+ const transformed = transformStreamingPayload ( event ) ;
561+ controller . enqueue ( encoder . encode ( transformed + "\n\n" ) ) ;
562+ }
563+ }
564+ } ,
565+ flush ( controller ) {
566+ // Flush any remaining bytes from TextDecoder
567+ buffer += decoder . decode ( ) ;
568+
569+ // Handle any remaining data at stream end
570+ if ( buffer . trim ( ) ) {
571+ const transformed = transformStreamingPayload ( buffer ) ;
572+ controller . enqueue ( encoder . encode ( transformed ) ) ;
573+ }
574+ }
575+ } ) ;
576+
577+ logAntigravityDebugResponse ( debugContext , response , {
578+ note : "Streaming SSE response (transformed)" ,
579+ } ) ;
580+
581+ return new Response ( response . body . pipeThrough ( transformStream ) , {
582+ status : response . status ,
583+ statusText : response . statusText ,
584+ headers,
585+ } ) ;
586+ }
587+
490588 try {
491589 const text = await response . text ( ) ;
492590 const headers = new Headers ( response . headers ) ;
@@ -558,13 +656,12 @@ export async function transformAntigravityResponse(
558656
559657 logAntigravityDebugResponse ( debugContext , response , {
560658 body : text ,
561- note : streaming ? "Streaming SSE payload" : undefined ,
659+ note : streaming ? "Streaming SSE payload (buffered fallback) " : undefined ,
562660 headersOverride : headers ,
563661 } ) ;
564662
565- if ( streaming && response . ok && isEventStreamResponse ) {
566- return new Response ( transformStreamingPayload ( text ) , init ) ;
567- }
663+ // Note: successful streaming responses are handled above via TransformStream.
664+ // This path only handles non-streaming responses or failed streaming responses.
568665
569666 if ( ! parsed ) {
570667 return new Response ( text , init ) ;
0 commit comments