@@ -425,6 +425,154 @@ describe("CopilotKitProvider", () => {
425425 } ) ;
426426 } ) ;
427427
428+ describe ( "a2ui prop" , ( ) => {
429+ const originalFetch = global . fetch ;
430+ const originalWindow = ( globalThis as { window ?: unknown } ) . window ;
431+
432+ beforeEach ( ( ) => {
433+ ( globalThis as { window ?: unknown } ) . window = { } ;
434+ } ) ;
435+
436+ afterEach ( ( ) => {
437+ global . fetch = originalFetch ;
438+ if ( originalWindow === undefined ) {
439+ delete ( globalThis as { window ?: unknown } ) . window ;
440+ } else {
441+ ( globalThis as { window ?: unknown } ) . window = originalWindow ;
442+ }
443+ } ) ;
444+
445+ it ( "does not register an a2ui-surface renderer by default" , ( ) => {
446+ const { result } = renderHook ( ( ) => useCopilotKit ( ) , {
447+ wrapper : ( { children } ) => (
448+ < CopilotKitProvider > { children } </ CopilotKitProvider >
449+ ) ,
450+ } ) ;
451+
452+ const a2uiRenderer =
453+ result . current . copilotkit . renderActivityMessages . find (
454+ ( r ) => r . activityType === "a2ui-surface" ,
455+ ) ;
456+ expect ( a2uiRenderer ) . toBeUndefined ( ) ;
457+ } ) ;
458+
459+ it ( "does not register an a2ui-surface renderer when a2ui.theme is provided but runtime has not signaled a2uiEnabled" , ( ) => {
460+ const customTheme = { components : { } , elements : { } , markdown : { } } as any ;
461+
462+ const { result } = renderHook ( ( ) => useCopilotKit ( ) , {
463+ wrapper : ( { children } ) => (
464+ < CopilotKitProvider a2ui = { { theme : customTheme } } >
465+ { children }
466+ </ CopilotKitProvider >
467+ ) ,
468+ } ) ;
469+
470+ const a2uiRenderer =
471+ result . current . copilotkit . renderActivityMessages . find (
472+ ( r ) => r . activityType === "a2ui-surface" ,
473+ ) ;
474+ expect ( a2uiRenderer ) . toBeUndefined ( ) ;
475+ } ) ;
476+
477+ it ( "registers an a2ui-surface renderer when runtime reports a2uiEnabled: true" , async ( ) => {
478+ global . fetch = vi . fn ( ) . mockResolvedValue ( {
479+ ok : true ,
480+ json : async ( ) => ( {
481+ version : "1.0.0" ,
482+ agents : { } ,
483+ audioFileTranscriptionEnabled : false ,
484+ a2uiEnabled : true ,
485+ } ) ,
486+ } ) ;
487+
488+ const { result } = renderHook ( ( ) => useCopilotKit ( ) , {
489+ wrapper : ( { children } ) => (
490+ < CopilotKitProvider runtimeUrl = "http://localhost:3000/api" >
491+ { children }
492+ </ CopilotKitProvider >
493+ ) ,
494+ } ) ;
495+
496+ await vi . waitFor ( ( ) => {
497+ const a2uiRenderer =
498+ result . current . copilotkit . renderActivityMessages . find (
499+ ( r ) => r . activityType === "a2ui-surface" ,
500+ ) ;
501+ expect ( a2uiRenderer ) . toBeDefined ( ) ;
502+ } ) ;
503+ } ) ;
504+
505+ it ( "does not register an a2ui-surface renderer when runtime reports a2uiEnabled: false" , async ( ) => {
506+ global . fetch = vi . fn ( ) . mockResolvedValue ( {
507+ ok : true ,
508+ json : async ( ) => ( {
509+ version : "1.0.0" ,
510+ agents : { } ,
511+ audioFileTranscriptionEnabled : false ,
512+ a2uiEnabled : false ,
513+ } ) ,
514+ } ) ;
515+
516+ const { result } = renderHook ( ( ) => useCopilotKit ( ) , {
517+ wrapper : ( { children } ) => (
518+ < CopilotKitProvider runtimeUrl = "http://localhost:3000/api" >
519+ { children }
520+ </ CopilotKitProvider >
521+ ) ,
522+ } ) ;
523+
524+ // Let the connection settle
525+ await vi . waitFor ( ( ) => {
526+ expect ( global . fetch ) . toHaveBeenCalled ( ) ;
527+ } ) ;
528+
529+ const a2uiRenderer =
530+ result . current . copilotkit . renderActivityMessages . find (
531+ ( r ) => r . activityType === "a2ui-surface" ,
532+ ) ;
533+ expect ( a2uiRenderer ) . toBeUndefined ( ) ;
534+ } ) ;
535+
536+ it ( "user-provided renderActivityMessages with activityType a2ui-surface takes precedence over built-in" , async ( ) => {
537+ global . fetch = vi . fn ( ) . mockResolvedValue ( {
538+ ok : true ,
539+ json : async ( ) => ( {
540+ version : "1.0.0" ,
541+ agents : { } ,
542+ audioFileTranscriptionEnabled : false ,
543+ a2uiEnabled : true ,
544+ } ) ,
545+ } ) ;
546+
547+ const userRenderer = {
548+ activityType : "a2ui-surface" ,
549+ content : { } as any ,
550+ render : ( ) => null ,
551+ } ;
552+
553+ const { result } = renderHook ( ( ) => useCopilotKit ( ) , {
554+ wrapper : ( { children } ) => (
555+ < CopilotKitProvider
556+ runtimeUrl = "http://localhost:3000/api"
557+ renderActivityMessages = { [ userRenderer ] }
558+ >
559+ { children }
560+ </ CopilotKitProvider >
561+ ) ,
562+ } ) ;
563+
564+ await vi . waitFor ( ( ) => {
565+ const renderers =
566+ result . current . copilotkit . renderActivityMessages . filter (
567+ ( r ) => r . activityType === "a2ui-surface" ,
568+ ) ;
569+ // Both present; user-provided comes first (index 0)
570+ expect ( renderers . length ) . toBeGreaterThanOrEqual ( 1 ) ;
571+ expect ( renderers [ 0 ] . render ) . toBe ( userRenderer . render ) ;
572+ } ) ;
573+ } ) ;
574+ } ) ;
575+
428576 describe ( "Edge cases" , ( ) => {
429577 it ( "handles empty arrays for tools" , ( ) => {
430578 const { result } = renderHook ( ( ) => useCopilotKit ( ) , {
0 commit comments