@@ -19,6 +19,7 @@ import {
1919 type ProductHostConfig ,
2020} from "./product-host.js" ;
2121import { buildModelsFirstCatalog , modelOptionId } from "./model-catalog.js" ;
22+ import { MAX_RETAINED_STREAM_ROWS } from "./long-log.js" ;
2223
2324function makeFakeSessionPort ( ) : {
2425 readonly sends : string [ ] ;
@@ -136,6 +137,56 @@ describe("mountProductHost", () => {
136137 }
137138 } ) ;
138139
140+ test ( "history.hydrate caps oversized history at the newest retained rows (CL-9008)" , async ( ) => {
141+ const { host, emitter } = await mountHeadless ( ) ;
142+ try {
143+ const total = MAX_RETAINED_STREAM_ROWS + 200 ;
144+ const blocks = Array . from ( { length : total } , ( _ , i ) => ( {
145+ type : "text" ,
146+ content : `row-${ i } ` ,
147+ } ) ) ;
148+ emitter . emit ( "history.hydrate" , blocks ) ;
149+ expect ( host . shell . streamLog . length ) . toBe ( MAX_RETAINED_STREAM_ROWS ) ;
150+ expect ( host . shell . streamLog [ 0 ] ) . toEqual ( {
151+ role : "assistant" ,
152+ text : `row-${ total - MAX_RETAINED_STREAM_ROWS } ` ,
153+ } ) ;
154+ expect ( host . shell . streamLog [ MAX_RETAINED_STREAM_ROWS - 1 ] ) . toEqual ( {
155+ role : "assistant" ,
156+ text : `row-${ total - 1 } ` ,
157+ } ) ;
158+ // Pre-sliced to the cap, so the append loop never trips retention
159+ // eviction: no churn, no dropped-rows marker on resume.
160+ expect ( host . shell . streamLogBase ) . toBe ( 0 ) ;
161+ } finally {
162+ host . dispose ( ) ;
163+ }
164+ } ) ;
165+
166+ test ( "history.hydrate below the cap paints every row (CL-9008)" , async ( ) => {
167+ const { host, emitter } = await mountHeadless ( ) ;
168+ try {
169+ const total = MAX_RETAINED_STREAM_ROWS - 100 ;
170+ const blocks = Array . from ( { length : total } , ( _ , i ) => ( {
171+ type : "text" ,
172+ content : `small-${ i } ` ,
173+ } ) ) ;
174+ emitter . emit ( "history.hydrate" , blocks ) ;
175+ expect ( host . shell . streamLog . length ) . toBe ( total ) ;
176+ expect ( host . shell . streamLog [ 0 ] ) . toEqual ( {
177+ role : "assistant" ,
178+ text : "small-0" ,
179+ } ) ;
180+ expect ( host . shell . streamLog [ total - 1 ] ) . toEqual ( {
181+ role : "assistant" ,
182+ text : `small-${ total - 1 } ` ,
183+ } ) ;
184+ expect ( host . shell . streamLogBase ) . toBe ( 0 ) ;
185+ } finally {
186+ host . dispose ( ) ;
187+ }
188+ } ) ;
189+
139190 test ( "session.title updates the shell header" , async ( ) => {
140191 const { host, emitter } = await mountHeadless ( ) ;
141192 try {
0 commit comments