@@ -544,6 +544,239 @@ test.describe('Kitty Graphics Protocol', () => {
544544 } ) ;
545545 } ) ;
546546
547+ test . describe ( 'Error responses for transmit and display' , ( ) => {
548+ test ( 'a=t sends EINVAL on decode error when id is specified' , async ( ) => {
549+ await ctx . page . evaluate ( ( ) => {
550+ ( window as any ) . kittyResponse = '' ;
551+ ( window as any ) . term . onData ( ( data : string ) => { ( window as any ) . kittyResponse = data ; } ) ;
552+ } ) ;
553+
554+ await ctx . proxy . write ( '\x1b_Gi=110,a=t,f=100;!!!invalid!!!\x1b\\' ) ;
555+ await timeout ( 100 ) ;
556+
557+ const response = await ctx . page . evaluate ( 'window.kittyResponse' ) ;
558+ strictEqual ( response , '\x1b_Gi=110;EINVAL:invalid base64 data\x1b\\' ) ;
559+ } ) ;
560+
561+ test ( 'a=t sends no response on decode error without id' , async ( ) => {
562+ await ctx . page . evaluate ( ( ) => {
563+ ( window as any ) . kittyGotResponse = false ;
564+ ( window as any ) . term . onData ( ( ) => { ( window as any ) . kittyGotResponse = true ; } ) ;
565+ } ) ;
566+
567+ await ctx . proxy . write ( '\x1b_Ga=t,f=100;!!!invalid!!!\x1b\\' ) ;
568+ await timeout ( 100 ) ;
569+
570+ strictEqual ( await ctx . page . evaluate ( 'window.kittyGotResponse' ) , false ) ;
571+ } ) ;
572+
573+ test ( 'a=T sends EINVAL on decode error when id is specified' , async ( ) => {
574+ await ctx . page . evaluate ( ( ) => {
575+ ( window as any ) . kittyResponse = '' ;
576+ ( window as any ) . term . onData ( ( data : string ) => { ( window as any ) . kittyResponse = data ; } ) ;
577+ } ) ;
578+
579+ await ctx . proxy . write ( '\x1b_Gi=120,a=T,f=100;!!!invalid!!!\x1b\\' ) ;
580+ await timeout ( 100 ) ;
581+
582+ const response = await ctx . page . evaluate ( 'window.kittyResponse' ) ;
583+ strictEqual ( response , '\x1b_Gi=120;EINVAL:invalid base64 data\x1b\\' ) ;
584+ } ) ;
585+
586+ test ( 'a=T sends no response on decode error without id' , async ( ) => {
587+ await ctx . page . evaluate ( ( ) => {
588+ ( window as any ) . kittyGotResponse = false ;
589+ ( window as any ) . term . onData ( ( ) => { ( window as any ) . kittyGotResponse = true ; } ) ;
590+ } ) ;
591+
592+ await ctx . proxy . write ( '\x1b_Ga=T,f=100;!!!invalid!!!\x1b\\' ) ;
593+ await timeout ( 100 ) ;
594+
595+ strictEqual ( await ctx . page . evaluate ( 'window.kittyGotResponse' ) , false ) ;
596+ } ) ;
597+
598+ test ( 'a=T sends EINVAL when raw pixel render fails (missing dimensions)' , async ( ) => {
599+ await ctx . page . evaluate ( ( ) => {
600+ ( window as any ) . kittyResponse = '' ;
601+ ( window as any ) . term . onData ( ( data : string ) => { ( window as any ) . kittyResponse = data ; } ) ;
602+ } ) ;
603+
604+ await ctx . proxy . write ( `\x1b_Gi=130,a=T,f=24;${ RAW_RGB_1X1_BLACK } \x1b\\` ) ;
605+ await timeout ( 100 ) ;
606+
607+ const response : string = await ctx . page . evaluate ( 'window.kittyResponse' ) ;
608+ strictEqual ( response . startsWith ( '\x1b_Gi=130;EINVAL:' ) , true ) ;
609+ } ) ;
610+
611+ test ( 'a=T sends OK on successful render with id' , async ( ) => {
612+ await ctx . page . evaluate ( ( ) => {
613+ ( window as any ) . kittyResponse = '' ;
614+ ( window as any ) . term . onData ( ( data : string ) => { ( window as any ) . kittyResponse = data ; } ) ;
615+ } ) ;
616+
617+ await ctx . proxy . write ( `\x1b_Gi=140,a=T,f=100;${ KITTY_BLACK_1X1_BASE64 } \x1b\\` ) ;
618+ await timeout ( 100 ) ;
619+
620+ const response = await ctx . page . evaluate ( 'window.kittyResponse' ) ;
621+ strictEqual ( response , '\x1b_Gi=140;OK\x1b\\' ) ;
622+ } ) ;
623+
624+ test ( 'a=t sends OK on successful transmit with id' , async ( ) => {
625+ await ctx . page . evaluate ( ( ) => {
626+ ( window as any ) . kittyResponse = '' ;
627+ ( window as any ) . term . onData ( ( data : string ) => { ( window as any ) . kittyResponse = data ; } ) ;
628+ } ) ;
629+
630+ await ctx . proxy . write ( `\x1b_Gi=150,a=t,f=100;${ KITTY_BLACK_1X1_BASE64 } \x1b\\` ) ;
631+ await timeout ( 100 ) ;
632+
633+ const response = await ctx . page . evaluate ( 'window.kittyResponse' ) ;
634+ strictEqual ( response , '\x1b_Gi=150;OK\x1b\\' ) ;
635+ } ) ;
636+
637+ test ( 'a=t EINVAL suppressed by q=2' , async ( ) => {
638+ await ctx . page . evaluate ( ( ) => {
639+ ( window as any ) . kittyGotResponse = false ;
640+ ( window as any ) . term . onData ( ( ) => { ( window as any ) . kittyGotResponse = true ; } ) ;
641+ } ) ;
642+
643+ await ctx . proxy . write ( '\x1b_Gi=160,a=t,q=2,f=100;!!!invalid!!!\x1b\\' ) ;
644+ await timeout ( 100 ) ;
645+
646+ strictEqual ( await ctx . page . evaluate ( 'window.kittyGotResponse' ) , false ) ;
647+ } ) ;
648+
649+ test ( 'a=T EINVAL suppressed by q=2' , async ( ) => {
650+ await ctx . page . evaluate ( ( ) => {
651+ ( window as any ) . kittyGotResponse = false ;
652+ ( window as any ) . term . onData ( ( ) => { ( window as any ) . kittyGotResponse = true ; } ) ;
653+ } ) ;
654+
655+ await ctx . proxy . write ( '\x1b_Gi=170,a=T,q=2,f=100;!!!invalid!!!\x1b\\' ) ;
656+ await timeout ( 100 ) ;
657+
658+ strictEqual ( await ctx . page . evaluate ( 'window.kittyGotResponse' ) , false ) ;
659+ } ) ;
660+
661+ test ( 'a=t OK suppressed by q=1' , async ( ) => {
662+ await ctx . page . evaluate ( ( ) => {
663+ ( window as any ) . kittyGotResponse = false ;
664+ ( window as any ) . term . onData ( ( ) => { ( window as any ) . kittyGotResponse = true ; } ) ;
665+ } ) ;
666+
667+ await ctx . proxy . write ( `\x1b_Gi=180,a=t,q=1,f=100;${ KITTY_BLACK_1X1_BASE64 } \x1b\\` ) ;
668+ await timeout ( 100 ) ;
669+
670+ strictEqual ( await ctx . page . evaluate ( 'window.kittyGotResponse' ) , false ) ;
671+ } ) ;
672+
673+ test ( 'a=T OK suppressed by q=1' , async ( ) => {
674+ await ctx . page . evaluate ( ( ) => {
675+ ( window as any ) . kittyGotResponse = false ;
676+ ( window as any ) . term . onData ( ( ) => { ( window as any ) . kittyGotResponse = true ; } ) ;
677+ } ) ;
678+
679+ await ctx . proxy . write ( `\x1b_Gi=190,a=T,q=1,f=100;${ KITTY_BLACK_1X1_BASE64 } \x1b\\` ) ;
680+ await timeout ( 100 ) ;
681+
682+ strictEqual ( await ctx . page . evaluate ( 'window.kittyGotResponse' ) , false ) ;
683+ } ) ;
684+ } ) ;
685+
686+ test . describe ( 'Transmission medium rejection' , ( ) => {
687+ test ( 'query rejects t=f (file transmission)' , async ( ) => {
688+ await ctx . page . evaluate ( ( ) => {
689+ ( window as any ) . kittyResponse = '' ;
690+ ( window as any ) . term . onData ( ( data : string ) => { ( window as any ) . kittyResponse = data ; } ) ;
691+ } ) ;
692+
693+ await ctx . proxy . write ( `\x1b_Gi=200,a=q,t=f,f=100;${ KITTY_BLACK_1X1_BASE64 } \x1b\\` ) ;
694+ await timeout ( 100 ) ;
695+
696+ const response : string = await ctx . page . evaluate ( 'window.kittyResponse' ) ;
697+ strictEqual ( response . startsWith ( '\x1b_Gi=200;EINVAL:' ) , true ) ;
698+ } ) ;
699+
700+ test ( 'query rejects t=s (shared memory)' , async ( ) => {
701+ await ctx . page . evaluate ( ( ) => {
702+ ( window as any ) . kittyResponse = '' ;
703+ ( window as any ) . term . onData ( ( data : string ) => { ( window as any ) . kittyResponse = data ; } ) ;
704+ } ) ;
705+
706+ await ctx . proxy . write ( `\x1b_Gi=201,a=q,t=s,f=100;${ KITTY_BLACK_1X1_BASE64 } \x1b\\` ) ;
707+ await timeout ( 100 ) ;
708+
709+ const response : string = await ctx . page . evaluate ( 'window.kittyResponse' ) ;
710+ strictEqual ( response . startsWith ( '\x1b_Gi=201;EINVAL:' ) , true ) ;
711+ } ) ;
712+
713+ test ( 'query rejects t=t (temp file)' , async ( ) => {
714+ await ctx . page . evaluate ( ( ) => {
715+ ( window as any ) . kittyResponse = '' ;
716+ ( window as any ) . term . onData ( ( data : string ) => { ( window as any ) . kittyResponse = data ; } ) ;
717+ } ) ;
718+
719+ await ctx . proxy . write ( `\x1b_Gi=202,a=q,t=t,f=100;${ KITTY_BLACK_1X1_BASE64 } \x1b\\` ) ;
720+ await timeout ( 100 ) ;
721+
722+ const response : string = await ctx . page . evaluate ( 'window.kittyResponse' ) ;
723+ strictEqual ( response . startsWith ( '\x1b_Gi=202;EINVAL:' ) , true ) ;
724+ } ) ;
725+
726+ test ( 'query accepts t=d (direct transmission)' , async ( ) => {
727+ await ctx . page . evaluate ( ( ) => {
728+ ( window as any ) . kittyResponse = '' ;
729+ ( window as any ) . term . onData ( ( data : string ) => { ( window as any ) . kittyResponse = data ; } ) ;
730+ } ) ;
731+
732+ await ctx . proxy . write ( `\x1b_Gi=203,a=q,t=d,f=100;${ KITTY_BLACK_1X1_BASE64 } \x1b\\` ) ;
733+ await timeout ( 100 ) ;
734+
735+ const response = await ctx . page . evaluate ( 'window.kittyResponse' ) ;
736+ strictEqual ( response , '\x1b_Gi=203;OK\x1b\\' ) ;
737+ } ) ;
738+
739+ test ( 'query without t key defaults to direct (OK)' , async ( ) => {
740+ await ctx . page . evaluate ( ( ) => {
741+ ( window as any ) . kittyResponse = '' ;
742+ ( window as any ) . term . onData ( ( data : string ) => { ( window as any ) . kittyResponse = data ; } ) ;
743+ } ) ;
744+
745+ await ctx . proxy . write ( `\x1b_Gi=204,a=q,f=100;${ KITTY_BLACK_1X1_BASE64 } \x1b\\` ) ;
746+ await timeout ( 100 ) ;
747+
748+ const response = await ctx . page . evaluate ( 'window.kittyResponse' ) ;
749+ strictEqual ( response , '\x1b_Gi=204;OK\x1b\\' ) ;
750+ } ) ;
751+ } ) ;
752+
753+ test . describe ( 'Unimplemented action responses' , ( ) => {
754+ test ( 'a=p with id responds EINVAL' , async ( ) => {
755+ await ctx . page . evaluate ( ( ) => {
756+ ( window as any ) . kittyResponse = '' ;
757+ ( window as any ) . term . onData ( ( data : string ) => { ( window as any ) . kittyResponse = data ; } ) ;
758+ } ) ;
759+
760+ await ctx . proxy . write ( `\x1b_Gi=210,a=p\x1b\\` ) ;
761+ await timeout ( 100 ) ;
762+
763+ const response : string = await ctx . page . evaluate ( 'window.kittyResponse' ) ;
764+ strictEqual ( response . startsWith ( '\x1b_Gi=210;EINVAL:' ) , true ) ;
765+ } ) ;
766+
767+ test ( 'a=p without id sends no response' , async ( ) => {
768+ await ctx . page . evaluate ( ( ) => {
769+ ( window as any ) . kittyGotResponse = false ;
770+ ( window as any ) . term . onData ( ( ) => { ( window as any ) . kittyGotResponse = true ; } ) ;
771+ } ) ;
772+
773+ await ctx . proxy . write ( `\x1b_Ga=p\x1b\\` ) ;
774+ await timeout ( 100 ) ;
775+
776+ strictEqual ( await ctx . page . evaluate ( 'window.kittyGotResponse' ) , false ) ;
777+ } ) ;
778+ } ) ;
779+
547780 test . describe ( 'Cursor positioning' , ( ) => {
548781 // NOTE: Current tests document ACTUAL behavior (MVP - cursor doesn't move)
549782 // Per Kitty spec: cursor placed at first column after last image column,
0 commit comments