@@ -1994,6 +1994,75 @@ func TestRunStream_EmptyMessages_SendUserMessage(t *testing.T) {
19941994 require .NotEmpty (t , events )
19951995}
19961996
1997+ // TestRunStream_AddEnvironmentInfo_DoesNotPolluteSession pins the
1998+ // regression where session_start hook output (the AddEnvironmentInfo
1999+ // env block) was persisted as a system message on the session AFTER
2000+ // the user's first message had already been added, then surfaced
2001+ // verbatim as the [UserMessageEvent] because the runtime relays
2002+ // messages[len-1] as the "current" user message.
2003+ func TestRunStream_AddEnvironmentInfo_DoesNotPolluteSession (t * testing.T ) {
2004+ t .Parallel ()
2005+
2006+ stream := newStreamBuilder ().
2007+ AddContent ("reply" ).
2008+ AddStopWithUsage (5 , 5 ).
2009+ Build ()
2010+
2011+ prov := & mockProvider {id : "test/mock-model" , stream : stream }
2012+ root := agent .New (
2013+ "root" , "You are a test agent" ,
2014+ agent .WithModel (prov ),
2015+ agent .WithAddEnvironmentInfo (true ),
2016+ )
2017+ tm := team .New (team .WithAgents (root ))
2018+
2019+ rt , err := NewLocalRuntime (
2020+ tm ,
2021+ WithSessionCompaction (false ),
2022+ WithModelStore (mockModelStore {}),
2023+ WithWorkingDir (t .TempDir ()),
2024+ )
2025+ require .NoError (t , err )
2026+
2027+ sess := session .New (
2028+ session .WithUserMessage ("hello" ),
2029+ session .WithWorkingDir (t .TempDir ()),
2030+ )
2031+
2032+ evCh := rt .RunStream (t .Context (), sess )
2033+ var events []Event
2034+ for ev := range evCh {
2035+ events = append (events , ev )
2036+ }
2037+
2038+ // The persisted transcript must contain only the user message and
2039+ // the assistant reply — no system message smuggled in by the hook.
2040+ var roles []chat.MessageRole
2041+ for _ , item := range sess .Messages {
2042+ if item .IsMessage () {
2043+ roles = append (roles , item .Message .Message .Role )
2044+ }
2045+ }
2046+ assert .Equal (t ,
2047+ []chat.MessageRole {chat .MessageRoleUser , chat .MessageRoleAssistant },
2048+ roles ,
2049+ "session_start hook output must not be persisted as a session message" ,
2050+ )
2051+
2052+ // The UserMessageEvent must mirror the user's input, not the env
2053+ // info block produced by the hook.
2054+ var userEvts []* UserMessageEvent
2055+ for _ , ev := range events {
2056+ if ue , ok := ev .(* UserMessageEvent ); ok {
2057+ userEvts = append (userEvts , ue )
2058+ }
2059+ }
2060+ require .Len (t , userEvts , 1 )
2061+ assert .Equal (t , "hello" , userEvts [0 ].Message )
2062+ assert .NotContains (t , userEvts [0 ].Message , "<env>" ,
2063+ "user_message event must not leak the AddEnvironmentInfo block" )
2064+ }
2065+
19972066// recordingProvider wraps a sequence of mock streams and records the tools
19982067// passed to each CreateChatCompletionStream call.
19992068type recordingProvider struct {
0 commit comments