@@ -130,10 +130,31 @@ function repeatedWord(word: string, count: number): string {
130130 return Array . from ( { length : count } , ( ) => word ) . join ( " " )
131131}
132132
133- test ( "injectMessageIds prefers assistant tool outputs over text parts in message mode" , ( ) => {
133+ test ( "injectMessageIds tags every text part and tool output in message mode" , ( ) => {
134134 const sessionID = "ses_message_priority_tags"
135135 const messages : WithParts [ ] = [
136- buildMessage ( "msg-user-1" , "user" , sessionID , repeatedWord ( "investigate" , 6000 ) , 1 ) ,
136+ {
137+ info : {
138+ id : "msg-user-1" ,
139+ role : "user" ,
140+ sessionID,
141+ agent : "assistant" ,
142+ model : {
143+ providerID : "anthropic" ,
144+ modelID : "claude-test" ,
145+ } ,
146+ time : { created : 1 } ,
147+ } as WithParts [ "info" ] ,
148+ parts : [
149+ textPart (
150+ "msg-user-1" ,
151+ sessionID ,
152+ "msg-user-1-part-1" ,
153+ repeatedWord ( "investigate" , 6000 ) ,
154+ ) ,
155+ textPart ( "msg-user-1" , sessionID , "msg-user-1-part-2" , "Trailing note." ) ,
156+ ] ,
157+ } ,
137158 {
138159 info : {
139160 id : "msg-assistant-1" ,
@@ -146,10 +167,23 @@ test("injectMessageIds prefers assistant tool outputs over text parts in message
146167 textPart (
147168 "msg-assistant-1" ,
148169 sessionID ,
149- "msg-assistant-1-part" ,
170+ "msg-assistant-1-part-1 " ,
150171 "Short follow-up note." ,
151172 ) ,
152173 toolPart ( "msg-assistant-1" , sessionID , "call-task-1" , "task" , "task output body" ) ,
174+ textPart (
175+ "msg-assistant-1" ,
176+ sessionID ,
177+ "msg-assistant-1-part-2" ,
178+ "Second text chunk." ,
179+ ) ,
180+ toolPart (
181+ "msg-assistant-1" ,
182+ sessionID ,
183+ "call-task-2" ,
184+ "bash" ,
185+ "second tool output body" ,
186+ ) ,
153187 ] ,
154188 } ,
155189 ]
@@ -161,31 +195,73 @@ test("injectMessageIds prefers assistant tool outputs over text parts in message
161195
162196 injectMessageIds ( state , config , messages , compressionPriorities )
163197
164- assert . equal ( messages [ 0 ] ?. parts . length , 1 )
165- assert . equal ( messages [ 1 ] ?. parts . length , 2 )
166-
167- const userText = messages [ 0 ] ?. parts [ 0 ]
168- const assistantText = messages [ 1 ] ?. parts [ 0 ]
169- const assistantTool = messages [ 1 ] ?. parts [ 1 ]
170-
171- assert . equal ( userText ?. type , "text" )
172- assert . equal ( assistantText ?. type , "text" )
173- assert . equal ( assistantTool ?. type , "tool" )
198+ assert . equal ( messages [ 0 ] ?. parts . length , 2 )
199+ assert . equal ( messages [ 1 ] ?. parts . length , 4 )
200+
201+ const userTextOne = messages [ 0 ] ?. parts [ 0 ]
202+ const userTextTwo = messages [ 0 ] ?. parts [ 1 ]
203+ const assistantTextOne = messages [ 1 ] ?. parts [ 0 ]
204+ const assistantToolOne = messages [ 1 ] ?. parts [ 1 ]
205+ const assistantTextTwo = messages [ 1 ] ?. parts [ 2 ]
206+ const assistantToolTwo = messages [ 1 ] ?. parts [ 3 ]
207+
208+ assert . equal ( userTextOne ?. type , "text" )
209+ assert . equal ( userTextTwo ?. type , "text" )
210+ assert . equal ( assistantTextOne ?. type , "text" )
211+ assert . equal ( assistantToolOne ?. type , "tool" )
212+ assert . equal ( assistantTextTwo ?. type , "text" )
213+ assert . equal ( assistantToolTwo ?. type , "tool" )
174214 assert . match (
175- ( userText as any ) . text ,
215+ ( userTextOne as any ) . text ,
176216 / \n \n < d c p - m e s s a g e - i d p r i o r i t y = " h i g h " > m 0 0 0 1 < \/ d c p - m e s s a g e - i d > / ,
177217 )
178- assert . equal ( ( assistantText as any ) . text , "Short follow-up note." )
179218 assert . match (
180- ( assistantTool as any ) . state . output ,
219+ ( userTextTwo as any ) . text ,
220+ / \n \n < d c p - m e s s a g e - i d p r i o r i t y = " h i g h " > m 0 0 0 1 < \/ d c p - m e s s a g e - i d > / ,
221+ )
222+ assert . match (
223+ ( assistantTextOne as any ) . text ,
224+ / \n \n < d c p - m e s s a g e - i d p r i o r i t y = " l o w " > m 0 0 0 2 < \/ d c p - m e s s a g e - i d > / ,
225+ )
226+ assert . match (
227+ ( assistantToolOne as any ) . state . output ,
228+ / < d c p - m e s s a g e - i d p r i o r i t y = " l o w " > m 0 0 0 2 < \/ d c p - m e s s a g e - i d > / ,
229+ )
230+ assert . match (
231+ ( assistantTextTwo as any ) . text ,
232+ / \n \n < d c p - m e s s a g e - i d p r i o r i t y = " l o w " > m 0 0 0 2 < \/ d c p - m e s s a g e - i d > / ,
233+ )
234+ assert . match (
235+ ( assistantToolTwo as any ) . state . output ,
181236 / < d c p - m e s s a g e - i d p r i o r i t y = " l o w " > m 0 0 0 2 < \/ d c p - m e s s a g e - i d > / ,
182237 )
183238} )
184239
185- test ( "injectMessageIds marks protected user messages as BLOCKED without priority in message mode" , ( ) => {
240+ test ( "injectMessageIds marks every protected user text part as BLOCKED in message mode" , ( ) => {
186241 const sessionID = "ses_message_blocked_user_tags"
187242 const messages : WithParts [ ] = [
188- buildMessage ( "msg-user-1" , "user" , sessionID , repeatedWord ( "investigate" , 6000 ) , 1 ) ,
243+ {
244+ info : {
245+ id : "msg-user-1" ,
246+ role : "user" ,
247+ sessionID,
248+ agent : "assistant" ,
249+ model : {
250+ providerID : "anthropic" ,
251+ modelID : "claude-test" ,
252+ } ,
253+ time : { created : 1 } ,
254+ } as WithParts [ "info" ] ,
255+ parts : [
256+ textPart (
257+ "msg-user-1" ,
258+ sessionID ,
259+ "msg-user-1-part-1" ,
260+ repeatedWord ( "investigate" , 6000 ) ,
261+ ) ,
262+ textPart ( "msg-user-1" , sessionID , "msg-user-1-part-2" , "Trailing note." ) ,
263+ ] ,
264+ } ,
189265 buildMessage ( "msg-assistant-1" , "assistant" , sessionID , "Short follow-up note." , 2 ) ,
190266 ]
191267 const state = createSessionState ( )
@@ -197,19 +273,66 @@ test("injectMessageIds marks protected user messages as BLOCKED without priority
197273
198274 injectMessageIds ( state , config , messages , compressionPriorities )
199275
200- const userText = messages [ 0 ] ?. parts [ 0 ]
276+ const userTextOne = messages [ 0 ] ?. parts [ 0 ]
277+ const userTextTwo = messages [ 0 ] ?. parts [ 1 ]
201278 const assistantText = messages [ 1 ] ?. parts [ 0 ]
202279
203- assert . equal ( userText ?. type , "text" )
280+ assert . equal ( userTextOne ?. type , "text" )
281+ assert . equal ( userTextTwo ?. type , "text" )
204282 assert . equal ( assistantText ?. type , "text" )
205- assert . match ( ( userText as any ) . text , / \n \n < d c p - m e s s a g e - i d > B L O C K E D < \/ d c p - m e s s a g e - i d > / )
206- assert . doesNotMatch ( ( userText as any ) . text , / p r i o r i t y = / )
283+ assert . match ( ( userTextOne as any ) . text , / \n \n < d c p - m e s s a g e - i d > B L O C K E D < \/ d c p - m e s s a g e - i d > / )
284+ assert . match ( ( userTextTwo as any ) . text , / \n \n < d c p - m e s s a g e - i d > B L O C K E D < \/ d c p - m e s s a g e - i d > / )
285+ assert . doesNotMatch ( ( userTextOne as any ) . text , / p r i o r i t y = / )
286+ assert . doesNotMatch ( ( userTextTwo as any ) . text , / p r i o r i t y = / )
207287 assert . match (
208288 ( assistantText as any ) . text ,
209289 / \n \n < d c p - m e s s a g e - i d p r i o r i t y = " l o w " > m 0 0 0 2 < \/ d c p - m e s s a g e - i d > / ,
210290 )
211291} )
212292
293+ test ( "injectMessageIds tags every text part and tool output in range mode" , ( ) => {
294+ const sessionID = "ses_range_message_id_tags"
295+ const messages : WithParts [ ] = [
296+ buildMessage ( "msg-user-1" , "user" , sessionID , repeatedWord ( "investigate" , 6000 ) , 1 ) ,
297+ {
298+ info : {
299+ id : "msg-assistant-1" ,
300+ role : "assistant" ,
301+ sessionID,
302+ agent : "assistant" ,
303+ time : { created : 2 } ,
304+ } as WithParts [ "info" ] ,
305+ parts : [
306+ textPart ( "msg-assistant-1" , sessionID , "msg-assistant-1-part-1" , "First chunk." ) ,
307+ toolPart ( "msg-assistant-1" , sessionID , "call-task-range-1" , "task" , "first output" ) ,
308+ textPart ( "msg-assistant-1" , sessionID , "msg-assistant-1-part-2" , "Second chunk." ) ,
309+ toolPart (
310+ "msg-assistant-1" ,
311+ sessionID ,
312+ "call-task-range-2" ,
313+ "bash" ,
314+ "second output" ,
315+ ) ,
316+ ] ,
317+ } ,
318+ ]
319+ const state = createSessionState ( )
320+ const config = buildConfig ( "range" )
321+
322+ assignMessageRefs ( state , messages )
323+ injectMessageIds ( state , config , messages )
324+
325+ const assistantTextOne = messages [ 1 ] ?. parts [ 0 ]
326+ const assistantToolOne = messages [ 1 ] ?. parts [ 1 ]
327+ const assistantTextTwo = messages [ 1 ] ?. parts [ 2 ]
328+ const assistantToolTwo = messages [ 1 ] ?. parts [ 3 ]
329+
330+ assert . match ( ( assistantTextOne as any ) . text , / \n \n < d c p - m e s s a g e - i d > m 0 0 0 2 < \/ d c p - m e s s a g e - i d > / )
331+ assert . match ( ( assistantToolOne as any ) . state . output , / < d c p - m e s s a g e - i d > m 0 0 0 2 < \/ d c p - m e s s a g e - i d > / )
332+ assert . match ( ( assistantTextTwo as any ) . text , / \n \n < d c p - m e s s a g e - i d > m 0 0 0 2 < \/ d c p - m e s s a g e - i d > / )
333+ assert . match ( ( assistantToolTwo as any ) . state . output , / < d c p - m e s s a g e - i d > m 0 0 0 2 < \/ d c p - m e s s a g e - i d > / )
334+ } )
335+
213336test ( "message-mode nudges append to existing text parts and list only earlier visible high-priority message IDs" , ( ) => {
214337 const sessionID = "ses_message_priority_nudges"
215338 const messages : WithParts [ ] = [
0 commit comments