@@ -824,6 +824,7 @@ describe('context-pruner saved run state overflow', () => {
824824 // Run context-pruner with 100k limit
825825 const mockAgentState = {
826826 messageHistory : initialMessages ,
827+ systemPrompt : savedRunState . sessionState ?. mainAgentState ?. systemPrompt ,
827828 } as AgentState
828829 const mockLogger = {
829830 debug : ( ) => { } ,
@@ -870,6 +871,90 @@ describe('context-pruner saved run state overflow', () => {
870871 expect ( finalTokens ) . toBeLessThan ( maxAllowedTokens )
871872 } )
872873
874+ test ( 'prunes message history from saved run state with large token count including system prompt' , ( ) => {
875+ // Load the saved run state file - message tokens (~183k) + system prompt tokens (~22k) = ~205k total
876+ // This exceeds the 200k limit when system prompt is included
877+ const runStatePath = join (
878+ __dirname ,
879+ 'data' ,
880+ 'run-state-context-overflow2.json' ,
881+ )
882+ const savedRunState = JSON . parse ( readFileSync ( runStatePath , 'utf-8' ) )
883+ const initialMessages =
884+ savedRunState . sessionState ?. mainAgentState ?. messageHistory
885+ const systemPrompt =
886+ savedRunState . sessionState ?. mainAgentState ?. systemPrompt
887+
888+ // Calculate initial token count
889+ const countTokens = ( msgs : any [ ] ) => {
890+ return msgs . reduce (
891+ ( sum , msg ) => sum + Math . ceil ( JSON . stringify ( msg ) . length / 3 ) ,
892+ 0 ,
893+ )
894+ }
895+ const initialMessageTokens = countTokens ( initialMessages )
896+ const systemPromptTokens = Math . ceil ( JSON . stringify ( systemPrompt ) . length / 3 )
897+ console . log ( 'Initial message count:' , initialMessages . length )
898+ console . log ( 'Initial message tokens (approx):' , initialMessageTokens )
899+ console . log ( 'System prompt tokens (approx):' , systemPromptTokens )
900+ console . log ( 'Total initial tokens (approx):' , initialMessageTokens + systemPromptTokens )
901+
902+ // Run context-pruner with 200k limit - must include systemPrompt in agentState
903+ // so the pruner knows about the extra tokens from the system prompt
904+ const mockAgentState = {
905+ messageHistory : initialMessages ,
906+ systemPrompt : systemPrompt ,
907+ } as AgentState
908+ const mockLogger = {
909+ debug : ( ) => { } ,
910+ info : ( ) => { } ,
911+ warn : ( ) => { } ,
912+ error : ( ) => { } ,
913+ }
914+
915+ const maxContextLength = 200_000
916+
917+ // Override maxMessageTokens via params
918+ const generator = contextPruner . handleSteps ! ( {
919+ agentState : mockAgentState ,
920+ logger : mockLogger ,
921+ params : { maxContextLength } ,
922+ } )
923+
924+ const results : any [ ] = [ ]
925+ let result = generator . next ( )
926+ while ( ! result . done ) {
927+ if ( typeof result . value === 'object' ) {
928+ results . push ( result . value )
929+ }
930+ result = generator . next ( )
931+ }
932+
933+ expect ( results ) . toHaveLength ( 1 )
934+ const prunedMessages = results [ 0 ] . input . messages
935+ const finalMessageTokens = countTokens ( prunedMessages )
936+ const finalTotalTokens = finalMessageTokens + systemPromptTokens
937+
938+ console . log ( 'Final message count:' , prunedMessages . length )
939+ console . log ( 'Final message tokens (approx):' , finalMessageTokens )
940+ console . log ( 'Final total tokens (approx):' , finalTotalTokens )
941+ console . log ( 'Message token reduction:' , initialMessageTokens - finalMessageTokens )
942+
943+ // The context-pruner calculates effective message budget as:
944+ // maxMessageTokens = maxContextLength - systemPromptTokens - toolDefinitionTokens
945+ // maxMessageTokens = 200k - ~22k - 0 = ~178k
946+ // Then it targets shortenedMessageTokenFactor (0.5) of that budget:
947+ // targetMessageTokens = 178k * 0.5 = ~89k
948+ // So final message tokens should be around 89k
949+ const effectiveMessageBudget = maxContextLength - systemPromptTokens
950+ const shortenedMessageTokenFactor = 0.5
951+ const targetMessageTokens = effectiveMessageBudget * shortenedMessageTokenFactor
952+ // Allow some overhead for the pruning not being exact
953+ const maxAllowedMessageTokens = targetMessageTokens + 5000
954+
955+ expect ( finalMessageTokens ) . toBeLessThan ( maxAllowedMessageTokens )
956+ } )
957+
873958 test ( 'accounts for system prompt and tool definitions when pruning with default 200k limit' , ( ) => {
874959 // Load the saved run state file with ~194k tokens in message history
875960 const runStatePath = join (
0 commit comments