@@ -355,3 +355,195 @@ async def test_include_sources_user_self_drops_upstream_across_turns():
355355 "upstream reply" in str (c ).lower () for _ , c in agent2_second_contents
356356 )
357357 assert not any ("For context:" in str (c ) for _ , c in agent2_second_contents )
358+
359+
360+ # ---------------------------------------------------------------------------
361+ # include_contents='default' + include_sources combinations
362+ # ---------------------------------------------------------------------------
363+
364+
365+ @pytest .mark .asyncio
366+ async def test_include_contents_default_with_source_filter_user ():
367+ """include_contents='default' + include_sources=['user'] keeps only user messages across full history."""
368+ agent1_model = testing_utils .MockModel .create (
369+ responses = ["Agent1 result turn1" , "Agent1 result turn2" ]
370+ )
371+ agent1 = LlmAgent (
372+ name = "agent1" , model = agent1_model , instruction = "You are agent1"
373+ )
374+
375+ agent2_model = testing_utils .MockModel .create (
376+ responses = ["Turn1 response" , "Turn2 response" ]
377+ )
378+ agent2 = LlmAgent (
379+ name = "agent2" ,
380+ model = agent2_model ,
381+ include_sources = ["user" ],
382+ instruction = "You are agent2" ,
383+ )
384+
385+ runner = testing_utils .InMemoryRunner (
386+ SequentialAgent (name = "pipeline" , sub_agents = [agent1 , agent2 ])
387+ )
388+ runner .run ("First user message" )
389+ runner .run ("Second user message" )
390+
391+ # Second invocation: full history, but only user messages kept
392+ agent2_second_contents = testing_utils .simplify_contents (
393+ agent2_model .requests [1 ].contents
394+ )
395+ assert any ("First user message" in str (c ) for _ , c in agent2_second_contents )
396+ assert any ("Second user message" in str (c ) for _ , c in agent2_second_contents )
397+ assert not any ("Agent1 result" in str (c ) for _ , c in agent2_second_contents )
398+ assert not any ("For context:" in str (c ) for _ , c in agent2_second_contents )
399+
400+
401+ # ---------------------------------------------------------------------------
402+ # include_contents='current'
403+ # ---------------------------------------------------------------------------
404+
405+
406+ @pytest .mark .asyncio
407+ async def test_include_contents_current_sees_user_and_all_upstream_agents ():
408+ """include_contents='current' anchors at user message — all sibling agents visible."""
409+ agent1_model = testing_utils .MockModel .create (responses = ["Agent1 result" ])
410+ agent1 = LlmAgent (
411+ name = "agent1" , model = agent1_model , instruction = "You are agent1"
412+ )
413+
414+ agent2_model = testing_utils .MockModel .create (responses = ["Agent2 result" ])
415+ agent2 = LlmAgent (
416+ name = "agent2" , model = agent2_model , instruction = "You are agent2"
417+ )
418+
419+ agent3_model = testing_utils .MockModel .create (responses = ["Agent3 done" ])
420+ agent3 = LlmAgent (
421+ name = "agent3" ,
422+ model = agent3_model ,
423+ include_contents = "current" ,
424+ instruction = "You are agent3" ,
425+ )
426+
427+ runner = testing_utils .InMemoryRunner (
428+ SequentialAgent (name = "pipeline" , sub_agents = [agent1 , agent2 , agent3 ])
429+ )
430+ runner .run ("Original user request" )
431+
432+ agent3_contents = testing_utils .simplify_contents (
433+ agent3_model .requests [0 ].contents
434+ )
435+
436+ # User message must be present
437+ assert any ("Original user request" in str (c ) for _ , c in agent3_contents )
438+ # Both upstream agents' narrative entries must be present
439+ assert any ("Agent1 result" in str (c ) for _ , c in agent3_contents )
440+ assert any ("Agent2 result" in str (c ) for _ , c in agent3_contents )
441+
442+
443+ @pytest .mark .asyncio
444+ async def test_include_contents_current_with_source_filter_user_not_empty ():
445+ """include_contents='current' + include_sources=['user'] → user message, not empty.
446+
447+ Contrast with include_contents='none' + include_sources=['user'] which
448+ produces empty context when the last event is a peer agent's output.
449+ """
450+ agent1_model = testing_utils .MockModel .create (responses = ["Agent1 result" ])
451+ agent1 = LlmAgent (
452+ name = "agent1" , model = agent1_model , instruction = "You are agent1"
453+ )
454+
455+ agent2_model = testing_utils .MockModel .create (responses = ["Agent2 done" ])
456+ agent2 = LlmAgent (
457+ name = "agent2" ,
458+ model = agent2_model ,
459+ include_contents = "current" ,
460+ include_sources = ["user" ],
461+ instruction = "You are agent2" ,
462+ )
463+
464+ runner = testing_utils .InMemoryRunner (
465+ SequentialAgent (name = "pipeline" , sub_agents = [agent1 , agent2 ])
466+ )
467+ runner .run ("Hello from user" )
468+
469+ agent2_contents = testing_utils .simplify_contents (
470+ agent2_model .requests [0 ].contents
471+ )
472+
473+ # User message must be present and result must not be empty
474+ assert len (agent2_contents ) > 0
475+ assert any ("Hello from user" in str (c ) for _ , c in agent2_contents )
476+ # Upstream agent narrative must be filtered out
477+ assert not any ("Agent1 result" in str (c ) for _ , c in agent2_contents )
478+ assert not any ("For context:" in str (c ) for _ , c in agent2_contents )
479+
480+
481+ @pytest .mark .asyncio
482+ async def test_include_contents_current_with_source_filter_user_and_self ():
483+ """include_contents='current' + include_sources=['user', 'self'] keeps user + own turns only."""
484+ agent1_model = testing_utils .MockModel .create (
485+ responses = ["Agent1 result turn1" , "Agent1 result turn2" ]
486+ )
487+ agent1 = LlmAgent (
488+ name = "agent1" , model = agent1_model , instruction = "You are agent1"
489+ )
490+
491+ agent2_model = testing_utils .MockModel .create (
492+ responses = ["Agent2 first turn" , "Agent2 second turn" ]
493+ )
494+ agent2 = LlmAgent (
495+ name = "agent2" ,
496+ model = agent2_model ,
497+ include_contents = "current" ,
498+ include_sources = ["user" , "self" ],
499+ instruction = "You are agent2" ,
500+ )
501+
502+ runner = testing_utils .InMemoryRunner (
503+ SequentialAgent (name = "pipeline" , sub_agents = [agent1 , agent2 ])
504+ )
505+ runner .run ("First user message" )
506+ runner .run ("Second user message" )
507+
508+ # Second invocation: current window starts at 'Second user message'.
509+ # Agent2's own prior turn from invocation 1 is outside that window — naturally absent.
510+ # What we verify: user present, upstream agent filtered by include_sources.
511+ agent2_second_contents = testing_utils .simplify_contents (
512+ agent2_model .requests [1 ].contents
513+ )
514+ assert any ("Second user message" in str (c ) for _ , c in agent2_second_contents )
515+ assert not any ("Agent1 result" in str (c ) for _ , c in agent2_second_contents )
516+ assert not any ("For context:" in str (c ) for _ , c in agent2_second_contents )
517+
518+
519+ def test_include_contents_none_with_include_sources_warns ():
520+ """include_contents='none' + include_sources triggers a UserWarning."""
521+ import warnings as _warnings
522+
523+ with _warnings .catch_warnings (record = True ) as w :
524+ _warnings .simplefilter ("always" )
525+ LlmAgent (
526+ name = "agent" ,
527+ model = "gemini-2.5-flash" ,
528+ include_contents = "none" ,
529+ include_sources = ["user" ],
530+ )
531+ assert len (w ) == 1
532+ assert issubclass (w [0 ].category , UserWarning )
533+ assert "include_contents='current'" in str (w [0 ].message )
534+
535+
536+ def test_include_contents_none_with_agent_name_in_sources_still_warns ():
537+ """Warning fires even with a concrete agent name — still risky at runtime."""
538+ import warnings as _warnings
539+
540+ with _warnings .catch_warnings (record = True ) as w :
541+ _warnings .simplefilter ("always" )
542+ LlmAgent (
543+ name = "agent" ,
544+ model = "gemini-2.5-flash" ,
545+ include_contents = "none" ,
546+ include_sources = ["user" , "upstream_agent" ],
547+ )
548+ assert len (w ) == 1
549+ assert issubclass (w [0 ].category , UserWarning )
0 commit comments