@@ -503,6 +503,38 @@ describe("web_fetch extraction fallbacks", () => {
503503 await rm ( details . fullOutputPath , { force : true } ) ;
504504 } ) ;
505505
506+ it ( "does not split an emoji at the web_fetch spill file cap" , async ( ) => {
507+ const prefix = "x" . repeat ( WEB_FETCH_SPILL_MAX_CHARS - 1 ) ;
508+ const fullText = `${ prefix } ${ String . fromCodePoint ( 0x1f600 ) } tail` ;
509+ installPlainTextFetch ( fullText ) ;
510+
511+ const tool = createFetchTool ( {
512+ firecrawl : { enabled : false } ,
513+ maxChars : 500 ,
514+ maxResponseBytes : WEB_FETCH_SPILL_MAX_CHARS + 1_000 ,
515+ } ) ;
516+
517+ const result = await tool ?. execute ?.( "call" , { url : "https://example.com/spill-utf16" } ) ;
518+ const details = result ?. details as {
519+ text ?: string ;
520+ fullOutputPath ?: string ;
521+ spilledChars ?: number ;
522+ spillTruncated ?: boolean ;
523+ } ;
524+ if ( ! details . fullOutputPath ) {
525+ throw new Error ( "expected fullOutputPath" ) ;
526+ }
527+
528+ expect ( details . spilledChars ) . toBe ( WEB_FETCH_SPILL_MAX_CHARS - 1 ) ;
529+ expect ( details . text ) . toContain ( `Spilled first ${ WEB_FETCH_SPILL_MAX_CHARS - 1 } chars.` ) ;
530+ expect ( details . spillTruncated ) . toBe ( true ) ;
531+ const spilledText = await readFile ( details . fullOutputPath , "utf8" ) ;
532+ expect ( spilledText ) . toContain ( prefix ) ;
533+ expect ( spilledText ) . not . toContain ( String . fromCodePoint ( 0x1f600 ) ) ;
534+ expect ( spilledText ) . not . toContain ( String . fromCharCode ( 0xd83d ) ) ;
535+ await rm ( details . fullOutputPath , { force : true } ) ;
536+ } ) ;
537+
506538 it ( "marks byte-capped web_fetch spills as partial" , async ( ) => {
507539 const fullText = "z" . repeat ( 40_000 ) ;
508540 installMockFetch ( ( input : RequestInfo | URL ) => {
0 commit comments